Loading Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ cc_library { // needed by the device. srcs: [ "core/proto/android/os/kernelwake.proto", "core/proto/android/os/pagetypeinfo.proto", "core/proto/android/os/procrank.proto", "core/proto/android/service/graphicsstats.proto", "libs/incident/proto/android/privacy.proto", Loading cmds/incident_helper/IncidentHelper.cpp +139 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include "ih_util.h" #include "frameworks/base/core/proto/android/os/kernelwake.pb.h" #include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" #include "frameworks/base/core/proto/android/os/procrank.pb.h" #include <android-base/file.h> Loading @@ -32,6 +33,22 @@ using namespace android::os; using namespace google::protobuf; using namespace std; static const string TAB_DELIMITER = "\t"; static const string COMMA_DELIMITER = ","; static inline int toInt(const string& s) { return atoi(s.c_str()); } static inline long toLong(const string& s) { return atol(s.c_str()); } /** * Sets the given protobuf message when the field name matches one of the * fields. It is useful to set values to proto from table-like plain texts. */ static bool SetTableField(::google::protobuf::Message* message, string field_name, string field_value) { const Descriptor* descriptor = message->GetDescriptor(); Loading @@ -43,16 +60,16 @@ SetTableField(::google::protobuf::Message* message, string field_name, string fi reflection->SetString(message, field, field_value); return true; case FieldDescriptor::TYPE_INT64: reflection->SetInt64(message, field, atol(field_value.c_str())); reflection->SetInt64(message, field, toLong(field_value)); return true; case FieldDescriptor::TYPE_UINT64: reflection->SetUInt64(message, field, atol(field_value.c_str())); reflection->SetUInt64(message, field, toLong(field_value)); return true; case FieldDescriptor::TYPE_INT32: reflection->SetInt32(message, field, atoi(field_value.c_str())); reflection->SetInt32(message, field, toInt(field_value)); return true; case FieldDescriptor::TYPE_UINT32: reflection->SetUInt32(message, field, atoi(field_value.c_str())); reflection->SetUInt32(message, field, toInt(field_value)); return true; default: // Add new scalar types Loading Loading @@ -93,8 +110,6 @@ status_t ReverseParser::Parse(const int in, const int out) const } // ================================================================================ static const string KERNEL_WAKEUP_LINE_DELIMITER = "\t"; status_t KernelWakesParser::Parse(const int in, const int out) const { Reader reader(in); string line; Loading @@ -105,16 +120,16 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { KernelWakeSources proto; // parse line by line while (reader.readLine(line)) { while (reader.readLine(&line)) { if (line.empty()) continue; // parse head line if (nline++ == 0) { header = parseHeader(line, KERNEL_WAKEUP_LINE_DELIMITER); header = parseHeader(line, TAB_DELIMITER); continue; } // parse for each record, the line delimiter is \t only! record = parseRecord(line, KERNEL_WAKEUP_LINE_DELIMITER); record = parseRecord(line, TAB_DELIMITER); if (record.size() != header.size()) { // TODO: log this to incident report! Loading @@ -131,7 +146,7 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { } } if (!reader.ok(line)) { if (!reader.ok(&line)) { fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); return -1; } Loading @@ -147,7 +162,7 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { // ================================================================================ status_t ProcrankParser::Parse(const int in, const int out) const { Reader reader(in); string line, content; string line; header_t header; // the header of /d/wakeup_sources record_t record; // retain each record int nline = 0; Loading @@ -155,7 +170,7 @@ status_t ProcrankParser::Parse(const int in, const int out) const { Procrank proto; // parse line by line while (reader.readLine(line)) { while (reader.readLine(&line)) { if (line.empty()) continue; // parse head line Loading @@ -164,6 +179,15 @@ status_t ProcrankParser::Parse(const int in, const int out) const { continue; } if (hasPrefix(&line, "ZRAM:")) { proto.mutable_summary()->mutable_zram()->set_raw_text(line); continue; } if (hasPrefix(&line, "RAM:")) { proto.mutable_summary()->mutable_ram()->set_raw_text(line); continue; } record = parseRecord(line); if (record.size() != header.size()) { if (record[record.size() - 1] == "TOTAL") { // TOTAL record Loading @@ -171,12 +195,6 @@ status_t ProcrankParser::Parse(const int in, const int out) const { for (int i=1; i<=(int)record.size(); i++) { SetTableField(total, header[header.size() - i], record[record.size() - i]); } } else if (record[0] == "ZRAM:") { record = parseRecord(line, ":"); proto.mutable_summary()->mutable_zram()->set_raw_text(record[1]); } else if (record[0] == "RAM:") { record = parseRecord(line, ":"); proto.mutable_summary()->mutable_ram()->set_raw_text(record[1]); } else { fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); Loading @@ -193,7 +211,7 @@ status_t ProcrankParser::Parse(const int in, const int out) const { } } if (!reader.ok(line)) { if (!reader.ok(&line)) { fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); return -1; } Loading @@ -205,3 +223,105 @@ status_t ProcrankParser::Parse(const int in, const int out) const { fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); return NO_ERROR; } // ================================================================================ status_t PageTypeInfoParser::Parse(const int in, const int out) const { Reader reader(in); string line; bool migrateTypeSession = false; int pageBlockOrder; header_t blockHeader; PageTypeInfo pageTypeInfo; while (reader.readLine(&line)) { if (line.empty()) { migrateTypeSession = false; blockHeader.clear(); continue; } if (hasPrefix(&line, "Page block order:")) { pageBlockOrder = toInt(line); pageTypeInfo.set_page_block_order(pageBlockOrder); continue; } if (hasPrefix(&line, "Pages per block:")) { pageTypeInfo.set_pages_per_block(toInt(line)); continue; } if (hasPrefix(&line, "Free pages count per migrate type at order")) { migrateTypeSession = true; continue; } if (hasPrefix(&line, "Number of blocks type")) { blockHeader = parseHeader(line); continue; } record_t record = parseRecord(line, COMMA_DELIMITER); if (migrateTypeSession && record.size() == 3) { MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types(); // expect part 0 starts with "Node" if (hasPrefix(&record[0], "Node")) { migrateType->set_node(toInt(record[0])); } else goto ERROR; // expect part 1 starts with "zone" if (hasPrefix(&record[1], "zone")) { migrateType->set_zone(record[1]); } else goto ERROR; // expect part 2 starts with "type" if (hasPrefix(&record[2], "type")) { // expect the rest of part 2 has number of (pageBlockOrder + 2) parts // An example looks like: // header line: type 0 1 2 3 4 5 6 7 8 9 10 // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0 // The pageBlockOrder = 10 and it's zero-indexed. so total parts // are 10 + 1(zero-indexed) + 1(the type part) = 12. record_t pageCounts = parseRecord(record[2]); int pageCountsSize = pageBlockOrder + 2; if ((int)pageCounts.size() != pageCountsSize) goto ERROR; migrateType->set_type(pageCounts[0]); for (auto i=1; i<pageCountsSize; i++) { migrateType->add_free_pages_count(toInt(pageCounts[i])); } } else goto ERROR; continue; } if (!blockHeader.empty() && record.size() == 2) { BlockProto* block = pageTypeInfo.add_blocks(); if (hasPrefix(&record[0], "Node")) { block->set_node(toInt(record[0])); } else goto ERROR; if (hasPrefix(&record[1], "zone")) { record_t blockCounts = parseRecord(record[1]); block->set_zone(blockCounts[0]); for (size_t i=0; i<blockHeader.size(); i++) { if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR; } } else goto ERROR; continue; } ERROR: // print out error for this single line and continue parsing fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str()); } if (!reader.ok(&line)) { fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); return -1; } if (!pageTypeInfo.SerializeToFileDescriptor(out)) { fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); return -1; } fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize()); return NO_ERROR; } cmds/incident_helper/IncidentHelper.h +11 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,17 @@ public: virtual status_t Parse(const int in, const int out) const; }; /** * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype */ class PageTypeInfoParser : public TextParserBase { public: PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {}; ~PageTypeInfoParser() {}; virtual status_t Parse(const int in, const int out) const; }; /** * Procrank parser, parses text produced by command procrank */ Loading cmds/incident_helper/ih_util.cpp +20 −4 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "ih_util.h" #include <algorithm> #include <sstream> #include <unistd.h> Loading Loading @@ -72,6 +73,20 @@ record_t parseRecord(const std::string& line, const std::string& delimiters) { return record; } bool hasPrefix(std::string* line, const char* key) { const auto head = line->find_first_not_of(DEFAULT_WHITESPACE); if (head == std::string::npos) return false; auto i = 0; auto j = head; while (key[i] != '\0') { if (j >= line->size() || key[i++] != line->at(j++)) { return false; } } line->assign(trim(line->substr(j))); return true; } Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {}; Reader::Reader(const int fd, const size_t capacity) Loading @@ -86,8 +101,9 @@ Reader::~Reader() free(mBuf); } bool Reader::readLine(std::string& line, const char newline) { bool Reader::readLine(std::string* line, const char newline) { if (!ok(line)) return false; // bad status line->clear(); std::stringstream ss; while (!EOR()) { // read if available Loading Loading @@ -124,14 +140,14 @@ bool Reader::readLine(std::string& line, const char newline) { if (mFlushed >= (int) mMaxSize) mFlushed = 0; if (EOR() || meetsNewLine) { line.assign(ss.str()); line->assign(ss.str()); return true; } } return false; } bool Reader::ok(std::string& error) { error.assign(mStatus); bool Reader::ok(std::string* error) { error->assign(mStatus); return mStatus.empty(); } cmds/incident_helper/ih_util.h +22 −2 Original line number Diff line number Diff line Loading @@ -28,9 +28,29 @@ typedef std::string (*trans_func) (const std::string&); const char DEFAULT_NEWLINE = '\n'; const std::string DEFAULT_WHITESPACE = " \t"; /** * When a text has a table format like this * line 1: HeadA HeadB HeadC * line 2: v1 v2 v3 * line 3: v11 v12 v13 * * We want to parse the line in structure given the delimiter. * parseHeader is used to parse the firse line of the table and returns a list of strings in lower case * parseRecord is used to parse other lines and returns a list of strings * empty strings are skipped */ header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); /** * When the line starts with the given key, the function returns true * as well as the line argument is changed to the rest part of the original. * e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes * "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:", * otherwise the line is not changed. */ bool hasPrefix(std::string* line, const char* key); /** * Reader class reads data from given fd in streaming fashion. * The buffer size is controlled by capacity parameter. Loading @@ -42,8 +62,8 @@ public: Reader(const int fd, const size_t capacity); ~Reader(); bool readLine(std::string& line, const char newline = DEFAULT_NEWLINE); bool ok(std::string& error); bool readLine(std::string* line, const char newline = DEFAULT_NEWLINE); bool ok(std::string* error); private: int mFd; // set mFd to -1 when read EOF() Loading Loading
Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ cc_library { // needed by the device. srcs: [ "core/proto/android/os/kernelwake.proto", "core/proto/android/os/pagetypeinfo.proto", "core/proto/android/os/procrank.proto", "core/proto/android/service/graphicsstats.proto", "libs/incident/proto/android/privacy.proto", Loading
cmds/incident_helper/IncidentHelper.cpp +139 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include "ih_util.h" #include "frameworks/base/core/proto/android/os/kernelwake.pb.h" #include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" #include "frameworks/base/core/proto/android/os/procrank.pb.h" #include <android-base/file.h> Loading @@ -32,6 +33,22 @@ using namespace android::os; using namespace google::protobuf; using namespace std; static const string TAB_DELIMITER = "\t"; static const string COMMA_DELIMITER = ","; static inline int toInt(const string& s) { return atoi(s.c_str()); } static inline long toLong(const string& s) { return atol(s.c_str()); } /** * Sets the given protobuf message when the field name matches one of the * fields. It is useful to set values to proto from table-like plain texts. */ static bool SetTableField(::google::protobuf::Message* message, string field_name, string field_value) { const Descriptor* descriptor = message->GetDescriptor(); Loading @@ -43,16 +60,16 @@ SetTableField(::google::protobuf::Message* message, string field_name, string fi reflection->SetString(message, field, field_value); return true; case FieldDescriptor::TYPE_INT64: reflection->SetInt64(message, field, atol(field_value.c_str())); reflection->SetInt64(message, field, toLong(field_value)); return true; case FieldDescriptor::TYPE_UINT64: reflection->SetUInt64(message, field, atol(field_value.c_str())); reflection->SetUInt64(message, field, toLong(field_value)); return true; case FieldDescriptor::TYPE_INT32: reflection->SetInt32(message, field, atoi(field_value.c_str())); reflection->SetInt32(message, field, toInt(field_value)); return true; case FieldDescriptor::TYPE_UINT32: reflection->SetUInt32(message, field, atoi(field_value.c_str())); reflection->SetUInt32(message, field, toInt(field_value)); return true; default: // Add new scalar types Loading Loading @@ -93,8 +110,6 @@ status_t ReverseParser::Parse(const int in, const int out) const } // ================================================================================ static const string KERNEL_WAKEUP_LINE_DELIMITER = "\t"; status_t KernelWakesParser::Parse(const int in, const int out) const { Reader reader(in); string line; Loading @@ -105,16 +120,16 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { KernelWakeSources proto; // parse line by line while (reader.readLine(line)) { while (reader.readLine(&line)) { if (line.empty()) continue; // parse head line if (nline++ == 0) { header = parseHeader(line, KERNEL_WAKEUP_LINE_DELIMITER); header = parseHeader(line, TAB_DELIMITER); continue; } // parse for each record, the line delimiter is \t only! record = parseRecord(line, KERNEL_WAKEUP_LINE_DELIMITER); record = parseRecord(line, TAB_DELIMITER); if (record.size() != header.size()) { // TODO: log this to incident report! Loading @@ -131,7 +146,7 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { } } if (!reader.ok(line)) { if (!reader.ok(&line)) { fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); return -1; } Loading @@ -147,7 +162,7 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { // ================================================================================ status_t ProcrankParser::Parse(const int in, const int out) const { Reader reader(in); string line, content; string line; header_t header; // the header of /d/wakeup_sources record_t record; // retain each record int nline = 0; Loading @@ -155,7 +170,7 @@ status_t ProcrankParser::Parse(const int in, const int out) const { Procrank proto; // parse line by line while (reader.readLine(line)) { while (reader.readLine(&line)) { if (line.empty()) continue; // parse head line Loading @@ -164,6 +179,15 @@ status_t ProcrankParser::Parse(const int in, const int out) const { continue; } if (hasPrefix(&line, "ZRAM:")) { proto.mutable_summary()->mutable_zram()->set_raw_text(line); continue; } if (hasPrefix(&line, "RAM:")) { proto.mutable_summary()->mutable_ram()->set_raw_text(line); continue; } record = parseRecord(line); if (record.size() != header.size()) { if (record[record.size() - 1] == "TOTAL") { // TOTAL record Loading @@ -171,12 +195,6 @@ status_t ProcrankParser::Parse(const int in, const int out) const { for (int i=1; i<=(int)record.size(); i++) { SetTableField(total, header[header.size() - i], record[record.size() - i]); } } else if (record[0] == "ZRAM:") { record = parseRecord(line, ":"); proto.mutable_summary()->mutable_zram()->set_raw_text(record[1]); } else if (record[0] == "RAM:") { record = parseRecord(line, ":"); proto.mutable_summary()->mutable_ram()->set_raw_text(record[1]); } else { fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); Loading @@ -193,7 +211,7 @@ status_t ProcrankParser::Parse(const int in, const int out) const { } } if (!reader.ok(line)) { if (!reader.ok(&line)) { fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); return -1; } Loading @@ -205,3 +223,105 @@ status_t ProcrankParser::Parse(const int in, const int out) const { fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); return NO_ERROR; } // ================================================================================ status_t PageTypeInfoParser::Parse(const int in, const int out) const { Reader reader(in); string line; bool migrateTypeSession = false; int pageBlockOrder; header_t blockHeader; PageTypeInfo pageTypeInfo; while (reader.readLine(&line)) { if (line.empty()) { migrateTypeSession = false; blockHeader.clear(); continue; } if (hasPrefix(&line, "Page block order:")) { pageBlockOrder = toInt(line); pageTypeInfo.set_page_block_order(pageBlockOrder); continue; } if (hasPrefix(&line, "Pages per block:")) { pageTypeInfo.set_pages_per_block(toInt(line)); continue; } if (hasPrefix(&line, "Free pages count per migrate type at order")) { migrateTypeSession = true; continue; } if (hasPrefix(&line, "Number of blocks type")) { blockHeader = parseHeader(line); continue; } record_t record = parseRecord(line, COMMA_DELIMITER); if (migrateTypeSession && record.size() == 3) { MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types(); // expect part 0 starts with "Node" if (hasPrefix(&record[0], "Node")) { migrateType->set_node(toInt(record[0])); } else goto ERROR; // expect part 1 starts with "zone" if (hasPrefix(&record[1], "zone")) { migrateType->set_zone(record[1]); } else goto ERROR; // expect part 2 starts with "type" if (hasPrefix(&record[2], "type")) { // expect the rest of part 2 has number of (pageBlockOrder + 2) parts // An example looks like: // header line: type 0 1 2 3 4 5 6 7 8 9 10 // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0 // The pageBlockOrder = 10 and it's zero-indexed. so total parts // are 10 + 1(zero-indexed) + 1(the type part) = 12. record_t pageCounts = parseRecord(record[2]); int pageCountsSize = pageBlockOrder + 2; if ((int)pageCounts.size() != pageCountsSize) goto ERROR; migrateType->set_type(pageCounts[0]); for (auto i=1; i<pageCountsSize; i++) { migrateType->add_free_pages_count(toInt(pageCounts[i])); } } else goto ERROR; continue; } if (!blockHeader.empty() && record.size() == 2) { BlockProto* block = pageTypeInfo.add_blocks(); if (hasPrefix(&record[0], "Node")) { block->set_node(toInt(record[0])); } else goto ERROR; if (hasPrefix(&record[1], "zone")) { record_t blockCounts = parseRecord(record[1]); block->set_zone(blockCounts[0]); for (size_t i=0; i<blockHeader.size(); i++) { if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR; } } else goto ERROR; continue; } ERROR: // print out error for this single line and continue parsing fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str()); } if (!reader.ok(&line)) { fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); return -1; } if (!pageTypeInfo.SerializeToFileDescriptor(out)) { fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); return -1; } fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize()); return NO_ERROR; }
cmds/incident_helper/IncidentHelper.h +11 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,17 @@ public: virtual status_t Parse(const int in, const int out) const; }; /** * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype */ class PageTypeInfoParser : public TextParserBase { public: PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {}; ~PageTypeInfoParser() {}; virtual status_t Parse(const int in, const int out) const; }; /** * Procrank parser, parses text produced by command procrank */ Loading
cmds/incident_helper/ih_util.cpp +20 −4 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "ih_util.h" #include <algorithm> #include <sstream> #include <unistd.h> Loading Loading @@ -72,6 +73,20 @@ record_t parseRecord(const std::string& line, const std::string& delimiters) { return record; } bool hasPrefix(std::string* line, const char* key) { const auto head = line->find_first_not_of(DEFAULT_WHITESPACE); if (head == std::string::npos) return false; auto i = 0; auto j = head; while (key[i] != '\0') { if (j >= line->size() || key[i++] != line->at(j++)) { return false; } } line->assign(trim(line->substr(j))); return true; } Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {}; Reader::Reader(const int fd, const size_t capacity) Loading @@ -86,8 +101,9 @@ Reader::~Reader() free(mBuf); } bool Reader::readLine(std::string& line, const char newline) { bool Reader::readLine(std::string* line, const char newline) { if (!ok(line)) return false; // bad status line->clear(); std::stringstream ss; while (!EOR()) { // read if available Loading Loading @@ -124,14 +140,14 @@ bool Reader::readLine(std::string& line, const char newline) { if (mFlushed >= (int) mMaxSize) mFlushed = 0; if (EOR() || meetsNewLine) { line.assign(ss.str()); line->assign(ss.str()); return true; } } return false; } bool Reader::ok(std::string& error) { error.assign(mStatus); bool Reader::ok(std::string* error) { error->assign(mStatus); return mStatus.empty(); }
cmds/incident_helper/ih_util.h +22 −2 Original line number Diff line number Diff line Loading @@ -28,9 +28,29 @@ typedef std::string (*trans_func) (const std::string&); const char DEFAULT_NEWLINE = '\n'; const std::string DEFAULT_WHITESPACE = " \t"; /** * When a text has a table format like this * line 1: HeadA HeadB HeadC * line 2: v1 v2 v3 * line 3: v11 v12 v13 * * We want to parse the line in structure given the delimiter. * parseHeader is used to parse the firse line of the table and returns a list of strings in lower case * parseRecord is used to parse other lines and returns a list of strings * empty strings are skipped */ header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); /** * When the line starts with the given key, the function returns true * as well as the line argument is changed to the rest part of the original. * e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes * "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:", * otherwise the line is not changed. */ bool hasPrefix(std::string* line, const char* key); /** * Reader class reads data from given fd in streaming fashion. * The buffer size is controlled by capacity parameter. Loading @@ -42,8 +62,8 @@ public: Reader(const int fd, const size_t capacity); ~Reader(); bool readLine(std::string& line, const char newline = DEFAULT_NEWLINE); bool ok(std::string& error); bool readLine(std::string* line, const char newline = DEFAULT_NEWLINE); bool ok(std::string* error); private: int mFd; // set mFd to -1 when read EOF() Loading