blob: d9f673f61d6b64feb0e4f9e265183957af1f6963 [file] [log] [blame]
Alexander Potapenko93b854f2022-02-07 19:22:52 +01001/*
2 * Copyright 2022, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <sys/epoll.h>
18#include <sys/types.h>
19#include <unistd.h>
20
21#include <array>
22#include <set>
23#include <string>
24
25#include <android/os/DropBoxManager.h>
26
27#include <android-base/file.h>
28#include <android-base/logging.h>
29#include <android-base/properties.h>
30#include <android-base/strings.h>
31
32#include "dmesg_parser.h"
33
34// If there are too many reports, the device is horribly broken.
35const unsigned int kMaxReports = 10;
36
37const char kSentPath[] = "/data/misc/dmesgd/sent_reports.txt";
38
39static std::set<std::string> ReadSentReports() {
40 std::set<std::string> ret;
41 std::string content;
42 if (!android::base::ReadFileToString(kSentPath, &content)) {
43 PLOG(ERROR) << kSentPath << " is empty";
44 return ret;
45 }
46 auto lines = android::base::Split(content, "\n");
47 for (auto line : lines) {
48 ret.insert(line);
49 }
50 LOG(ERROR) << "Read " << ret.size() << " records from " << kSentPath;
51 return ret;
52}
53
54static void WriteSentReports(std::set<std::string> reports) {
55 if (!android::base::WriteStringToFile(android::base::Join(reports, ""), kSentPath)) {
56 PLOG(ERROR) << "Failed to write to " << kSentPath;
57 }
58}
59
60const char kUnknown[] = "UNKNOWN";
61
62static std::string GetOneBootHeader(const std::string& pretty, const std::string& pname) {
63 return pretty + ": " + android::base::GetProperty(pname, kUnknown) + "\n";
64};
65
66static std::string GetBootHeaders() {
67 std::string ret = GetOneBootHeader("Build", "ro.build.fingerprint");
68 ret += GetOneBootHeader("Hardware", "ro.product.board");
69 ret += GetOneBootHeader("Revision", "ro.revision");
70 ret += GetOneBootHeader("Bootloader", "ro.bootloader");
71 ret += GetOneBootHeader("Radio", "gsm.version.baseband");
72
73 std::string version;
74 if (!android::base::ReadFileToString("/proc/version", &version)) version = kUnknown;
75 ret += "Kernel: " + version + "\n\n";
76 return ret;
77}
78
79static bool StoreReport(const std::string& tag, const std::string& report) {
80 std::string boot_headers = GetBootHeaders();
81 android::sp<android::os::DropBoxManager> dropbox(new android::os::DropBoxManager());
82 auto status = dropbox->addText(android::String16(tag.c_str()), boot_headers + report);
83 if (!status.isOk()) {
84 LOG(ERROR) << "Dropbox failed";
85 return false;
86 }
87 return true;
88}
89
90static int ProcessDmesg(std::set<std::string> &sent_reports) {
91 std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("dmesg", "r"), pclose);
92 if (!pipe) {
93 PLOG(ERROR) << "popen() failed!";
94 return 1;
95 }
96 dmesg_parser::DmesgParser dmesg_parser;
97
98 char* buffer = NULL;
99 size_t buffer_size = 0;
100 while (getline(&buffer, &buffer_size, pipe.get()) != -1) {
101 std::string line(buffer);
102 if (line.back() != '\n') line += "\n";
103 dmesg_parser.ProcessLine(line);
104 if (dmesg_parser.ReportReady()) {
105 std::string tag = "SYSTEM_" + dmesg_parser.ReportType() + "_ERROR_REPORT";
106 std::string title = dmesg_parser.ReportTitle();
107 if ((sent_reports.find(title) == sent_reports.end()) &&
108 (sent_reports.size() < kMaxReports)) {
109 if (StoreReport(tag, dmesg_parser.FlushReport())) sent_reports.insert(title);
110 }
111 }
112 }
113 free(buffer);
114 return 0;
115}
116
117int main(int, char*[]) {
118 auto sent_reports = ReadSentReports();
119 int result = ProcessDmesg(sent_reports);
120 WriteSentReports(sent_reports);
121 return result;
122}