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 // Note: aside from using windows headers to obtain the definitions of minidump | |
6 // structures, nothing here is windows specific. This seems like the best | |
7 // approach given this code is for temporary experimentation on Windows. | |
8 // Longer term, CrashPad will take over the minidump writing in this case as | |
9 // well. | |
10 | |
11 #include "components/browser_watcher/postmortem_minidump_writer.h" | |
12 | |
13 #include <windows.h> // NOLINT | |
14 #include <dbghelp.h> | |
15 | |
16 #include <string> | |
17 | |
18 #include "base/files/file_util.h" | |
19 #include "base/numerics/safe_math.h" | |
20 #include "third_party/crashpad/crashpad/minidump/minidump_extensions.h" | |
21 | |
22 namespace browser_watcher { | |
23 | |
24 // The stream type assigned to the minidump stream that holds the serialized | |
25 // stability report. | |
26 // Note: the value was obtained by adding 1 to the stream type used for holding | |
27 // the SyzyAsan proto. | |
28 // TODO(manzagop): centralize the stream type definitions to avoid issues. | |
29 const uint32_t kStabilityReportStreamType = 0x4B6B0002; | |
30 | |
31 PostmortemMinidumpWriter::PostmortemMinidumpWriter( | |
32 base::PlatformFile minidump_file) | |
33 : next_available_byte_(0U), cursor_(0U), minidump_file_(minidump_file) { | |
34 CHECK(minidump_file); | |
Sigurður Ásgeirsson
2016/09/09 14:21:00
compare to base::kInvalidPlatformFile, or better u
manzagop (departed)
2016/09/09 20:34:40
Done.
| |
35 } | |
36 | |
37 bool PostmortemMinidumpWriter::WriteDump(const StabilityReport& report, | |
38 const crashpad::UUID& client_id, | |
39 const crashpad::UUID& report_id, | |
40 const std::string& product_name, | |
41 const std::string& version_number) { | |
42 DCHECK_NE(base::kInvalidPlatformFile, minidump_file_); | |
Sigurður Ásgeirsson
2016/09/09 14:21:00
Check that the file pos is at head - maybe you als
manzagop (departed)
2016/09/09 20:34:40
Done.
| |
43 DCHECK_EQ(0U, next_available_byte_); | |
44 DCHECK(directory_.empty()); | |
45 | |
46 // Allocate space for the header and seek the cursor. | |
47 Position pos = 0U; | |
48 if (!Allocate(sizeof(MINIDUMP_HEADER), &pos)) | |
49 return false; | |
50 if (!SeekCursor(sizeof(MINIDUMP_HEADER))) | |
51 return false; | |
52 DCHECK_EQ(kHeaderPos, pos); | |
53 | |
54 // Write the proto to the file. | |
55 std::string serialized_report; | |
56 if (!report.SerializeToString(&serialized_report)) | |
57 return false; | |
58 Position report_pos = 0U; | |
59 if (!AppendBytes(serialized_report, &report_pos)) | |
60 return false; | |
61 | |
62 // The directory entry for the stability report's stream. | |
63 RegisterDirectoryEntry(kStabilityReportStreamType, report_pos, | |
64 serialized_report.length()); | |
65 | |
66 // Write mandatory crash keys. These will be read by crashpad and used as | |
67 // http request parameters for the upload. Keys and values should match | |
68 // server side configuration. | |
69 // TODO(manzagop): use product and version from the stability report. The | |
70 // current executable's values are an (imperfect) proxy. | |
71 std::map<std::string, std::string> crash_keys = { | |
72 {"product", product_name + "_Postmortem"}, {"version", version_number}}; | |
73 if (!AppendCrashpadInfo(client_id, report_id, crash_keys)) | |
74 return false; | |
75 | |
76 // Write the directory. | |
77 Position directory_pos = 0U; | |
78 if (!AppendVec(directory_, &directory_pos)) | |
79 return false; | |
80 | |
81 // Write the header. | |
82 MINIDUMP_HEADER header; | |
83 header.Signature = MINIDUMP_SIGNATURE; | |
84 header.Version = MINIDUMP_VERSION; | |
85 header.NumberOfStreams = directory_.size(); | |
86 header.StreamDirectoryRva = directory_pos; | |
87 if (!SeekCursor(0U)) | |
88 return false; | |
89 return Write(kHeaderPos, header); | |
90 } | |
91 | |
92 bool PostmortemMinidumpWriter::AppendCrashpadInfo( | |
93 const crashpad::UUID& client_id, | |
94 const crashpad::UUID& report_id, | |
95 const std::map<std::string, std::string>& crash_keys) { | |
96 // Write the crash keys as the contents of a crashpad dictionary. | |
97 std::vector<crashpad::MinidumpSimpleStringDictionaryEntry> entries; | |
98 for (const auto& crash_key : crash_keys) { | |
99 if (!AppendCrashpadDictionaryEntry(crash_key.first, crash_key.second, | |
100 &entries)) { | |
101 return false; | |
102 } | |
103 } | |
104 | |
105 // Write the dictionary's index. | |
106 Position dict_pos = 0U; | |
107 uint32_t entry_count = entries.size(); | |
108 if (entry_count > 0) { | |
109 if (!Append(entry_count, &dict_pos)) | |
110 return false; | |
111 Position unused_pos = 0U; | |
112 if (!AppendVec(entries, &unused_pos)) | |
113 return false; | |
114 } | |
115 | |
116 MINIDUMP_LOCATION_DESCRIPTOR simple_annotations; | |
Sigurður Ásgeirsson
2016/09/09 14:21:00
initialize with {}?
manzagop (departed)
2016/09/09 20:34:40
Done.
| |
117 simple_annotations.DataSize = 0U; | |
118 if (entry_count > 0) | |
119 simple_annotations.DataSize = next_available_byte_ - dict_pos; | |
120 // Note: an RVA of 0 indicates the absence of a dictionary. | |
121 simple_annotations.Rva = dict_pos; | |
122 | |
123 // Write the crashpad info. | |
124 crashpad::MinidumpCrashpadInfo crashpad_info; | |
125 crashpad_info.version = crashpad::MinidumpCrashpadInfo::kVersion; | |
126 crashpad_info.report_id = report_id; | |
127 crashpad_info.client_id = client_id; | |
128 crashpad_info.simple_annotations = simple_annotations; | |
129 // Note: module_list is left at 0, which means none. | |
130 | |
131 Position crashpad_pos = 0U; | |
132 if (!Append(crashpad_info, &crashpad_pos)) | |
133 return false; | |
134 | |
135 // Append a directory entry for the crashpad info stream. | |
136 RegisterDirectoryEntry(crashpad::kMinidumpStreamTypeCrashpadInfo, | |
137 crashpad_pos, sizeof(crashpad::MinidumpCrashpadInfo)); | |
138 | |
139 return true; | |
140 } | |
141 | |
142 bool PostmortemMinidumpWriter::AppendCrashpadDictionaryEntry( | |
143 const std::string& key, | |
144 const std::string& value, | |
145 std::vector<crashpad::MinidumpSimpleStringDictionaryEntry>* entries) { | |
146 DCHECK_NE(nullptr, entries); | |
147 | |
148 Position key_pos = 0U; | |
149 if (!AppendUtf8String(key, &key_pos)) | |
150 return false; | |
151 Position value_pos = 0U; | |
152 if (!AppendUtf8String(value, &value_pos)) | |
153 return false; | |
154 | |
155 crashpad::MinidumpSimpleStringDictionaryEntry entry = {0}; | |
156 entry.key = key_pos; | |
157 entry.value = value_pos; | |
158 entries->push_back(entry); | |
159 | |
160 return true; | |
161 } | |
162 | |
163 bool PostmortemMinidumpWriter::Allocate(size_t size_bytes, Position* pos) { | |
164 DCHECK(pos); | |
165 *pos = next_available_byte_; | |
166 | |
167 base::CheckedNumeric<Position> next = next_available_byte_; | |
168 next += size_bytes; | |
169 if (!next.IsValid()) | |
170 return false; | |
171 | |
172 next_available_byte_ += size_bytes; | |
173 return true; | |
174 } | |
175 | |
176 bool PostmortemMinidumpWriter::SeekCursor(Position destination) { | |
177 // Validate the write does not extend past the allocated space. | |
178 if (destination > next_available_byte_) | |
179 return false; | |
180 | |
181 LARGE_INTEGER offset = {0}; | |
182 offset.LowPart = destination; | |
183 if (!::SetFilePointerEx(minidump_file_, offset, nullptr, FILE_BEGIN)) | |
184 return false; | |
185 | |
186 cursor_ = destination; | |
187 return true; | |
188 } | |
189 | |
190 bool PostmortemMinidumpWriter::WriteBytes(Position pos, | |
191 size_t size_bytes, | |
192 const void* data) { | |
193 DCHECK(data); | |
194 DCHECK_NE(base::kInvalidPlatformFile, minidump_file_); | |
195 DCHECK_EQ(cursor_, pos); | |
196 | |
197 // Validate the write does not extend past the next available byte. | |
198 base::CheckedNumeric<Position> pos_end = pos; | |
199 pos_end += size_bytes; | |
200 if (!pos_end.IsValid() || pos_end.ValueOrDie() > next_available_byte_) | |
201 return false; | |
202 | |
203 // Write. | |
204 DWORD written_bytes = 0U; | |
205 BOOL success = | |
206 ::WriteFile(minidump_file_, data, size_bytes, &written_bytes, nullptr); | |
207 if (!success || size_bytes != written_bytes) | |
208 return false; | |
209 | |
210 // Advance cursor. | |
211 cursor_ += size_bytes; | |
212 return true; | |
213 } | |
214 | |
215 bool PostmortemMinidumpWriter::AppendUtf8String(base::StringPiece data, | |
216 Position* pos) { | |
217 DCHECK(pos); | |
218 uint32_t string_size = data.size(); | |
219 if (!Append(string_size, pos)) | |
220 return false; | |
221 | |
222 Position unused_pos = 0U; | |
223 return AppendBytes(data, &unused_pos); | |
224 } | |
225 | |
226 bool PostmortemMinidumpWriter::AppendBytes(base::StringPiece data, | |
227 Position* pos) { | |
228 DCHECK(pos); | |
229 if (!Allocate(data.length(), pos)) | |
230 return false; | |
231 return WriteBytes(*pos, data.length(), data.data()); | |
232 } | |
233 | |
234 void PostmortemMinidumpWriter::RegisterDirectoryEntry(uint32_t stream_type, | |
235 Position pos, | |
236 uint32_t size) { | |
237 MINIDUMP_DIRECTORY entry = {0}; | |
238 entry.StreamType = stream_type; | |
239 entry.Location.Rva = pos; | |
240 entry.Location.DataSize = size; | |
241 directory_.push_back(entry); | |
242 } | |
243 | |
244 } // namespace browser_watcher | |
OLD | NEW |