Snap for 8176975 from 45b6106898f6bca88d66639354b536d770d1c53c to tm-release
Change-Id: I573bd705d6894ba1d32a09f7333ff385943939bd
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..ce3da9a
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2022 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+ name: "dmesgd_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-argument",
+ "-Wno-unused-function",
+ "-Wno-nullability-completeness",
+ "-Os",
+ ],
+}
+
+cc_binary {
+ name: "dmesgd",
+ srcs: [
+ "dmesgd.cpp",
+ "dmesg_parser.cpp",
+ ],
+ defaults: ["dmesgd_defaults"],
+ shared_libs: [
+ "libbase",
+ "libevent",
+ "liblog",
+ "libservices",
+ "libutils",
+ ],
+ init_rc: ["dmesgd.rc"],
+}
+
+cc_test {
+ name: "dmesg_parser_test",
+ defaults: ["dmesgd_defaults"],
+ require_root: false,
+ srcs: [
+ "dmesg_parser.cpp",
+ "dmesg_parser_test.cpp",
+ ],
+}
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..3e4631b
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+enh@google.com
+glider@google.com
diff --git a/dmesg_parser.cpp b/dmesg_parser.cpp
new file mode 100644
index 0000000..6034965
--- /dev/null
+++ b/dmesg_parser.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2022, 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 <regex>
+#include <string>
+
+#include "dmesg_parser.h"
+
+namespace dmesg_parser {
+
+const std::string kTimestampRe = "^\\[[^\\]]+\\]\\s";
+
+DmesgParser::DmesgParser() : report_ready_(false) {
+ std::string bug_types;
+ for (auto t : {"KFENCE", "KASAN"}) {
+ if (bug_types.empty()) {
+ bug_types = t;
+ } else {
+ bug_types.append("|");
+ bug_types.append(t);
+ }
+ }
+ std::string bug_re = kTimestampRe + "\\[([0-9T\\s]+)\\]\\s(BUG: (" + bug_types + "):.*)";
+ this->bug_pattern_ = std::regex(bug_re);
+ this->ignore_pattern_ = std::regex("([ _][Rx]..|raw): [0-9a-f]{16}|"
+ "Hardware name:|Comm:");
+ this->addr64_pattern_ = std::regex("\\b(?:0x)?[0-9a-f]{16}\\b");
+}
+
+/*
+ * Read a single line terminated by a newline, and process it as follows:
+ * 1. If we haven't seen a bug header, skip the current line unless it contains
+ * "BUG:".
+ * If it does, parse the line to extract the task ID (T1234), tool name
+ * (KASAN or KFENCE) and the whole report title (needed for report
+ * deduplication).
+ * 2. If the current line does not contain the known task ID, skip it.
+ * 3. If the current line contains a delimiter ("====="), stop accepting new
+ * lines.
+ * 4. Otherwise strip potential sensitive data from the current line and append
+ * it to the report.
+ */
+void DmesgParser::ProcessLine(const std::string& line) {
+ if (report_ready_) return;
+
+ // We haven't encountered a BUG: line yet.
+ if (current_report_.empty()) {
+ std::smatch m;
+ if (std::regex_search(line, m, bug_pattern_)) {
+ std::string task_re = kTimestampRe + "\\[" + std::string(m[1]) + "\\]\\s";
+ task_line_pattern_ = std::regex(task_re);
+ task_delimiter_pattern_ = std::regex(task_re + "={10,}");
+ current_title_ = m[2];
+ current_tool_ = m[3];
+ current_report_ = this->StripSensitiveData(line);
+ }
+ return;
+ }
+
+ // If there is a delimiter, mark the current report as ready.
+ if (std::regex_search(line, task_delimiter_pattern_)) {
+ report_ready_ = true;
+ return;
+ }
+
+ if (std::regex_search(line, task_line_pattern_)) current_report_ += StripSensitiveData(line);
+}
+
+/*
+ * Return true iff the current report is ready (it was terminated by the "====="
+ * delimiter.
+ */
+bool DmesgParser::ReportReady() const {
+ return report_ready_;
+}
+
+/*
+ * Return the tool that generated the currently collected report.
+ */
+std::string DmesgParser::ReportType() const {
+ return current_tool_;
+}
+
+/*
+ * Return the title of the currently collected report.
+ */
+std::string DmesgParser::ReportTitle() const {
+ return current_title_;
+}
+
+/*
+ * Return the report collected so far and reset the parser.
+ */
+std::string DmesgParser::FlushReport() {
+ report_ready_ = false;
+ return std::move(current_report_);
+}
+
+/*
+ * Strip potentially sensitive data from the reports by performing the
+ * following actions:
+ * 1. Drop the entire line, if it contains a process name:
+ * [ 69.547684] [ T6006]c7 6006 CPU: 7 PID: 6006 Comm: sh Tainted:
+ *
+ * or hardware name:
+ * [ 69.558923] [ T6006]c7 6006 Hardware name: Phone1
+ *
+ * or a memory dump, e.g.:
+ *
+ * ... raw: 4000000000010200 0000000000000000 0000000000000000
+ *
+ * or register dump:
+ *
+ * ... RIP: 0033:0x7f96443109da
+ * ... RSP: 002b:00007ffcf0b51b08 EFLAGS: 00000202 ORIG_RAX: 00000000000000af
+ * ... RAX: ffffffffffffffda RBX: 000055dc3ee521a0 RCX: 00007f96443109da
+ *
+ * (on x86_64)
+ *
+ * ... pc : lpm_cpuidle_enter+0x258/0x384
+ * ... lr : lpm_cpuidle_enter+0x1d4/0x384
+ * ... sp : ffffff800820bea0
+ * ... x29: ffffff800820bea0 x28: ffffffc2305f3ce0
+ * ... ...
+ * ... x9 : 0000000000000001 x8 : 0000000000000000
+ *
+ * (on ARM64)
+ *
+ * 2. For substrings that are known to be followed by sensitive information,
+ * cut the line after those substrings and append "DELETED\n",
+ * e.g. " by task ":
+ * ... Read at addr f0ffff87c23fdf7f by task sh/9971
+ * and "Corrupted memory at":
+ * ... Corrupted memory at 0xf0ffff87c23fdf00 [ ! . . . . . . . . . . . . . . . ]
+ *
+ * 3. Replace all strings that look like 64-bit hexadecimal values, with
+ * XXXXXXXXXXXXXXXX.
+ */
+std::string DmesgParser::StripSensitiveData(const std::string& line) const {
+ if (std::regex_search(line, ignore_pattern_)) return "";
+
+ std::string ret = line;
+ for (std::string infix : {"Corrupted memory at ", " by task "}) {
+ auto pos = ret.find(infix);
+ if (pos != std::string::npos) {
+ ret = ret.substr(0, pos + infix.size()) + "DELETED\n";
+ }
+ }
+ ret = std::regex_replace(ret, addr64_pattern_, "XXXXXXXXXXXXXXXX");
+ return ret;
+}
+
+} // namespace dmesg_parser
diff --git a/dmesg_parser.h b/dmesg_parser.h
new file mode 100644
index 0000000..7f6b6a9
--- /dev/null
+++ b/dmesg_parser.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022, 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.
+ */
+
+#pragma once
+
+#include <regex>
+#include <string>
+
+namespace dmesg_parser {
+
+class DmesgParser {
+ public:
+ DmesgParser();
+ void ProcessLine(const std::string& line);
+ bool ReportReady() const;
+ std::string ReportType() const;
+ std::string ReportTitle() const;
+ std::string FlushReport();
+
+ private:
+ std::string StripSensitiveData(const std::string& line) const;
+
+ bool report_ready_;
+ std::string last_report_;
+ std::regex bug_pattern_, ignore_pattern_, addr64_pattern_;
+ std::regex task_line_pattern_, task_delimiter_pattern_;
+ std::string current_report_;
+ std::string current_task_, current_tool_, current_title_;
+};
+
+} // namespace dmesg_parser
diff --git a/dmesg_parser_test.cpp b/dmesg_parser_test.cpp
new file mode 100644
index 0000000..0d288b6
--- /dev/null
+++ b/dmesg_parser_test.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2022, 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 <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "dmesg_parser.h"
+
+class DmesgParserTest : public ::testing::Test {
+ public:
+ void ReadLines(const std::vector<std::string>& lines);
+ bool CheckReport(const std::vector<std::string>& lines);
+
+ dmesg_parser::DmesgParser parser;
+ std::string parsed_report;
+};
+
+void DmesgParserTest::ReadLines(const std::vector<std::string>& lines) {
+ for (auto line : lines) parser.ProcessLine(line);
+}
+
+bool DmesgParserTest::CheckReport(const std::vector<std::string>& lines) {
+ if (!parser.ReportReady()) return false;
+ parsed_report = parser.FlushReport();
+
+ std::string report;
+ for (auto line : lines) {
+ report += line;
+ }
+ EXPECT_EQ(report, parsed_report);
+ return report == parsed_report;
+}
+
+TEST_F(DmesgParserTest, SimpleKasanReport) {
+ std::vector<std::string> in = {
+ "[ 495.412333] [ T1] init: this line will be dropped\n",
+ "[ 495.412345] [ T9971] "
+ "==================================================================\n",
+ "[ 495.496694] [ T9971] BUG: KASAN: invalid-access in crash_write+0x134/0x140\n",
+ "[ 495.712345] [ T9971] "
+ "==================================================================\n",
+ "[ 495.767899] [ T9971] logs after the separator do not belong to report\n",
+ };
+
+ std::vector<std::string> report = {
+ "[ 495.496694] [ T9971] BUG: KASAN: invalid-access in crash_write+0x134/0x140\n",
+ };
+
+ ReadLines(in);
+ ASSERT_TRUE(parser.ReportReady());
+ ASSERT_EQ("KASAN", parser.ReportType());
+ ASSERT_EQ("BUG: KASAN: invalid-access in crash_write+0x134/0x140", parser.ReportTitle());
+ ASSERT_TRUE(CheckReport(report));
+}
+
+TEST_F(DmesgParserTest, StrippedKasanReport) {
+ /*
+ * From the following report, only the lines from T9971 between the "======="
+ * delimiters will be preserved, and only those that do not contain raw
+ * memory.
+ * Task name is also stripped off, because it may contain sensitive data.
+ */
+ std::vector<std::string> in = {
+ "[ 495.412333] [ T1] init: this line will be dropped\n",
+ "[ 495.412345] [ T9971] "
+ "==================================================================\n",
+ "[ 495.496694] [ T9971] BUG: KASAN: invalid-access in crash_write+0x134/0x140\n",
+ "[ 495.501234] [ T333] random_process: interleaving output with our error report\n",
+ "[ 495.503671] [ T9971] Read at addr f0ffff87c23fdf7f by task sh/9971\n",
+ "[ 495.510025] [ T9971] Pointer tag: [f0], memory tag: [fe]\n",
+ "[ 495.515400] [ T9971] \n",
+ "[ 495.667603] [ T9971] raw: 4000000000010200 0000000000000000 0000000000000000 "
+ "0000000100200020\n",
+ "[ 495.667634] [ T9971] raw: dead000000000100 dead000000000200 ffffffc14900fc00 "
+ "0000000000000000\n",
+ "[ 495.712345] [ T9971] "
+ "==================================================================\n",
+ "[ 495.767899] [ T9971] logs after the separator do not belong to report\n",
+ };
+
+ std::vector<std::string> report = {
+ "[ 495.496694] [ T9971] BUG: KASAN: invalid-access in crash_write+0x134/0x140\n",
+ "[ 495.503671] [ T9971] Read at addr XXXXXXXXXXXXXXXX by task DELETED\n",
+ "[ 495.510025] [ T9971] Pointer tag: [f0], memory tag: [fe]\n",
+ "[ 495.515400] [ T9971] \n",
+ };
+
+ ReadLines(in);
+ ASSERT_TRUE(parser.ReportReady());
+ ASSERT_EQ("KASAN", parser.ReportType());
+ ASSERT_EQ("BUG: KASAN: invalid-access in crash_write+0x134/0x140", parser.ReportTitle());
+ ASSERT_TRUE(CheckReport(report));
+}
+
+TEST_F(DmesgParserTest, SimpleKfenceReport) {
+ std::vector<std::string> in = {
+ "[ 495.412333] [ T1] init: this line will be dropped\n",
+ "[ 495.412345] [ T9971] "
+ "==================================================================\n",
+ "[ 495.496694] [ T9971] BUG: KFENCE: memory corruption in "
+ "test_corruption+0x98/0x19c\n",
+ "[ 495.712345] [ T9971] "
+ "==================================================================\n",
+ "[ 495.767899] [ T9971] logs after the separator do not belong to report\n",
+ };
+
+ std::vector<std::string> report = {
+ "[ 495.496694] [ T9971] BUG: KFENCE: memory corruption in "
+ "test_corruption+0x98/0x19c\n",
+ };
+
+ ReadLines(in);
+ ASSERT_TRUE(parser.ReportReady());
+ ASSERT_EQ("KFENCE", parser.ReportType());
+ ASSERT_EQ("BUG: KFENCE: memory corruption in test_corruption+0x98/0x19c", parser.ReportTitle());
+ ASSERT_TRUE(CheckReport(report));
+}
+
+TEST_F(DmesgParserTest, StrippedKfenceReport) {
+ std::vector<std::string> in = {
+ "[ 200.412333] [ T1] init: this line will be dropped\n",
+ "[ 213.648234] [ T8752] "
+ "==================================================================\n",
+ "[ 213.648253] [ T8752] BUG: KFENCE: out-of-bounds write in crash_write+0x14c/0x174\n",
+ "[ 213.648262] [ T8752] Out-of-bounds write at 0xffffff8938a05000 (4096B left of "
+ "kfence-#2):\n",
+ "[ 213.648270] [ T8752] crash_write+0x14c/0x174\n",
+ "[ 213.648367] [ T8752] kfence-#2 [0xffffff8938a06000-0xffffff8938a0603f, size=64, "
+ "cache=kmalloc-128] allocated by task 1:\n",
+ "[ 213.648471] [ T8752] CPU: 1 PID: 8752 Comm: sh Tainted: G C O\n",
+ "[ 213.648478] [ T8752] Hardware name: Phone 1\n",
+ "[ 213.648498] [ T8752] "
+ "==================================================================\n",
+ "[ 495.767899] [ T8752] logs after the separator do not belong to report\n",
+ };
+
+ std::vector<std::string> report = {
+ "[ 213.648253] [ T8752] BUG: KFENCE: out-of-bounds write in crash_write+0x14c/0x174\n",
+ "[ 213.648262] [ T8752] Out-of-bounds write at XXXXXXXXXXXXXXXX (4096B left of "
+ "kfence-#2):\n",
+ "[ 213.648270] [ T8752] crash_write+0x14c/0x174\n",
+ "[ 213.648367] [ T8752] kfence-#2 [XXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXX, size=64, "
+ "cache=kmalloc-128] allocated by task DELETED\n",
+ };
+
+ ReadLines(in);
+ ASSERT_TRUE(parser.ReportReady());
+ ASSERT_EQ("KFENCE", parser.ReportType());
+ ASSERT_EQ("BUG: KFENCE: out-of-bounds write in crash_write+0x14c/0x174", parser.ReportTitle());
+ ASSERT_TRUE(CheckReport(report));
+}
+
+TEST_F(DmesgParserTest, PartialReport) {
+ std::vector<std::string> in = {
+ "[ 213.648234] [ T8752] "
+ "==================================================================\n",
+ "[ 213.648253] [ T8752] BUG: KFENCE: out-of-bounds write in crash_write+0x14c/0x174\n",
+ "[ 213.648262] [ T8752] Out-of-bounds write at 0xffffff8938a05000 (4096B left of "
+ "kfence-#2):\n",
+ "[ 213.648270] [ T8752] crash_write+0x14c/0x174\n",
+ };
+
+ ReadLines(in);
+ ASSERT_FALSE(parser.ReportReady());
+}
+
+TEST_F(DmesgParserTest, TwoReports) {
+ std::vector<std::string> in = {
+ "[ 200.412333] [ T1] init: this line will be dropped\n",
+ "[ 213.648234] [ T8752] "
+ "==================================================================\n",
+ "[ 213.648253] [ T8752] BUG: KFENCE: out-of-bounds write in crash_write+0x14c/0x174\n",
+ "[ 213.648262] [ T8752] Out-of-bounds write at 0xffffff8938a05000 (4096B left of "
+ "kfence-#2):\n",
+ "[ 214.648234] [ T9971] "
+ "==================================================================\n",
+ "[ 215.496694] [ T9971] BUG: KFENCE: memory corruption in "
+ "test_corruption+0x98/0x19c\n",
+ "[ 216.648270] [ T8752] crash_write+0x14c/0x174\n",
+ "[ 217.648234] [ T8752] "
+ "==================================================================\n",
+ };
+
+ std::vector<std::string> report = {
+ "[ 213.648253] [ T8752] BUG: KFENCE: out-of-bounds write in crash_write+0x14c/0x174\n",
+ "[ 213.648262] [ T8752] Out-of-bounds write at XXXXXXXXXXXXXXXX (4096B left of "
+ "kfence-#2):\n",
+ "[ 216.648270] [ T8752] crash_write+0x14c/0x174\n",
+ };
+
+ ReadLines(in);
+ ASSERT_TRUE(parser.ReportReady());
+ ASSERT_EQ("KFENCE", parser.ReportType());
+ ASSERT_EQ("BUG: KFENCE: out-of-bounds write in crash_write+0x14c/0x174", parser.ReportTitle());
+ ASSERT_TRUE(CheckReport(report));
+}
diff --git a/dmesgd.cpp b/dmesgd.cpp
new file mode 100644
index 0000000..d9f673f
--- /dev/null
+++ b/dmesgd.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2022, 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 <sys/epoll.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <array>
+#include <set>
+#include <string>
+
+#include <android/os/DropBoxManager.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "dmesg_parser.h"
+
+// If there are too many reports, the device is horribly broken.
+const unsigned int kMaxReports = 10;
+
+const char kSentPath[] = "/data/misc/dmesgd/sent_reports.txt";
+
+static std::set<std::string> ReadSentReports() {
+ std::set<std::string> ret;
+ std::string content;
+ if (!android::base::ReadFileToString(kSentPath, &content)) {
+ PLOG(ERROR) << kSentPath << " is empty";
+ return ret;
+ }
+ auto lines = android::base::Split(content, "\n");
+ for (auto line : lines) {
+ ret.insert(line);
+ }
+ LOG(ERROR) << "Read " << ret.size() << " records from " << kSentPath;
+ return ret;
+}
+
+static void WriteSentReports(std::set<std::string> reports) {
+ if (!android::base::WriteStringToFile(android::base::Join(reports, ""), kSentPath)) {
+ PLOG(ERROR) << "Failed to write to " << kSentPath;
+ }
+}
+
+const char kUnknown[] = "UNKNOWN";
+
+static std::string GetOneBootHeader(const std::string& pretty, const std::string& pname) {
+ return pretty + ": " + android::base::GetProperty(pname, kUnknown) + "\n";
+};
+
+static std::string GetBootHeaders() {
+ std::string ret = GetOneBootHeader("Build", "ro.build.fingerprint");
+ ret += GetOneBootHeader("Hardware", "ro.product.board");
+ ret += GetOneBootHeader("Revision", "ro.revision");
+ ret += GetOneBootHeader("Bootloader", "ro.bootloader");
+ ret += GetOneBootHeader("Radio", "gsm.version.baseband");
+
+ std::string version;
+ if (!android::base::ReadFileToString("/proc/version", &version)) version = kUnknown;
+ ret += "Kernel: " + version + "\n\n";
+ return ret;
+}
+
+static bool StoreReport(const std::string& tag, const std::string& report) {
+ std::string boot_headers = GetBootHeaders();
+ android::sp<android::os::DropBoxManager> dropbox(new android::os::DropBoxManager());
+ auto status = dropbox->addText(android::String16(tag.c_str()), boot_headers + report);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Dropbox failed";
+ return false;
+ }
+ return true;
+}
+
+static int ProcessDmesg(std::set<std::string> &sent_reports) {
+ std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("dmesg", "r"), pclose);
+ if (!pipe) {
+ PLOG(ERROR) << "popen() failed!";
+ return 1;
+ }
+ dmesg_parser::DmesgParser dmesg_parser;
+
+ char* buffer = NULL;
+ size_t buffer_size = 0;
+ while (getline(&buffer, &buffer_size, pipe.get()) != -1) {
+ std::string line(buffer);
+ if (line.back() != '\n') line += "\n";
+ dmesg_parser.ProcessLine(line);
+ if (dmesg_parser.ReportReady()) {
+ std::string tag = "SYSTEM_" + dmesg_parser.ReportType() + "_ERROR_REPORT";
+ std::string title = dmesg_parser.ReportTitle();
+ if ((sent_reports.find(title) == sent_reports.end()) &&
+ (sent_reports.size() < kMaxReports)) {
+ if (StoreReport(tag, dmesg_parser.FlushReport())) sent_reports.insert(title);
+ }
+ }
+ }
+ free(buffer);
+ return 0;
+}
+
+int main(int, char*[]) {
+ auto sent_reports = ReadSentReports();
+ int result = ProcessDmesg(sent_reports);
+ WriteSentReports(sent_reports);
+ return result;
+}
diff --git a/dmesgd.rc b/dmesgd.rc
new file mode 100644
index 0000000..e5ffdd4
--- /dev/null
+++ b/dmesgd.rc
@@ -0,0 +1,16 @@
+# Copyright (C) 2022 The Android Open Source Project
+
+on property:ro.product.cpu.abilist64=* && property:bootreceiver.enable=1
+ mkdir /data/misc/dmesgd 0700 dmesgd system
+ rm /data/misc/dmesgd/sent_reports.txt
+
+on property:ro.product.cpu.abilist64=* && property:bootreceiver.enable=1 && property:dmesgd.start=1
+ start dmesgd
+ setprop dmesgd.start 0
+
+service dmesgd /system/bin/dmesgd
+ user dmesgd
+ group system
+ class main
+ disabled
+ oneshot