Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/browser_watcher/postmortem_report_collector.h" | 5 #include "components/browser_watcher/postmortem_report_collector.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/debug/activity_analyzer.h" | 9 #include "base/debug/activity_analyzer.h" |
| 10 #include "base/files/file_enumerator.h" | 10 #include "base/files/file_enumerator.h" |
| 11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
| 14 #include "base/path_service.h" | 14 #include "base/path_service.h" |
| 15 #include "base/strings/string_piece.h" | 15 #include "base/strings/string_piece.h" |
| 16 #include "base/strings/string_util.h" | |
| 16 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 17 #include "components/browser_watcher/postmortem_minidump_writer.h" | 18 #include "components/browser_watcher/postmortem_minidump_writer.h" |
| 19 #include "components/variations/active_field_trials.h" | |
| 18 #include "third_party/crashpad/crashpad/client/settings.h" | 20 #include "third_party/crashpad/crashpad/client/settings.h" |
| 19 #include "third_party/crashpad/crashpad/util/misc/uuid.h" | 21 #include "third_party/crashpad/crashpad/util/misc/uuid.h" |
| 20 | 22 |
| 21 using base::FilePath; | 23 using base::FilePath; |
| 22 | 24 |
| 23 namespace browser_watcher { | 25 namespace browser_watcher { |
| 24 | 26 |
| 25 using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot; | 27 using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot; |
| 26 using base::debug::ActivityUserData; | 28 using base::debug::ActivityUserData; |
| 27 using base::debug::GlobalActivityAnalyzer; | 29 using base::debug::GlobalActivityAnalyzer; |
| 28 using base::debug::GlobalActivityTracker; | 30 using base::debug::GlobalActivityTracker; |
| 29 using base::debug::ThreadActivityAnalyzer; | 31 using base::debug::ThreadActivityAnalyzer; |
| 30 using crashpad::CrashReportDatabase; | 32 using crashpad::CrashReportDatabase; |
| 31 | 33 |
| 32 namespace { | 34 namespace { |
| 33 | 35 |
| 36 const char kFieldTrialKeyPrefix[] = "FieldTrial."; | |
| 37 | |
| 34 // Collects stability user data from the recorded format to the collected | 38 // Collects stability user data from the recorded format to the collected |
| 35 // format. | 39 // format. |
| 36 void CollectUserData( | 40 void CollectUserData( |
| 37 const ActivityUserData::Snapshot& recorded_map, | 41 const ActivityUserData::Snapshot& recorded_map, |
| 38 google::protobuf::Map<std::string, TypedValue>* collected_map) { | 42 google::protobuf::Map<std::string, TypedValue>* collected_map, |
| 43 StabilityReport* report) { | |
| 39 DCHECK(collected_map); | 44 DCHECK(collected_map); |
| 40 | 45 |
| 41 for (const auto& name_and_value : recorded_map) { | 46 for (const auto& name_and_value : recorded_map) { |
| 47 const std::string& key = name_and_value.first; | |
| 42 const ActivityUserData::TypedValue& recorded_value = name_and_value.second; | 48 const ActivityUserData::TypedValue& recorded_value = name_and_value.second; |
| 43 TypedValue collected_value; | 49 TypedValue collected_value; |
| 44 | 50 |
| 45 switch (recorded_value.type()) { | 51 switch (recorded_value.type()) { |
| 46 case ActivityUserData::END_OF_VALUES: | 52 case ActivityUserData::END_OF_VALUES: |
| 47 NOTREACHED(); | 53 NOTREACHED(); |
| 48 break; | 54 break; |
| 49 case ActivityUserData::RAW_VALUE: | 55 case ActivityUserData::RAW_VALUE: |
| 50 collected_value.set_bytes_value(recorded_value.Get().as_string()); | 56 collected_value.set_bytes_value(recorded_value.Get().as_string()); |
| 51 break; | 57 break; |
| 52 case ActivityUserData::RAW_VALUE_REFERENCE: { | 58 case ActivityUserData::RAW_VALUE_REFERENCE: { |
| 53 base::StringPiece recorded_ref = recorded_value.GetReference(); | 59 base::StringPiece recorded_ref = recorded_value.GetReference(); |
| 54 TypedValue::Reference* collected_ref = | 60 TypedValue::Reference* collected_ref = |
| 55 collected_value.mutable_bytes_reference(); | 61 collected_value.mutable_bytes_reference(); |
| 56 collected_ref->set_address( | 62 collected_ref->set_address( |
| 57 reinterpret_cast<uintptr_t>(recorded_ref.data())); | 63 reinterpret_cast<uintptr_t>(recorded_ref.data())); |
| 58 collected_ref->set_size(recorded_ref.size()); | 64 collected_ref->set_size(recorded_ref.size()); |
| 59 break; | 65 break; |
| 60 } | 66 } |
| 61 case ActivityUserData::STRING_VALUE: | 67 case ActivityUserData::STRING_VALUE: { |
| 62 collected_value.set_string_value( | 68 std::string value = recorded_value.GetString().as_string(); |
| 63 recorded_value.GetString().as_string()); | 69 |
| 70 if (report && base::StartsWith(key, kFieldTrialKeyPrefix, | |
|
bcwhite
2017/02/14 15:42:14
To be clear, you want field-trial strings to go in
manzagop (departed)
2017/02/14 19:21:15
Yeah, this is clunky. I'm using this function to c
bcwhite
2017/02/14 21:05:16
On the second pass, do you want the field trials t
manzagop (departed)
2017/02/14 21:27:57
Not sure I follow: what's the second pass? Do you
bcwhite
2017/02/15 16:24:19
You said that "in the second I don't have one" so
manzagop (departed)
2017/02/15 18:35:07
Acknowledged.
| |
| 71 base::CompareCase::SENSITIVE)) { | |
| 72 // This entry represents an active Finch experiment. | |
| 73 std::string trial_name = | |
| 74 key.substr(std::strlen(kFieldTrialKeyPrefix)); | |
| 75 variations::ActiveGroupId group_id = | |
| 76 variations::MakeActiveGroupId(trial_name, value); | |
| 77 FieldTrial* field_trial = report->add_field_trials(); | |
| 78 field_trial->set_name_id(group_id.name); | |
| 79 field_trial->set_group_id(group_id.group); | |
| 80 continue; | |
| 81 } | |
| 82 | |
| 83 collected_value.set_string_value(value); | |
|
bcwhite
2017/02/14 15:42:14
I believe set_string_value will accept (char*, len
manzagop (departed)
2017/02/14 19:21:15
Nice! Ah, but MakeActiveGroupId wants a string. St
| |
| 64 break; | 84 break; |
| 85 } | |
| 65 case ActivityUserData::STRING_VALUE_REFERENCE: { | 86 case ActivityUserData::STRING_VALUE_REFERENCE: { |
| 66 base::StringPiece recorded_ref = recorded_value.GetStringReference(); | 87 base::StringPiece recorded_ref = recorded_value.GetStringReference(); |
| 67 TypedValue::Reference* collected_ref = | 88 TypedValue::Reference* collected_ref = |
| 68 collected_value.mutable_string_reference(); | 89 collected_value.mutable_string_reference(); |
| 69 collected_ref->set_address( | 90 collected_ref->set_address( |
| 70 reinterpret_cast<uintptr_t>(recorded_ref.data())); | 91 reinterpret_cast<uintptr_t>(recorded_ref.data())); |
| 71 collected_ref->set_size(recorded_ref.size()); | 92 collected_ref->set_size(recorded_ref.size()); |
| 72 break; | 93 break; |
| 73 } | 94 } |
| 74 case ActivityUserData::CHAR_VALUE: | 95 case ActivityUserData::CHAR_VALUE: |
| 75 collected_value.set_char_value( | 96 collected_value.set_char_value( |
| 76 std::string(1, recorded_value.GetChar())); | 97 std::string(1, recorded_value.GetChar())); |
| 77 break; | 98 break; |
| 78 case ActivityUserData::BOOL_VALUE: | 99 case ActivityUserData::BOOL_VALUE: |
| 79 collected_value.set_bool_value(recorded_value.GetBool()); | 100 collected_value.set_bool_value(recorded_value.GetBool()); |
| 80 break; | 101 break; |
| 81 case ActivityUserData::SIGNED_VALUE: | 102 case ActivityUserData::SIGNED_VALUE: |
| 82 collected_value.set_signed_value(recorded_value.GetInt()); | 103 collected_value.set_signed_value(recorded_value.GetInt()); |
| 83 break; | 104 break; |
| 84 case ActivityUserData::UNSIGNED_VALUE: | 105 case ActivityUserData::UNSIGNED_VALUE: |
| 85 collected_value.set_unsigned_value(recorded_value.GetUint()); | 106 collected_value.set_unsigned_value(recorded_value.GetUint()); |
| 86 break; | 107 break; |
| 87 } | 108 } |
| 88 | 109 |
| 89 (*collected_map)[name_and_value.first].Swap(&collected_value); | 110 (*collected_map)[key].Swap(&collected_value); |
| 90 } | 111 } |
| 91 } | 112 } |
| 92 | 113 |
| 93 void CollectModuleInformation( | 114 void CollectModuleInformation( |
| 94 const std::vector<GlobalActivityTracker::ModuleInfo>& modules, | 115 const std::vector<GlobalActivityTracker::ModuleInfo>& modules, |
| 95 ProcessState* process_state) { | 116 ProcessState* process_state) { |
| 96 DCHECK(process_state); | 117 DCHECK(process_state); |
| 97 | 118 |
| 98 char code_identifier[17]; | 119 char code_identifier[17]; |
| 99 char debug_identifier[41]; | 120 char debug_identifier[41]; |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 | 299 |
| 279 // Create the report, then flesh it out. | 300 // Create the report, then flesh it out. |
| 280 report->reset(new StabilityReport()); | 301 report->reset(new StabilityReport()); |
| 281 | 302 |
| 282 // Collect log messages. | 303 // Collect log messages. |
| 283 for (const std::string& message : log_messages) { | 304 for (const std::string& message : log_messages) { |
| 284 (*report)->add_log_messages(message); | 305 (*report)->add_log_messages(message); |
| 285 } | 306 } |
| 286 | 307 |
| 287 // Collect global user data. | 308 // Collect global user data. |
| 288 CollectUserData(global_data_snapshot, (*report)->mutable_global_data()); | 309 CollectUserData(global_data_snapshot, (*report)->mutable_global_data(), |
| 310 report->get()); | |
| 289 | 311 |
| 290 // Collect thread activity data. | 312 // Collect thread activity data. |
| 291 // Note: a single process is instrumented. | 313 // Note: a single process is instrumented. |
| 292 ProcessState* process_state = (*report)->add_process_states(); | 314 ProcessState* process_state = (*report)->add_process_states(); |
| 293 for (; thread_analyzer != nullptr; | 315 for (; thread_analyzer != nullptr; |
| 294 thread_analyzer = global_analyzer->GetNextAnalyzer()) { | 316 thread_analyzer = global_analyzer->GetNextAnalyzer()) { |
| 295 // Only valid analyzers are expected per contract of GetFirstAnalyzer / | 317 // Only valid analyzers are expected per contract of GetFirstAnalyzer / |
| 296 // GetNextAnalyzer. | 318 // GetNextAnalyzer. |
| 297 DCHECK(thread_analyzer->IsValid()); | 319 DCHECK(thread_analyzer->IsValid()); |
| 298 | 320 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 349 collected->set_type(Activity::ACT_PROCESS_WAIT); | 371 collected->set_type(Activity::ACT_PROCESS_WAIT); |
| 350 collected->set_process_id(recorded.data.process.process_id); | 372 collected->set_process_id(recorded.data.process.process_id); |
| 351 break; | 373 break; |
| 352 default: | 374 default: |
| 353 break; | 375 break; |
| 354 } | 376 } |
| 355 | 377 |
| 356 // Collect user data | 378 // Collect user data |
| 357 if (i < snapshot.user_data_stack.size()) { | 379 if (i < snapshot.user_data_stack.size()) { |
| 358 CollectUserData(snapshot.user_data_stack[i], | 380 CollectUserData(snapshot.user_data_stack[i], |
| 359 collected->mutable_user_data()); | 381 collected->mutable_user_data(), nullptr); |
| 360 } | 382 } |
| 361 } | 383 } |
| 362 } | 384 } |
| 363 | 385 |
| 364 bool PostmortemReportCollector::WriteReportToMinidump( | 386 bool PostmortemReportCollector::WriteReportToMinidump( |
| 365 const StabilityReport& report, | 387 const StabilityReport& report, |
| 366 const crashpad::UUID& client_id, | 388 const crashpad::UUID& client_id, |
| 367 const crashpad::UUID& report_id, | 389 const crashpad::UUID& report_id, |
| 368 base::PlatformFile minidump_file) { | 390 base::PlatformFile minidump_file) { |
| 369 MinidumpInfo minidump_info; | 391 MinidumpInfo minidump_info; |
| 370 minidump_info.client_id = client_id; | 392 minidump_info.client_id = client_id; |
| 371 minidump_info.report_id = report_id; | 393 minidump_info.report_id = report_id; |
| 372 // TODO(manzagop): replace this information, i.e. the reporter's attributes, | 394 // TODO(manzagop): replace this information, i.e. the reporter's attributes, |
| 373 // by that of the reportee. Doing so requires adding this information to the | 395 // by that of the reportee. Doing so requires adding this information to the |
| 374 // stability report. In the meantime, there is a tolerable information | 396 // stability report. In the meantime, there is a tolerable information |
| 375 // mismatch after upgrades. | 397 // mismatch after upgrades. |
| 376 minidump_info.product_name = product_name(); | 398 minidump_info.product_name = product_name(); |
| 377 minidump_info.version_number = version_number(); | 399 minidump_info.version_number = version_number(); |
| 378 minidump_info.channel_name = channel_name(); | 400 minidump_info.channel_name = channel_name(); |
| 379 #if defined(ARCH_CPU_X86) | 401 #if defined(ARCH_CPU_X86) |
| 380 minidump_info.platform = std::string("Win32"); | 402 minidump_info.platform = std::string("Win32"); |
| 381 #elif defined(ARCH_CPU_X86_64) | 403 #elif defined(ARCH_CPU_X86_64) |
| 382 minidump_info.platform = std::string("Win64"); | 404 minidump_info.platform = std::string("Win64"); |
| 383 #endif | 405 #endif |
| 384 | 406 |
| 385 return WritePostmortemDump(minidump_file, report, minidump_info); | 407 return WritePostmortemDump(minidump_file, report, minidump_info); |
| 386 } | 408 } |
| 387 | 409 |
| 388 } // namespace browser_watcher | 410 } // namespace browser_watcher |
| OLD | NEW |