Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(845)

Side by Side Diff: components/browser_watcher/postmortem_report_extractor.cc

Issue 2722223002: Separate collection logic from the extraction of the report (Closed)
Patch Set: Address Siggi's comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 #include "components/browser_watcher/postmortem_report_extractor.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/debug/activity_analyzer.h"
13 #include "base/logging.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "components/browser_watcher/stability_data_names.h"
19 #include "components/variations/active_field_trials.h"
20 #include "third_party/crashpad/crashpad/util/misc/uuid.h"
21
22 namespace browser_watcher {
23
24 using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot;
25 using base::debug::ActivityUserData;
26 using base::debug::GlobalActivityAnalyzer;
27 using base::debug::GlobalActivityTracker;
28 using base::debug::ThreadActivityAnalyzer;
29
30 namespace {
31
32 const char kFieldTrialKeyPrefix[] = "FieldTrial.";
33
34 // Collects stability user data from the recorded format to the collected
35 // format.
36 void CollectUserData(
37 const ActivityUserData::Snapshot& recorded_map,
38 google::protobuf::Map<std::string, TypedValue>* collected_map,
39 StabilityReport* report) {
40 DCHECK(collected_map);
41
42 for (const auto& name_and_value : recorded_map) {
43 const std::string& key = name_and_value.first;
44 const ActivityUserData::TypedValue& recorded_value = name_and_value.second;
45 TypedValue collected_value;
46
47 switch (recorded_value.type()) {
48 case ActivityUserData::END_OF_VALUES:
49 NOTREACHED();
50 break;
51 case ActivityUserData::RAW_VALUE: {
52 base::StringPiece raw = recorded_value.Get();
53 collected_value.set_bytes_value(raw.data(), raw.size());
54 break;
55 }
56 case ActivityUserData::RAW_VALUE_REFERENCE: {
57 base::StringPiece recorded_ref = recorded_value.GetReference();
58 TypedValue::Reference* collected_ref =
59 collected_value.mutable_bytes_reference();
60 collected_ref->set_address(
61 reinterpret_cast<uintptr_t>(recorded_ref.data()));
62 collected_ref->set_size(recorded_ref.size());
63 break;
64 }
65 case ActivityUserData::STRING_VALUE: {
66 base::StringPiece value = recorded_value.GetString();
67
68 if (report && base::StartsWith(key, kFieldTrialKeyPrefix,
69 base::CompareCase::SENSITIVE)) {
70 // This entry represents an active Field Trial.
71 std::string trial_name =
72 key.substr(std::strlen(kFieldTrialKeyPrefix));
73 variations::ActiveGroupId group_id =
74 variations::MakeActiveGroupId(trial_name, value.as_string());
75 FieldTrial* field_trial = report->add_field_trials();
76 field_trial->set_name_id(group_id.name);
77 field_trial->set_group_id(group_id.group);
78 continue;
79 }
80
81 collected_value.set_string_value(value.data(), value.size());
82 break;
83 }
84 case ActivityUserData::STRING_VALUE_REFERENCE: {
85 base::StringPiece recorded_ref = recorded_value.GetStringReference();
86 TypedValue::Reference* collected_ref =
87 collected_value.mutable_string_reference();
88 collected_ref->set_address(
89 reinterpret_cast<uintptr_t>(recorded_ref.data()));
90 collected_ref->set_size(recorded_ref.size());
91 break;
92 }
93 case ActivityUserData::CHAR_VALUE: {
94 char char_value = recorded_value.GetChar();
95 collected_value.set_char_value(&char_value, 1);
96 break;
97 }
98 case ActivityUserData::BOOL_VALUE:
99 collected_value.set_bool_value(recorded_value.GetBool());
100 break;
101 case ActivityUserData::SIGNED_VALUE:
102 collected_value.set_signed_value(recorded_value.GetInt());
103 break;
104 case ActivityUserData::UNSIGNED_VALUE:
105 collected_value.set_unsigned_value(recorded_value.GetUint());
106 break;
107 }
108
109 (*collected_map)[key].Swap(&collected_value);
110 }
111 }
112
113 void CollectModuleInformation(
114 const std::vector<GlobalActivityTracker::ModuleInfo>& modules,
115 ProcessState* process_state) {
116 DCHECK(process_state);
117
118 for (const GlobalActivityTracker::ModuleInfo& recorded : modules) {
119 CodeModule* collected = process_state->add_modules();
120 collected->set_base_address(recorded.address);
121 collected->set_size(recorded.size);
122 collected->set_code_file(recorded.file);
123
124 // Compute the code identifier using the required format.
125 std::string code_identifier =
126 base::StringPrintf("%08X%zx", recorded.timestamp, recorded.size);
127 collected->set_code_identifier(code_identifier);
128 collected->set_debug_file(recorded.debug_file);
129
130 // Compute the debug identifier using the required format.
131 const crashpad::UUID* uuid =
132 reinterpret_cast<const crashpad::UUID*>(recorded.identifier);
133 std::string debug_identifier = base::StringPrintf(
134 "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", uuid->data_1,
135 uuid->data_2, uuid->data_3, uuid->data_4[0], uuid->data_4[1],
136 uuid->data_5[0], uuid->data_5[1], uuid->data_5[2], uuid->data_5[3],
137 uuid->data_5[4], uuid->data_5[5], recorded.age);
138 collected->set_debug_identifier(debug_identifier);
139 collected->set_is_unloaded(!recorded.is_loaded);
140 }
141 }
142
143 } // namespace
144
145 // DO NOT SUBMIT: review diff stepping stone. Move the function after 1st round.
146 void CollectThread(
147 const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
148 ThreadState* thread_state);
149
150 CollectionStatus Extract(const base::FilePath& stability_file,
151 StabilityReport* report) {
152 DCHECK(report);
153
154 // Create a global analyzer.
155 std::unique_ptr<GlobalActivityAnalyzer> global_analyzer =
156 GlobalActivityAnalyzer::CreateWithFile(stability_file);
157 if (!global_analyzer)
158 return ANALYZER_CREATION_FAILED;
159
160 // Early exit if there is no data.
161 std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
162 ActivityUserData::Snapshot global_data_snapshot =
163 global_analyzer->GetGlobalUserDataSnapshot();
164 ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer();
165 if (log_messages.empty() && global_data_snapshot.empty() &&
166 !thread_analyzer) {
167 return DEBUG_FILE_NO_DATA;
168 }
169
170 // Collect log messages.
171 for (const std::string& message : log_messages) {
172 report->add_log_messages(message);
173 }
174
175 // Collect global user data.
176 google::protobuf::Map<std::string, TypedValue>& global_data =
177 *(report->mutable_global_data());
178 CollectUserData(global_data_snapshot, &global_data, report);
179
180 // Collect thread activity data.
181 // Note: a single process is instrumented.
182 ProcessState* process_state = report->add_process_states();
183 for (; thread_analyzer != nullptr;
184 thread_analyzer = global_analyzer->GetNextAnalyzer()) {
185 // Only valid analyzers are expected per contract of GetFirstAnalyzer /
186 // GetNextAnalyzer.
187 DCHECK(thread_analyzer->IsValid());
188
189 if (!process_state->has_process_id()) {
190 process_state->set_process_id(
191 thread_analyzer->activity_snapshot().process_id);
192 }
193 DCHECK_EQ(thread_analyzer->activity_snapshot().process_id,
194 process_state->process_id());
195
196 ThreadState* thread_state = process_state->add_threads();
197 CollectThread(thread_analyzer->activity_snapshot(), thread_state);
198 }
199
200 // Collect module information.
201 CollectModuleInformation(global_analyzer->GetModules(), process_state);
202
203 return SUCCESS;
204 }
205
206 // DO NOT SUBMIT: review diff stepping stone. Move the function after 1st round.
207 void CollectThread(
208 const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
209 ThreadState* thread_state) {
210 DCHECK(thread_state);
211
212 thread_state->set_thread_name(snapshot.thread_name);
213 thread_state->set_thread_id(snapshot.thread_id);
214 thread_state->set_activity_count(snapshot.activity_stack_depth);
215
216 for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
217 const base::debug::Activity& recorded = snapshot.activity_stack[i];
218 Activity* collected = thread_state->add_activities();
219
220 // Collect activity
221 switch (recorded.activity_type) {
222 case base::debug::Activity::ACT_TASK_RUN:
223 collected->set_type(Activity::ACT_TASK_RUN);
224 collected->set_origin_address(recorded.origin_address);
225 collected->set_task_sequence_id(recorded.data.task.sequence_id);
226 break;
227 case base::debug::Activity::ACT_LOCK_ACQUIRE:
228 collected->set_type(Activity::ACT_LOCK_ACQUIRE);
229 collected->set_lock_address(recorded.data.lock.lock_address);
230 break;
231 case base::debug::Activity::ACT_EVENT_WAIT:
232 collected->set_type(Activity::ACT_EVENT_WAIT);
233 collected->set_event_address(recorded.data.event.event_address);
234 break;
235 case base::debug::Activity::ACT_THREAD_JOIN:
236 collected->set_type(Activity::ACT_THREAD_JOIN);
237 collected->set_thread_id(recorded.data.thread.thread_id);
238 break;
239 case base::debug::Activity::ACT_PROCESS_WAIT:
240 collected->set_type(Activity::ACT_PROCESS_WAIT);
241 collected->set_process_id(recorded.data.process.process_id);
242 break;
243 default:
244 break;
245 }
246
247 // Collect user data
248 if (i < snapshot.user_data_stack.size()) {
249 CollectUserData(snapshot.user_data_stack[i],
250 collected->mutable_user_data(), nullptr);
251 }
252 }
253 }
254
255 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698