New class FileRotatingStreamReader

When landed, the FileRotatingStream class can be made write-only.

Bug: webrtc:7811
Change-Id: I6dcd2a869301b9b8273b48d47df51a1065767ffd
Reviewed-on: https://webrtc-review.googlesource.com/c/115302
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Commit-Queue: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26126}
This commit is contained in:
Niels Möller 2019-01-03 14:21:38 +01:00 committed by Commit Bot
parent fd87da7a4b
commit d9ac058464
5 changed files with 98 additions and 27 deletions

View File

@ -36,6 +36,8 @@ namespace rtc {
namespace {
const char kCallSessionLogPrefix[] = "webrtc_log";
std::string AddTrailingPathDelimiterIfNeeded(std::string directory);
// |dir| must have a trailing delimiter. |prefix| must not include wild card
@ -458,7 +460,7 @@ std::string FileRotatingStream::GetFilePath(size_t index,
CallSessionFileRotatingStream::CallSessionFileRotatingStream(
const std::string& dir_path)
: FileRotatingStream(dir_path, kLogPrefix),
: FileRotatingStream(dir_path, kCallSessionLogPrefix),
max_total_log_size_(0),
num_rotations_(0) {}
@ -466,7 +468,7 @@ CallSessionFileRotatingStream::CallSessionFileRotatingStream(
const std::string& dir_path,
size_t max_total_log_size)
: FileRotatingStream(dir_path,
kLogPrefix,
kCallSessionLogPrefix,
max_total_log_size / 2,
GetNumRotatingLogFiles(max_total_log_size) + 1),
max_total_log_size_(max_total_log_size),
@ -474,7 +476,6 @@ CallSessionFileRotatingStream::CallSessionFileRotatingStream(
RTC_DCHECK_GE(max_total_log_size, 4);
}
const char* CallSessionFileRotatingStream::kLogPrefix = "webrtc_log";
const size_t CallSessionFileRotatingStream::kRotatingLogFileDefaultSize =
1024 * 1024;
@ -508,4 +509,47 @@ size_t CallSessionFileRotatingStream::GetNumRotatingLogFiles(
(max_total_log_size / 2) / kRotatingLogFileDefaultSize);
}
FileRotatingStreamReader::FileRotatingStreamReader(
const std::string& dir_path,
const std::string& file_prefix) {
file_names_ = GetFilesWithPrefix(AddTrailingPathDelimiterIfNeeded(dir_path),
file_prefix);
// Plain sort of the file names would sort by age, i.e., oldest last. Using
// std::greater gives us the desired chronological older, oldest first.
std::sort(file_names_.begin(), file_names_.end(),
std::greater<std::string>());
}
FileRotatingStreamReader::~FileRotatingStreamReader() = default;
size_t FileRotatingStreamReader::GetSize() const {
size_t total_size = 0;
for (const auto& file_name : file_names_) {
total_size += GetFileSize(file_name).value_or(0);
}
return total_size;
}
size_t FileRotatingStreamReader::ReadAll(void* buffer, size_t size) const {
size_t done = 0;
for (const auto& file_name : file_names_) {
if (done < size) {
FILE* f = fopen(file_name.c_str(), "rb");
if (!f) {
break;
}
done += fread(static_cast<char*>(buffer) + done, 1, size - done, f);
fclose(f);
} else {
break;
}
}
return done;
}
CallSessionFileRotatingStreamReader::CallSessionFileRotatingStreamReader(
const std::string& dir_path)
: FileRotatingStreamReader(dir_path, kCallSessionLogPrefix) {}
} // namespace rtc

View File

@ -158,7 +158,6 @@ class CallSessionFileRotatingStream : public FileRotatingStream {
private:
static size_t GetRotatingLogSize(size_t max_total_log_size);
static size_t GetNumRotatingLogFiles(size_t max_total_log_size);
static const char* kLogPrefix;
static const size_t kRotatingLogFileDefaultSize;
const size_t max_total_log_size_;
@ -167,6 +166,27 @@ class CallSessionFileRotatingStream : public FileRotatingStream {
RTC_DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingStream);
};
// This is a convenience class, to read all files produced by a
// FileRotatingStream, all in one go. Typical use calls GetSize and ReadData
// only once. The list of file names to read is based on the contents of the log
// directory at construction time.
class FileRotatingStreamReader {
public:
FileRotatingStreamReader(const std::string& dir_path,
const std::string& file_prefix);
~FileRotatingStreamReader();
size_t GetSize() const;
size_t ReadAll(void* buffer, size_t size) const;
private:
std::vector<std::string> file_names_;
};
class CallSessionFileRotatingStreamReader : public FileRotatingStreamReader {
public:
CallSessionFileRotatingStreamReader(const std::string& dir_path);
};
} // namespace rtc
#endif // RTC_BASE_FILEROTATINGSTREAM_H_

View File

@ -80,6 +80,8 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test {
const size_t expected_length,
const std::string& dir_path,
const char* file_prefix) {
// TODO(nisse): Delete the part of this method using read-mode
// FileRotatingStream, together with support for read-mode.
std::unique_ptr<FileRotatingStream> stream;
stream.reset(new FileRotatingStream(dir_path, file_prefix));
ASSERT_TRUE(stream->Open());
@ -92,6 +94,13 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test {
EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
EXPECT_EQ(stream_size, read);
// Test also with the FileRotatingStreamReader class.
FileRotatingStreamReader reader(dir_path, file_prefix);
EXPECT_EQ(reader.GetSize(), expected_length);
memset(buffer.get(), 0, expected_length);
EXPECT_EQ(expected_length, reader.ReadAll(buffer.get(), expected_length));
EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
}
void VerifyFileContents(const char* expected_contents,
@ -293,6 +302,8 @@ class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test {
void VerifyStreamRead(const char* expected_contents,
const size_t expected_length,
const std::string& dir_path) {
// TODO(nisse): Delete the part of this method using read-mode
// CallSessionFileRotatingStream, together with support for read-mode.
std::unique_ptr<CallSessionFileRotatingStream> stream(
new CallSessionFileRotatingStream(dir_path));
ASSERT_TRUE(stream->Open());
@ -305,6 +316,13 @@ class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test {
EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
EXPECT_EQ(stream_size, read);
// Test also with the CallSessionFileRotatingStreamReader class.
CallSessionFileRotatingStreamReader reader(dir_path);
EXPECT_EQ(reader.GetSize(), expected_length);
memset(buffer.get(), 0, expected_length);
EXPECT_EQ(expected_length, reader.ReadAll(buffer.get(), expected_length));
EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
}
std::unique_ptr<CallSessionFileRotatingStream> stream_;

View File

@ -50,23 +50,17 @@ JNI_CallSessionFileRotatingLogSink_GetLogData(
JNIEnv* jni,
const JavaParamRef<jstring>& j_dirPath) {
std::string dir_path = JavaToStdString(jni, j_dirPath);
std::unique_ptr<rtc::CallSessionFileRotatingStream> stream(
new rtc::CallSessionFileRotatingStream(dir_path));
if (!stream->Open()) {
RTC_LOG_V(rtc::LoggingSeverity::LS_WARNING)
<< "Failed to open CallSessionFileRotatingStream for path " << dir_path;
return ScopedJavaLocalRef<jbyteArray>(jni, jni->NewByteArray(0));
}
size_t log_size = 0;
if (!stream->GetSize(&log_size) || log_size == 0) {
rtc::CallSessionFileRotatingStreamReader file_reader(dir_path);
size_t log_size = file_reader.GetSize();
if (log_size == 0) {
RTC_LOG_V(rtc::LoggingSeverity::LS_WARNING)
<< "CallSessionFileRotatingStream returns 0 size for path " << dir_path;
return ScopedJavaLocalRef<jbyteArray>(jni, jni->NewByteArray(0));
}
size_t read = 0;
// TODO(nisse, sakal): To avoid copying, change api to use ByteBuffer.
std::unique_ptr<jbyte> buffer(static_cast<jbyte*>(malloc(log_size)));
stream->ReadAll(buffer.get(), log_size, &read, nullptr);
size_t read = file_reader.ReadAll(buffer.get(), log_size);
ScopedJavaLocalRef<jbyteArray> result =
ScopedJavaLocalRef<jbyteArray>(jni, jni->NewByteArray(read));

View File

@ -129,29 +129,24 @@ const char *kRTCFileLoggerRotatingLogPrefix = "rotating_log";
return nil;
}
NSMutableData* logData = [NSMutableData data];
std::unique_ptr<rtc::FileRotatingStream> stream;
std::unique_ptr<rtc::FileRotatingStreamReader> stream;
switch(_rotationType) {
case RTCFileLoggerTypeApp:
stream.reset(
new rtc::FileRotatingStream(_dirPath.UTF8String,
kRTCFileLoggerRotatingLogPrefix));
stream = absl::make_unique<rtc::FileRotatingStreamReader>(_dirPath.UTF8String,
kRTCFileLoggerRotatingLogPrefix);
break;
case RTCFileLoggerTypeCall:
stream.reset(new rtc::CallSessionFileRotatingStream(_dirPath.UTF8String));
stream = absl::make_unique<rtc::CallSessionFileRotatingStreamReader>(_dirPath.UTF8String);
break;
}
if (!stream->Open()) {
size_t bufferSize = stream->GetSize();
if (bufferSize == 0) {
return logData;
}
size_t bufferSize = 0;
if (!stream->GetSize(&bufferSize) || bufferSize == 0) {
return logData;
}
size_t read = 0;
// Allocate memory using malloc so we can pass it direcly to NSData without
// copying.
std::unique_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize)));
stream->ReadAll(buffer.get(), bufferSize, &read, nullptr);
size_t read = stream->ReadAll(buffer.get(), bufferSize);
logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release()
length:read];
return logData;