OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // A utility for printing the contents of a postmortem stability minidump. | |
6 | |
7 #include <windows.h> // NOLINT | |
8 #include <dbghelp.h> | |
9 | |
10 #include "base/command_line.h" | |
11 #include "base/files/file_path.h" | |
12 #include "base/files/file_util.h" | |
13 #include "base/files/scoped_file.h" | |
14 #include "base/logging.h" | |
15 #include "base/strings/stringprintf.h" | |
16 #include "components/browser_watcher/stability_report.pb.h" | |
17 | |
18 namespace { | |
19 | |
20 const char kUsage[] = | |
21 "Usage: %ls --minidump=<minidump file>\n" | |
22 "\n" | |
23 " Dumps the contents of a postmortem minidump in a human readable way.\n"; | |
24 | |
25 bool ParseCommandLine(const base::CommandLine* cmd, | |
26 base::FilePath* minidump_path) { | |
27 *minidump_path = cmd->GetSwitchValuePath("minidump"); | |
28 if (minidump_path->empty()) { | |
29 LOG(ERROR) << "Missing minidump file.\n"; | |
30 LOG(ERROR) << base::StringPrintf(kUsage, cmd->GetProgram().value().c_str()); | |
31 return false; | |
32 } | |
33 return true; | |
34 } | |
35 | |
36 void Indent(FILE* out, int indent_level) { | |
37 DCHECK(out); | |
38 for (int i = 0; i < indent_level; ++i) | |
39 fprintf(out, " "); | |
40 } | |
41 | |
42 void PrintUserData( | |
43 FILE* out, | |
44 int indent_level, | |
45 const google::protobuf::Map<std::string, browser_watcher::TypedValue>& | |
46 user_data) { | |
47 DCHECK(out); | |
48 Indent(out, indent_level); | |
49 fprintf(out, "User data (%zu)\n", user_data.size()); | |
50 for (const auto& kv : user_data) { | |
51 Indent(out, indent_level + 1); | |
52 fprintf(out, "%s : ", kv.first.c_str()); | |
53 const browser_watcher::TypedValue& value = kv.second; | |
54 switch (kv.second.value_case()) { | |
55 case browser_watcher::TypedValue::kBytesValue: { | |
56 const std::string& bytes_value = value.bytes_value(); | |
57 for (size_t i = 0; i < bytes_value.size(); ++i) | |
58 fprintf(out, "%02X ", bytes_value.at(i)); | |
59 fprintf(out, "\n"); | |
60 break; | |
61 } | |
62 case browser_watcher::TypedValue::kBytesReference: | |
63 fprintf(out, "bytes reference (address: %llX, size: %llX)\n", | |
64 value.bytes_reference().address(), | |
65 value.bytes_reference().size()); | |
66 break; | |
67 case browser_watcher::TypedValue::kStringValue: | |
68 fprintf(out, "\"%s\"\n", value.string_value().c_str()); | |
69 break; | |
70 case browser_watcher::TypedValue::kStringReference: | |
71 fprintf(out, "string reference (address: %llX, size: %llX)\n", | |
72 value.string_reference().address(), | |
73 value.string_reference().size()); | |
74 break; | |
75 case browser_watcher::TypedValue::kCharValue: | |
76 fprintf(out, "'%s'\n", value.char_value().c_str()); | |
77 break; | |
78 case browser_watcher::TypedValue::kBoolValue: | |
79 fprintf(out, "%s\n", value.bool_value() ? "true" : "false"); | |
80 break; | |
81 case browser_watcher::TypedValue::kSignedValue: | |
82 fprintf(out, "%lld\n", value.signed_value()); | |
83 break; | |
84 case browser_watcher::TypedValue::kUnsignedValue: | |
85 fprintf(out, "%llu\n", value.unsigned_value()); | |
86 break; | |
87 case browser_watcher::TypedValue::VALUE_NOT_SET: | |
88 fprintf(out, "<not set>\n"); | |
89 break; | |
90 } | |
91 } | |
92 } | |
93 | |
94 void PrintActivity(FILE* out, | |
95 int indent_level, | |
96 const browser_watcher::Activity& activity) { | |
97 DCHECK(out); | |
98 Indent(out, indent_level); | |
99 fprintf(out, "Activity\n"); | |
100 Indent(out, indent_level + 1); | |
101 fprintf(out, "type: %d\n", activity.type()); | |
102 Indent(out, indent_level + 1); | |
103 fprintf(out, "time: %lld\n", activity.time()); | |
104 Indent(out, indent_level + 1); | |
105 fprintf(out, "address: %llX\n", activity.address()); | |
106 switch (activity.type()) { | |
107 case browser_watcher::Activity::UNKNOWN: | |
108 break; | |
109 case browser_watcher::Activity::ACT_TASK_RUN: | |
110 Indent(out, indent_level + 1); | |
111 fprintf(out, "origin_address: %llX\n", activity.origin_address()); | |
112 fprintf(out, "task_sequence_id: %lld\n", activity.task_sequence_id()); | |
113 break; | |
114 case browser_watcher::Activity::ACT_LOCK_ACQUIRE: | |
115 Indent(out, indent_level + 1); | |
116 fprintf(out, "lock_address: %llX\n", activity.lock_address()); | |
117 break; | |
118 case browser_watcher::Activity::ACT_EVENT_WAIT: | |
119 Indent(out, indent_level + 1); | |
120 fprintf(out, "event_address: %llX\n", activity.event_address()); | |
121 break; | |
122 case browser_watcher::Activity::ACT_THREAD_JOIN: | |
123 Indent(out, indent_level + 1); | |
124 fprintf(out, "thread_id: %lld\n", activity.thread_id()); | |
125 break; | |
126 case browser_watcher::Activity::ACT_PROCESS_WAIT: | |
127 Indent(out, indent_level + 1); | |
128 fprintf(out, "process_id: %lld\n", activity.process_id()); | |
129 break; | |
130 case browser_watcher::Activity::ACT_GENERIC: | |
131 Indent(out, indent_level + 1); | |
132 fprintf(out, "id: %u, data: %d\n", activity.generic_id(), | |
133 activity.generic_data()); | |
134 break; | |
135 } | |
136 | |
137 PrintUserData(out, indent_level + 1, activity.user_data()); | |
138 } | |
139 | |
140 void PrintProcessState(FILE* out, | |
141 const browser_watcher::ProcessState& process) { | |
142 fprintf(out, "Process %lld (%d threads)\n", process.process_id(), | |
143 process.threads_size()); | |
144 for (const browser_watcher::ThreadState& thread : process.threads()) { | |
145 fprintf(out, "Thread %lld (%s) : %d activities\n", thread.thread_id(), | |
146 thread.thread_name().c_str(), thread.activity_count()); | |
147 for (const browser_watcher::Activity& activity : thread.activities()) | |
148 PrintActivity(out, 1, activity); | |
149 } | |
150 } | |
151 | |
152 // TODO(manzagop): flesh out as StabilityReport gets fleshed out. | |
153 void PrintReport(FILE* out, const browser_watcher::StabilityReport& report) { | |
154 PrintUserData(out, 0, report.global_data()); | |
155 for (int i = 0; i < report.process_states_size(); ++i) { | |
156 const browser_watcher::ProcessState process = report.process_states(i); | |
157 PrintProcessState(out, process); | |
158 } | |
159 } | |
160 | |
161 int Main(int argc, char** argv) { | |
162 base::CommandLine::Init(argc, argv); | |
163 | |
164 // Get the dump. | |
165 base::FilePath minidump_path; | |
166 if (!ParseCommandLine(base::CommandLine::ForCurrentProcess(), &minidump_path)) | |
167 return 1; | |
168 | |
169 // Read the minidump to extract the proto. | |
170 base::ScopedFILE minidump_file; | |
171 minidump_file.reset(base::OpenFile(minidump_path, "rb")); | |
172 CHECK(minidump_file.get()); | |
173 | |
174 // Read the header. | |
175 // TODO(manzagop): leverage Crashpad to do this. | |
176 MINIDUMP_HEADER header = {}; | |
177 CHECK_EQ(1U, fread(&header, sizeof(header), 1U, minidump_file.get())); | |
178 CHECK_EQ(static_cast<ULONG32>(MINIDUMP_SIGNATURE), header.Signature); | |
179 CHECK_EQ(2U, header.NumberOfStreams); | |
180 RVA directory_rva = header.StreamDirectoryRva; | |
181 | |
182 // Read the directory entry for the stability report's stream. | |
183 // Note: this hardcodes an expectation that the stability report is the first | |
184 // encountered stream. This is acceptable for a debug tool. | |
185 MINIDUMP_DIRECTORY directory = {}; | |
186 CHECK_EQ(0, fseek(minidump_file.get(), directory_rva, SEEK_SET)); | |
187 CHECK_EQ(1U, fread(&directory, sizeof(directory), 1U, minidump_file.get())); | |
188 CHECK_EQ(static_cast<ULONG32>(0x4B6B0002), directory.StreamType); | |
189 RVA report_rva = directory.Location.Rva; | |
190 ULONG32 report_size_bytes = directory.Location.DataSize; | |
191 | |
192 // Read the serialized stability report. | |
193 std::string serialized_report; | |
194 serialized_report.resize(report_size_bytes); | |
195 CHECK_EQ(0, fseek(minidump_file.get(), report_rva, SEEK_SET)); | |
196 CHECK_EQ(report_size_bytes, fread(&serialized_report.at(0), 1, | |
197 report_size_bytes, minidump_file.get())); | |
198 | |
199 browser_watcher::StabilityReport report; | |
200 CHECK(report.ParseFromString(serialized_report)); | |
201 | |
202 // Note: we can't use the usual protocol buffer human readable API due to | |
203 // the use of optimize_for = LITE_RUNTIME. | |
204 PrintReport(stdout, report); | |
205 | |
206 return 0; | |
207 } | |
208 | |
209 } // namespace | |
210 | |
211 int main(int argc, char** argv) { | |
212 return Main(argc, argv); | |
213 } | |
OLD | NEW |