summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp10
-rw-r--r--cmds/incident_helper/src/ih_util.cpp13
-rw-r--r--cmds/incident_helper/src/ih_util.h7
-rw-r--r--cmds/incident_helper/src/main.cpp3
-rw-r--r--cmds/incident_helper/src/parsers/EventLogTagsParser.cpp84
-rw-r--r--cmds/incident_helper/src/parsers/EventLogTagsParser.h35
-rw-r--r--cmds/incident_helper/testdata/event-log-tags.txt6
-rw-r--r--cmds/incident_helper/tests/EventLogTagsParser_test.cpp125
-rw-r--r--cmds/incident_helper/tests/ih_util_test.cpp19
-rw-r--r--cmds/incidentd/Android.mk49
-rw-r--r--cmds/incidentd/src/Section.cpp187
-rw-r--r--cmds/incidentd/src/Section.h21
-rw-r--r--cmds/incidentd/src/section_list.h2
-rw-r--r--cmds/incidentd/tests/Section_test.cpp22
-rw-r--r--core/proto/android/os/incident.proto51
-rw-r--r--core/proto/android/util/event_log_tags.proto58
-rw-r--r--core/proto/android/util/log.proto88
-rw-r--r--libs/incident/proto/android/section.proto3
-rw-r--r--tools/incident_section_gen/main.cpp3
19 files changed, 767 insertions, 19 deletions
diff --git a/Android.bp b/Android.bp
index 69ee848cadad..03a79f61def7 100644
--- a/Android.bp
+++ b/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",
}
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index e23e80ae21e8..847b26a39ffe 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -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());
}
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index b063b2fe0bba..53f443873e4d 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -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);
@@ -89,6 +91,11 @@ 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
*/
int toInt(const std::string& s);
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 8c6cd78d3bf2..418dc3fad761 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -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:
diff --git a/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
new file mode 100644
index 000000000000..73e37bd166cd
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
@@ -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;
+}
diff --git a/cmds/incident_helper/src/parsers/EventLogTagsParser.h b/cmds/incident_helper/src/parsers/EventLogTagsParser.h
new file mode 100644
index 000000000000..79057ce0b3ca
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/EventLogTagsParser.h
@@ -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
diff --git a/cmds/incident_helper/testdata/event-log-tags.txt b/cmds/incident_helper/testdata/event-log-tags.txt
new file mode 100644
index 000000000000..35396bfb4250
--- /dev/null
+++ b/cmds/incident_helper/testdata/event-log-tags.txt
@@ -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) \ No newline at end of file
diff --git a/cmds/incident_helper/tests/EventLogTagsParser_test.cpp b/cmds/incident_helper/tests/EventLogTagsParser_test.cpp
new file mode 100644
index 000000000000..d0d1f1e023a8
--- /dev/null
+++ b/cmds/incident_helper/tests/EventLogTagsParser_test.cpp
@@ -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);
+}
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
index 7b8cf52c8bee..efe714d98b18 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -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);
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 11d3e4911761..1cf02d35636c 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -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)
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 22053ef3c53a..61d16f815e65 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -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;
+}
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 64558a6b732b..d440ee92601c 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -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
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
index dfd2312df668..ddc0505df331 100644
--- a/cmds/incidentd/src/section_list.h
+++ b/cmds/incidentd/src/section_list.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"
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 0c7876c1ecfb..cbfb89685de0 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -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);
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index a6db31f67b33..2f856ab68768 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -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"
];
+
}
diff --git a/core/proto/android/util/event_log_tags.proto b/core/proto/android/util/event_log_tags.proto
new file mode 100644
index 000000000000..cb039be55b85
--- /dev/null
+++ b/core/proto/android/util/event_log_tags.proto
@@ -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;
+} \ No newline at end of file
diff --git a/core/proto/android/util/log.proto b/core/proto/android/util/log.proto
new file mode 100644
index 000000000000..30ff41242bcc
--- /dev/null
+++ b/core/proto/android/util/log.proto
@@ -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;
+}
+
diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto
index d268cf4fc09a..49bfe1e8a598 100644
--- a/libs/incident/proto/android/section.proto
+++ b/libs/incident/proto/android/section.proto
@@ -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 {
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 102bbf9d07d7..e7b269aaa9a5 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -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");