blob: 9d355b77ebfb1fd7b481de0410707b554c76ea10 [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.
// This class is intended to be used by LogReader in anomaly_detector_log_reader
//
// Class TextFileReader reads from text file returning a line each time it
// finds the newline character '\n'. If '\n' is not found, it will store
// the characters read so far in line_fragment_ and waits for '\n'. This
// behaviour is useful when read and write is happening concurrently.
//
// If underlying base::File file_ is invalid, TextFileReader tries to reopen the
// file every time GetLine is called until it reaches kMaxOpenRetries_. Once the
// limit is reached, it will simply return false on GetLine().
#ifndef CRASH_REPORTER_ANOMALY_DETECTOR_TEXT_FILE_READER_H_
#define CRASH_REPORTER_ANOMALY_DETECTOR_TEXT_FILE_READER_H_
#include <string>
#include <vector>
#include <base/files/file.h>
#include <base/files/file_util.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
namespace anomaly {
class TextFileReader {
public:
explicit TextFileReader(const base::FilePath& path);
~TextFileReader();
// If it finds the next line delimited with '\n', it returns true and assigns
// the line to the output parameter. If it reaches EOF, it also checks if
// file_path_ has been replaced by a new file and if so updates file_ to point
// to the new file.
bool GetLine(std::string* line);
// Sets the position of read to -1 from the end of file and sets skip_next_ to
// true. This results in GetLine discarding all characters from just before
// the end of the file to the next '\n'.
// This prevents LogReader from trying to parse a partial line.
void SeekToEnd();
// Sets the position of read to the beginning of the file.
// Only used in test by LogReader class.
void SeekToBegin();
private:
// Opens a file pointed by file_path_ and stores it in file_. It returns true
// if base::File file_ was opened successfully and false if not. This method
// will be called once every time GetLine is called until file_ is opened
// successfully or the number of tries reaches the limit kMaxOpenRetries_.
bool Open();
// Reads the content of file_ from position offset_ onwards to buf_.
bool LoadToBuffer();
// Clears line_fragment_ and virtually clears the buffer by setting pos_ and
// end_pos_ to 0.
void Clear();
// Check inode number of the file pointed by file_path_ against the current
// inode number stored. It uses stat(2) system call.
bool CheckForNewFile();
const base::FilePath file_path_;
base::File file_;
static constexpr int kBufferSize_ = 1024;
std::vector<char> buf_;
std::vector<char> line_fragment_;
// Current position of read within buf_.
int pos_ = 0;
// The end position of the used part of the buf_.
int end_pos_ = 0;
// The inode number of the file_.
ino_t inode_number_;
// If true, skip the next line. This is set to true if SeekToEnd was called to
// make sure that no partial line is returned as a line.
bool skip_next_ = false;
// Number of times Open was called. It is reset on successful Open.
int open_tries_ = 0;
// Limit of consecutive unsuccessful Open method call before giving up.
static constexpr int kMaxOpenRetries_ = 10;
FRIEND_TEST(AnomalyDetectorFileReaderTest, InvalidFileTest);
FRIEND_TEST(AnomalyDetectorFileReaderTest, OpenFileTest);
FRIEND_TEST(AnomalyDetectorFileReaderTest, ReopenFileOnMoveTest);
FRIEND_TEST(AnomalyDetectorFileReaderConcurrentTest, ReadAppendedTextTest);
FRIEND_TEST(AnomalyDetectorFileReaderConcurrentTest,
ReadLineLongerThanBufferTest);
FRIEND_TEST(AnomalyDetectorFileReaderConcurrentTest, OpenFileRetryTest);
FRIEND_TEST(AnomalyDetectorFileReaderConcurrentTest,
OpenFileRetryExceededTest);
FRIEND_TEST(AnomalyDetectorFileReaderConcurrentTest, HandleFileMoveTest);
};
} // namespace anomaly
#endif // CRASH_REPORTER_ANOMALY_DETECTOR_TEXT_FILE_READER_H_