blob: a294fdc3cc151567de14a809287fa3f72a106eca [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <utility>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "croslog/file_change_watcher.h"
#include "croslog/log_entry.h"
#include "croslog/log_parser.h"
#include <base/check.h>
#include <base/check_op.h>
namespace croslog {
class FileMapReaderDelegate;
// A class to read a large file with chunks.
// How to use:
// - Call CreateReader(base::File) method to instantiate the class
// - Call MapBuffer(pos, len) method to map the specified region from the file
// - Use the methods of MappedBuffer class to access the content of the file.
// Requisites:
// - This class assumes the file is written with "append-only" mode and is never
// truncated or shrunk. If the file size shrinks, the crash may happen.
// (file rotation is not a problem because the renamed old log keeps its FD.)
class FileMapReader {
// A class to access to the mapped buffer. Access to buffer should be done
// through this class and be safe during the lifetime of the instance.
class MappedBuffer {
// Returns the specified range of the buffer. The caller must be sure that
// the specified range is within the range passed to MapBuffer().
std::pair<const uint8_t*, uint64_t> GetBuffer(
uint64_t request_pos, uint64_t request_length) const;
// Returns the character (byte) at the specified position.
// This is inline since this is frequently called.
inline uint8_t GetChar(uint64_t position) const {
DCHECK(buffer_start_ <= position);
DCHECK(position < (buffer_start_ + buffer_length_));
return buffer_[position - buffer_start_];
// Returns true if the mmap succeeded and the mapped buffer is valid.
bool valid() const { return buffer_ != nullptr; }
friend class FileMapReader;
MappedBuffer(const uint8_t* buffer_,
uint64_t buffer_start_,
uint64_t buffer_length_);
MappedBuffer(const MappedBuffer&) = delete;
MappedBuffer& operator=(const MappedBuffer&) = delete;
const uint8_t* const buffer_ = nullptr;
const uint64_t buffer_start_ = 0;
const uint64_t buffer_length_ = 0;
base::WeakPtrFactory<MappedBuffer> weak_factory_{this};
// Creates an instance and returns it.
static std::unique_ptr<FileMapReader> CreateReader(base::File file);
// Retrieves the chunk size. The value may change on tests.
static uint64_t GetChunkSizeInBytes();
// Static methods for test:
static std::unique_ptr<FileMapReader>
CreateFileMapReaderDelegateImplMemoryReaderForTest(const uint8_t* buffer,
uint64_t length);
static void SetBlockSizesForTest(uint64_t chunk_size_in_bytes,
uint32_t allocate_chunks);
// ctor and dtor
explicit FileMapReader(std::unique_ptr<FileMapReaderDelegate> delegate);
FileMapReader(const FileMapReader&) = delete;
FileMapReader& operator=(const FileMapReader&) = delete;
// Should be called when the file size is expanded.
void ApplyFileSizeExpansion();
// Maps the specified range of the buffer from the file. The mapped buffer
// can be accessed using the returned object.
// Only one MappedBuffer instance can be alive at the same time due to the
// current implementation limitation. Please ensure that the previous one was
// freed before calling this.
// On failure of mmap, the returned mapped buffer is invalid.
std::unique_ptr<MappedBuffer> MapBuffer(uint64_t request_pos,
uint64_t request_length);
// Returns the cached file size of the target file. The cache is updated
// when RequestRemap() is called.
inline int64_t GetFileSize() const {
DCHECK_GE(file_size_, 0);
return file_size_;
// Utility method to create a mapped buffer.
std::unique_ptr<FileMapReader::MappedBuffer> CreateMappedBuffer();
// Delegate to actually map a buffer of file (or memory).
std::unique_ptr<FileMapReaderDelegate> delegate_;
const uint8_t* buffer_ = nullptr;
uint64_t buffer_start_ = 0;
uint64_t buffer_length_ = 0;
int64_t file_size_;
base::WeakPtr<MappedBuffer> instantiated_mapped_buffer_;
} // namespace croslog