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:
parent
a598c05d63
commit
3c034c987e
10
Android.bp
10
Android.bp
@ -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",
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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:
|
||||
|
84
cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
Normal file
84
cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
Normal 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;
|
||||
}
|
35
cmds/incident_helper/src/parsers/EventLogTagsParser.h
Normal file
35
cmds/incident_helper/src/parsers/EventLogTagsParser.h
Normal 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
|
6
cmds/incident_helper/testdata/event-log-tags.txt
vendored
Normal file
6
cmds/incident_helper/testdata/event-log-tags.txt
vendored
Normal 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)
|
125
cmds/incident_helper/tests/EventLogTagsParser_test.cpp
Normal file
125
cmds/incident_helper/tests/EventLogTagsParser_test.cpp
Normal 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);
|
||||
}
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
];
|
||||
|
||||
}
|
||||
|
58
core/proto/android/util/event_log_tags.proto
Normal file
58
core/proto/android/util/event_log_tags.proto
Normal 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;
|
||||
}
|
88
core/proto/android/util/log.proto
Normal file
88
core/proto/android/util/log.proto
Normal 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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user