Implement a new type of section which reads from logd and dumps proto.

And implement a file section which reads from event-log-tags for
decoding binary logs.

Bug: 70936599
Test: atest incidentd_test && atest incident_helper_test and flush on
device and test log sections and event_log_tag_map
Change-Id: Ib3d35e317f355de69f01ded012482486e9a43da6
This commit is contained in:
Yi Jin 2017-12-22 17:36:47 -08:00
parent a598c05d63
commit 3c034c987e
19 changed files with 767 additions and 19 deletions

View File

@ -730,10 +730,18 @@ gensrcs {
"core/proto/android/os/procrank.proto",
"core/proto/android/os/ps.proto",
"core/proto/android/os/system_properties.proto",
"core/proto/android/util/event_log_tags.proto",
],
// Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
cmd: "PATH=$$PATH:$$(dirname $(location protoc-gen-cppstream)) $(location aprotoc) --plugin=protoc-gen-cpp-stream=$(location protoc-gen-cppstream) --dependency_out=$(depfile) --cppstream_out=$(genDir)/ -Iexternal/protobuf/src -I . $(in)",
cmd: "mkdir -p $(genDir) " +
"&& $(location aprotoc) " +
" --plugin=$(location protoc-gen-cppstream) " +
" --dependency_out=$(depfile) " +
" --cppstream_out=$(genDir) " +
" -Iexternal/protobuf/src " +
" -I . " +
" $(in)",
output_extension = "proto.h",
}

View File

@ -208,6 +208,19 @@ bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
return true;
}
std::string behead(std::string* line, const char cut) {
auto found = line->find_first_of(cut);
if (found == std::string::npos) {
std::string head = line->substr(0);
line->assign("");
return head;
}
std::string head = line->substr(0, found);
while(line->at(found) == cut) found++; // trim more cut of the rest
line->assign(line->substr(found));
return head;
}
int toInt(const std::string& s) {
return atoi(s.c_str());
}

View File

@ -34,6 +34,8 @@ const std::string DEFAULT_WHITESPACE = " \t";
const std::string DEFAULT_NEWLINE = "\r\n";
const std::string TAB_DELIMITER = "\t";
const std::string COMMA_DELIMITER = ",";
const std::string PIPE_DELIMITER = "|";
const std::string PARENTHESES_DELIMITER = "()";
// returns true if c is a-zA-Z0-9 or underscore
bool isValidChar(char c);
@ -88,6 +90,11 @@ void printRecord(const record_t& record);
bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false);
bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false);
/**
* behead the given line by the cut, return the head and reassign the line to be the rest.
*/
std::string behead(std::string* line, const char cut);
/**
* Converts string to the desired type
*/

View File

@ -19,6 +19,7 @@
#include "parsers/BatteryTypeParser.h"
#include "parsers/CpuFreqParser.h"
#include "parsers/CpuInfoParser.h"
#include "parsers/EventLogTagsParser.h"
#include "parsers/KernelWakesParser.h"
#include "parsers/PageTypeInfoParser.h"
#include "parsers/ProcrankParser.h"
@ -55,6 +56,8 @@ static TextParserBase* selectParser(int section) {
// IDs larger than 1 are section ids reserved in incident.proto
case 1000:
return new SystemPropertiesParser();
case 1100:
return new EventLogTagsParser();
case 2000:
return new ProcrankParser();
case 2001:

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "incident_helper"
#include <android/util/ProtoOutputStream.h>
#include "frameworks/base/core/proto/android/util/event_log_tags.proto.h"
#include "ih_util.h"
#include "EventLogTagsParser.h"
status_t
EventLogTagsParser::Parse(const int in, const int out) const
{
Reader reader(in);
string line;
ProtoOutputStream proto;
// parse line by line
while (reader.readLine(&line)) {
if (line.empty()) continue;
string debug = line;
string tagNumber = behead(&line, ' ');
string tagName = behead(&line, ' ');
if (tagNumber == "" || tagName == "") {
fprintf(stderr, "Bad line, expect at least two parts: %s[%s, %s]\n",
debug.c_str(), tagNumber.c_str(), tagName.c_str());
continue;
}
long long token = proto.start(EventLogTagMapProto::EVENT_LOG_TAGS);
proto.write(EventLogTag::TAG_NUMBER, toInt(tagNumber));
proto.write(EventLogTag::TAG_NAME, tagName);
record_t valueDescriptors = parseRecord(line, PARENTHESES_DELIMITER);
for (size_t i = 0; i < valueDescriptors.size(); i++) {
record_t valueDescriptor = parseRecord(valueDescriptors[i], PIPE_DELIMITER);
if (valueDescriptor.size() != 2 && valueDescriptor.size() != 3) {
// If the parts doesn't contains pipe, then skips it.
continue;
}
long long descriptorToken = proto.start(EventLogTag::VALUE_DESCRIPTORS);
proto.write(EventLogTag::ValueDescriptor::NAME, valueDescriptor[0]);
proto.write(EventLogTag::ValueDescriptor::TYPE, toInt(valueDescriptor[1]));
if (valueDescriptor.size() == 3) {
char c = valueDescriptor[2][0];
int unit = 0;
if (c < '0' || c > '9') {
unit = (int) c;
} else {
unit = toInt(valueDescriptor[2]);
}
proto.write(EventLogTag::ValueDescriptor::UNIT, unit);
}
proto.end(descriptorToken);
}
proto.end(token);
}
if (!reader.ok(&line)) {
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
return -1;
}
if (!proto.flush(out)) {
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
return -1;
}
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
return NO_ERROR;
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef EVENT_LOG_TAGS_PARSER_H
#define EVENT_LOG_TAGS_PARSER_H
#include "TextParserBase.h"
using namespace android;
/**
* event.logtags parser, parse file in /system/etc/event-log-tags
*/
class EventLogTagsParser : public TextParserBase {
public:
EventLogTagsParser() : TextParserBase(String8("EventLogTagsParser")) {};
~EventLogTagsParser() {};
virtual status_t Parse(const int in, const int out) const;
};
#endif // EVENT_LOG_TAGS_PARSER_H

View File

@ -0,0 +1,6 @@
42 answer (to life the universe etc|3)
314 pi
1004 chatty (dropped|3)
1005 tag_def (tag|1),(name|3),(format|3)
2747 contacts_aggregation (aggregation time|2|3), (count|1|1)
1397638484 snet_event_log (subtag|3) (uid|1) (message|3|s)

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EventLogTagsParser.h"
#include "frameworks/base/core/proto/android/util/event_log_tags.pb.h"
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gmock/gmock.h>
#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <string.h>
#include <fcntl.h>
using namespace android::base;
using namespace android::util;
using namespace std;
using ::testing::StrEq;
using ::testing::Test;
using ::testing::internal::CaptureStderr;
using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStderr;
using ::testing::internal::GetCapturedStdout;
class EventLogTagsParserTest : public Test {
public:
virtual void SetUp() override {
ASSERT_TRUE(tf.fd != -1);
}
protected:
TemporaryFile tf;
const string kTestPath = GetExecutableDirectory();
const string kTestDataPath = kTestPath + "/testdata/";
};
TEST_F(EventLogTagsParserTest, Success) {
const string testFile = kTestDataPath + "event-log-tags.txt";
EventLogTagsParser parser;
EventLogTagMapProto expected;
EventLogTag* eventLogTag;
EventLogTag::ValueDescriptor* desp;
eventLogTag = expected.add_event_log_tags();
eventLogTag->set_tag_number(42);
eventLogTag->set_tag_name("answer");
desp = eventLogTag->add_value_descriptors();
desp->set_name("to life the universe etc");
desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING);
eventLogTag = expected.add_event_log_tags();
eventLogTag->set_tag_number(314);
eventLogTag->set_tag_name("pi");
eventLogTag = expected.add_event_log_tags();
eventLogTag->set_tag_number(1004);
eventLogTag->set_tag_name("chatty");
desp = eventLogTag->add_value_descriptors();
desp->set_name("dropped");
desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING);
eventLogTag = expected.add_event_log_tags();
eventLogTag->set_tag_number(1005);
eventLogTag->set_tag_name("tag_def");
desp = eventLogTag->add_value_descriptors();
desp->set_name("tag");
desp->set_type(EventLogTag_ValueDescriptor_DataType_INT);
desp = eventLogTag->add_value_descriptors();
desp->set_name("name");
desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING);
desp = eventLogTag->add_value_descriptors();
desp->set_name("format");
desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING);
eventLogTag = expected.add_event_log_tags();
eventLogTag->set_tag_number(2747);
eventLogTag->set_tag_name("contacts_aggregation");
desp = eventLogTag->add_value_descriptors();
desp->set_name("aggregation time");
desp->set_type(EventLogTag_ValueDescriptor_DataType_LONG);
desp->set_unit(EventLogTag_ValueDescriptor_DataUnit_MILLISECONDS);
desp = eventLogTag->add_value_descriptors();
desp->set_name("count");
desp->set_type(EventLogTag_ValueDescriptor_DataType_INT);
desp->set_unit(EventLogTag_ValueDescriptor_DataUnit_OBJECTS);
eventLogTag = expected.add_event_log_tags();
eventLogTag->set_tag_number(1397638484);
eventLogTag->set_tag_name("snet_event_log");
desp = eventLogTag->add_value_descriptors();
desp->set_name("subtag");
desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING);
desp = eventLogTag->add_value_descriptors();
desp->set_name("uid");
desp->set_type(EventLogTag_ValueDescriptor_DataType_INT);
desp = eventLogTag->add_value_descriptors();
desp->set_name("message");
desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING);
desp->set_unit(EventLogTag_ValueDescriptor_DataUnit_SECONDS);
int fd = open(testFile.c_str(), O_RDONLY);
ASSERT_TRUE(fd != -1);
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}

View File

@ -60,6 +60,9 @@ TEST(IhUtilTest, ParseRecord) {
result = parseRecord("123,456,78_9", ",");
expected = { "123", "456", "78_9" };
EXPECT_EQ(expected, result);
result = parseRecord("", " ");
EXPECT_TRUE(result.empty());
}
TEST(IhUtilTest, ParseRecordByColumns) {
@ -133,6 +136,22 @@ TEST(IhUtilTest, stripSuffix) {
EXPECT_THAT(data4, StrEq(" 243%abc"));
}
TEST(IhUtilTest, behead) {
string testcase1 = "81002 dropbox_file_copy (a)(b)";
EXPECT_THAT(behead(&testcase1, ' '), StrEq("81002"));
EXPECT_THAT(behead(&testcase1, ' '), StrEq("dropbox_file_copy"));
EXPECT_THAT(testcase1, "(a)(b)");
string testcase2 = "adbce,erwqr";
EXPECT_THAT(behead(&testcase2, ' '), StrEq("adbce,erwqr"));
EXPECT_THAT(testcase2, "");
string testcase3 = "first second";
EXPECT_THAT(behead(&testcase3, ' '), StrEq("first"));
EXPECT_THAT(behead(&testcase3, ' '), StrEq("second"));
EXPECT_THAT(testcase3, "");
}
TEST(IhUtilTest, Reader) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);

View File

@ -14,6 +14,9 @@
LOCAL_PATH:= $(call my-dir)
# proto files used in incidentd to generate cppstream proto headers.
PROTO_FILES:= frameworks/base/core/proto/android/util/log.proto
# ========= #
# incidentd #
# ========= #
@ -59,18 +62,34 @@ LOCAL_SHARED_LIBRARIES := \
libutils
LOCAL_MODULE_CLASS := EXECUTABLES
gen_src_dir := $(local-generated-sources-dir)
GEN := $(gen_src_dir)/src/section_list.cpp
$(GEN): $(HOST_OUT_EXECUTABLES)/incident-section-gen
$(GEN): PRIVATE_CUSTOM_TOOL = \
# generate section_list.cpp
GEN_LIST := $(gen_src_dir)/src/section_list.cpp
$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
$(GEN_LIST): PRIVATE_CUSTOM_TOOL = \
$(HOST_OUT_EXECUTABLES)/incident-section-gen incidentd > $@
$(GEN): $(HOST_OUT_EXECUTABLES)/incident-section-gen
$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
LOCAL_GENERATED_SOURCES += $(GEN_LIST)
GEN_LIST:=
# generate cppstream proto, add proto files to PROTO_FILES
GEN_PROTO := $(gen_src_dir)/proto.timestamp
$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
$(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
--cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
$(PROTO_FILES) \
&& touch $@
$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
GEN_PROTO:=
gen_src_dir:=
GEN:=
LOCAL_INIT_RC := incidentd.rc
@ -120,4 +139,22 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, testdata)
LOCAL_MODULE_CLASS := NATIVE_TESTS
gen_src_dir := $(local-generated-sources-dir)
# generate cppstream proto for testing
GEN_PROTO := $(gen_src_dir)/log.proto.timestamp
$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
$(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
--cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
$(PROTO_FILES) \
&& touch $@
$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
GEN_PROTO:=
gen_src_dir:=
include $(BUILD_NATIVE_TEST)

View File

@ -16,21 +16,29 @@
#define LOG_TAG "incidentd"
#include "FdBuffer.h"
#include "Privacy.h"
#include "PrivacyBuffer.h"
#include "Section.h"
#include "io_util.h"
#include "section_list.h"
#include <errno.h>
#include <unistd.h>
#include <wait.h>
#include <memory>
#include <mutex>
#include <android/util/protobuf.h>
#include <private/android_filesystem_config.h>
#include <binder/IServiceManager.h>
#include <map>
#include <mutex>
#include <wait.h>
#include <unistd.h>
#include <log/log_event_list.h>
#include <log/logprint.h>
#include <log/log_read.h>
#include <private/android_filesystem_config.h> // for AID_NOBODY
#include <private/android_logger.h>
#include "FdBuffer.h"
#include "frameworks/base/core/proto/android/util/log.proto.h"
#include "io_util.h"
#include "Privacy.h"
#include "PrivacyBuffer.h"
#include "section_list.h"
using namespace android::util;
using namespace std;
@ -41,7 +49,7 @@ const int FIELD_ID_INCIDENT_HEADER = 1;
// incident section parameters
const int WAIT_MAX = 5;
const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
const char* INCIDENT_HELPER = "/system/bin/incident_helper";
const char INCIDENT_HELPER[] = "/system/bin/incident_helper";
static pid_t
fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
@ -609,3 +617,160 @@ DumpsysSection::BlockingCall(int pipeWriteFd) const
return NO_ERROR;
}
// ================================================================================
// initialization only once in Section.cpp.
map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
LogSection::LogSection(int id, log_id_t logID)
:WorkerThreadSection(id),
mLogID(logID)
{
name += "logcat ";
name += android_log_id_to_name(logID);
switch (logID) {
case LOG_ID_EVENTS:
case LOG_ID_STATS:
case LOG_ID_SECURITY:
mBinary = true;
break;
default:
mBinary = false;
}
}
LogSection::~LogSection()
{
}
static size_t
trimTail(char const* buf, size_t len)
{
while (len > 0) {
char c = buf[len - 1];
if (c == '\0' || c == ' ' || c == '\n' || c == '\r' || c == ':') {
len--;
} else {
break;
}
}
return len;
}
static inline int32_t get4LE(uint8_t const* src) {
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
status_t
LogSection::BlockingCall(int pipeWriteFd) const
{
status_t err = NO_ERROR;
// Open log buffer and getting logs since last retrieved time if any.
unique_ptr<logger_list, void (*)(logger_list*)> loggers(
gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() ?
android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) :
android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
gLastLogsRetrieved[mLogID], 0),
android_logger_list_free);
if (android_logger_open(loggers.get(), mLogID) == NULL) {
ALOGW("LogSection %s: Can't get logger.", this->name.string());
return err;
}
log_msg msg;
log_time lastTimestamp(0);
ProtoOutputStream proto;
while (true) { // keeps reading until logd buffer is fully read.
status_t err = android_logger_list_read(loggers.get(), &msg);
// err = 0 - no content, unexpected connection drop or EOF.
// err = +ive number - size of retrieved data from logger
// err = -ive number, OS supplied error _except_ for -EAGAIN
// err = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end of data.
if (err <= 0) {
if (err != -EAGAIN) {
ALOGE("LogSection %s: fails to read a log_msg.\n", this->name.string());
}
break;
}
if (mBinary) {
// remove the first uint32 which is tag's index in event log tags
android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t),
msg.len() - sizeof(uint32_t));;
android_log_list_element elem;
lastTimestamp.tv_sec = msg.entry_v1.sec;
lastTimestamp.tv_nsec = msg.entry_v1.nsec;
// format a BinaryLogEntry
long long token = proto.start(LogProto::BINARY_LOGS);
proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec);
proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec);
proto.write(BinaryLogEntry::UID, (int) msg.entry_v4.uid);
proto.write(BinaryLogEntry::PID, msg.entry_v1.pid);
proto.write(BinaryLogEntry::TID, msg.entry_v1.tid);
proto.write(BinaryLogEntry::TAG_INDEX, get4LE(reinterpret_cast<uint8_t const*>(msg.msg())));
do {
elem = android_log_read_next(context);
long long elemToken = proto.start(BinaryLogEntry::ELEMS);
switch (elem.type) {
case EVENT_TYPE_INT:
proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_INT);
proto.write(BinaryLogEntry::Elem::VAL_INT32, (int) elem.data.int32);
break;
case EVENT_TYPE_LONG:
proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LONG);
proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long) elem.data.int64);
break;
case EVENT_TYPE_STRING:
proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_STRING);
proto.write(BinaryLogEntry::Elem::VAL_STRING, elem.data.string, elem.len);
break;
case EVENT_TYPE_FLOAT:
proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_FLOAT);
proto.write(BinaryLogEntry::Elem::VAL_FLOAT, elem.data.float32);
break;
case EVENT_TYPE_LIST:
proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST);
break;
case EVENT_TYPE_LIST_STOP:
proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP);
break;
case EVENT_TYPE_UNKNOWN:
proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN);
break;
}
proto.end(elemToken);
} while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
proto.end(token);
if (context) {
android_log_destroy(&context);
}
} else {
AndroidLogEntry entry;
err = android_log_processLogBuffer(&msg.entry_v1, &entry);
if (err != NO_ERROR) {
ALOGE("LogSection %s: fails to process to an entry.\n", this->name.string());
break;
}
lastTimestamp.tv_sec = entry.tv_sec;
lastTimestamp.tv_nsec = entry.tv_nsec;
// format a TextLogEntry
long long token = proto.start(LogProto::TEXT_LOGS);
proto.write(TextLogEntry::SEC, (long long)entry.tv_sec);
proto.write(TextLogEntry::NANOSEC, (long long)entry.tv_nsec);
proto.write(TextLogEntry::PRIORITY, (int)entry.priority);
proto.write(TextLogEntry::UID, entry.uid);
proto.write(TextLogEntry::PID, entry.pid);
proto.write(TextLogEntry::TID, entry.tid);
proto.write(TextLogEntry::TAG, entry.tag, trimTail(entry.tag, entry.tagLen));
proto.write(TextLogEntry::LOG, entry.message, trimTail(entry.message, entry.messageLen));
proto.end(token);
}
}
gLastLogsRetrieved[mLogID] = lastTimestamp;
proto.flush(pipeWriteFd);
return err;
}

View File

@ -19,7 +19,9 @@
#include "Reporter.h"
#include <map>
#include <stdarg.h>
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@ -122,5 +124,24 @@ private:
Vector<String16> mArgs;
};
/**
* Section that reads from logd.
*/
class LogSection : public WorkerThreadSection
{
// global last log retrieved timestamp for each log_id_t.
static map<log_id_t, log_time> gLastLogsRetrieved;
public:
LogSection(int id, log_id_t logID);
virtual ~LogSection();
virtual status_t BlockingCall(int pipeWriteFd) const;
private:
log_id_t mLogID;
bool mBinary;
};
#endif // SECTIONS_H

View File

@ -17,6 +17,8 @@
#ifndef SECTION_LIST_H
#define SECTION_LIST_H
#include <log/log_event_list.h> // include log_id_t enums.
#include "Privacy.h"
#include "Section.h"

View File

@ -155,7 +155,7 @@ TEST(SectionTest, CommandSectionIncidentHelperTimeout) {
}
TEST(SectionTest, CommandSectionBadCommand) {
CommandSection cs(NOOP_PARSER, "echo", "about", NULL);
CommandSection cs(NOOP_PARSER, "echoo", "about", NULL);
ReportRequestSet requests;
ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests));
}
@ -167,6 +167,26 @@ TEST(SectionTest, CommandSectionBadCommandAndTimeout) {
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
}
TEST(SectionTest, LogSectionBinary) {
LogSection ls(1, LOG_ID_EVENTS);
ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
CaptureStdout();
ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
string results = GetCapturedStdout();
EXPECT_FALSE(results.empty());
}
TEST(SectionTest, LogSectionSystem) {
LogSection ls(1, LOG_ID_SYSTEM);
ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
CaptureStdout();
ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
string results = GetCapturedStdout();
EXPECT_FALSE(results.empty());
}
TEST(SectionTest, TestFilterPiiTaggedFields) {
TemporaryFile tf;
FileSection fs(NOOP_PARSER, tf.path);

View File

@ -43,6 +43,8 @@ import "frameworks/base/core/proto/android/service/notification.proto";
import "frameworks/base/core/proto/android/service/package.proto";
import "frameworks/base/core/proto/android/service/print.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
@ -59,6 +61,8 @@ message IncidentMetadata {
// the sections are able to be controlled and configured by section ids.
// Instead privacy field options need to be configured in each section proto message.
message IncidentProto {
reserved 1001;
// Incident header from callers
repeated IncidentHeaderProto header = 1;
// Internal metadata of incidentd
@ -70,6 +74,52 @@ message IncidentProto {
(section).args = "getprop"
];
// Device Logs
optional android.util.EventLogTagMapProto event_log_tag_map = 1100 [
(section).type = SECTION_FILE,
(section).args = "/system/etc/event-log-tags"
];
optional android.util.LogProto main_logs = 1101 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_MAIN"
];
optional android.util.LogProto radio_logs = 1102 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_RADIO"
];
optional android.util.LogProto events_logs = 1103 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_EVENTS"
];
optional android.util.LogProto system_logs = 1104 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_SYSTEM"
];
optional android.util.LogProto crash_logs = 1105 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_CRASH"
];
optional android.util.LogProto stats_logs = 1106 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_STATS"
];
optional android.util.LogProto security_logs = 1107 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_SECURITY"
];
optional android.util.LogProto kernel_logs = 1108 [
(section).type = SECTION_LOG,
(section).args = "LOG_ID_KERNEL"
];
// Linux services
optional Procrank procrank = 2000 [
(section).type = SECTION_COMMAND,
@ -196,4 +246,5 @@ message IncidentProto {
(section).type = SECTION_DUMPSYS,
(section).args = "graphicsstats --proto"
];
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto2";
package android.util;
option java_multiple_files = true;
// Proto representation of event.logtags.
// Usually sit in /system/etc/event-log-tags.
message EventLogTagMapProto {
repeated EventLogTag event_log_tags = 1;
}
message EventLogTag {
optional uint32 tag_number = 1; // keyed by tag number.
optional string tag_name = 2;
message ValueDescriptor {
optional string name = 1;
enum DataType {
UNKNOWN = 0;
INT = 1;
LONG = 2;
STRING = 3;
LIST = 4;
FLOAT = 5;
}
optional DataType type = 2;
enum DataUnit {
UNSET = 0; // this field is optional, so default is unset
OBJECTS = 1; // Number of objects
BYTES = 2; // Number of bytes (default for type of int/long)
MILLISECONDS = 3; // Number of milliseconds
ALLOCATIONS = 4; // Number of allocations
ID = 5; // Id
PERCENT = 6; // Percent
SECONDS = 115; // 's', Number of seconds (monotonic time)
}
optional DataUnit unit = 3;
}
repeated ValueDescriptor value_descriptors = 3;
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto2";
package android.util;
option java_multiple_files = true;
// Represents a Text Log in logd
// Next Tag: 9
message TextLogEntry {
optional uint64 sec = 1;
optional uint64 nanosec = 2;
enum LogPriority {
LOG_UNKNOWN = 0;
LOG_DEFAULT = 1;
LOG_VERBOSE = 2;
LOG_DEBUG = 3;
LOG_INFO = 4;
LOG_WARN = 5;
LOG_ERROR = 6;
LOG_FATAL = 7;
LOG_SILENT = 8;
}
optional LogPriority priority = 3;
optional int32 uid = 4;
optional int32 pid = 5;
optional int32 tid = 6;
optional string tag = 7;
optional string log = 8;
}
// Represents a Binary Log in logd, need to look event-log-tags for more info.
// Next Tag: 8
message BinaryLogEntry {
optional uint64 sec = 1;
optional uint64 nanosec = 2;
optional int32 uid = 3;
optional int32 pid = 4;
optional int32 tid = 5;
// Index of the event tag, can look up in event-log-tags file
optional uint32 tag_index = 6;
message Elem {
// must be sync with liblog log/log.h
enum Type {
EVENT_TYPE_LIST_STOP = 10; // '\n'
EVENT_TYPE_UNKNOWN = 63; // '?'
EVENT_TYPE_INT = 0;
EVENT_TYPE_LONG = 1;
EVENT_TYPE_STRING = 2;
EVENT_TYPE_LIST = 3;
EVENT_TYPE_FLOAT = 4;
}
optional Type type = 1 [default=EVENT_TYPE_UNKNOWN];
oneof value {
int32 val_int32 = 2;
int64 val_int64 = 3;
string val_string = 4;
float val_float = 5;
}
}
repeated Elem elems = 7;
}
message LogProto {
repeated TextLogEntry text_logs = 1;
repeated BinaryLogEntry binary_logs = 2;
}

View File

@ -37,6 +37,9 @@ enum SectionType {
// incidentd calls dumpsys for annotated field
SECTION_DUMPSYS = 3;
// incidentd calls logs for annotated field
SECTION_LOG = 4;
}
message SectionFlags {

View File

@ -408,6 +408,9 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
splitAndPrint(s.args());
printf(" NULL),\n");
break;
case SECTION_LOG:
printf(" new LogSection(%d, %s),\n", field->number(), s.args().c_str());
break;
}
}
printf(" NULL };\n");