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

Side by Side Diff: base/debug/activity_tracker.cc

Issue 2666653002: Record field-trial information in stability file for crash analysis. (Closed)
Patch Set: rebased Created 3 years, 10 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
« no previous file with comments | « base/debug/activity_tracker.h ('k') | base/metrics/field_trial.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "base/debug/activity_tracker.h" 5 #include "base/debug/activity_tracker.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 #include <utility> 9 #include <utility>
10 10
11 #include "base/atomic_sequence_num.h" 11 #include "base/atomic_sequence_num.h"
12 #include "base/debug/stack_trace.h" 12 #include "base/debug/stack_trace.h"
13 #include "base/files/file.h" 13 #include "base/files/file.h"
14 #include "base/files/file_path.h" 14 #include "base/files/file_path.h"
15 #include "base/files/memory_mapped_file.h" 15 #include "base/files/memory_mapped_file.h"
16 #include "base/lazy_instance.h"
16 #include "base/logging.h" 17 #include "base/logging.h"
17 #include "base/memory/ptr_util.h" 18 #include "base/memory/ptr_util.h"
18 #include "base/metrics/field_trial.h" 19 #include "base/metrics/field_trial.h"
19 #include "base/metrics/histogram_macros.h" 20 #include "base/metrics/histogram_macros.h"
20 #include "base/pending_task.h" 21 #include "base/pending_task.h"
21 #include "base/pickle.h" 22 #include "base/pickle.h"
22 #include "base/process/process.h" 23 #include "base/process/process.h"
23 #include "base/process/process_handle.h" 24 #include "base/process/process_handle.h"
24 #include "base/stl_util.h" 25 #include "base/stl_util.h"
25 #include "base/strings/string_util.h" 26 #include "base/strings/string_util.h"
26 #include "base/threading/platform_thread.h" 27 #include "base/threading/platform_thread.h"
27 28
28 namespace base { 29 namespace base {
29 namespace debug { 30 namespace debug {
30 31
31 namespace { 32 namespace {
32 33
33 // A number that identifies the memory as having been initialized. It's 34 // A number that identifies the memory as having been initialized. It's
34 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker). 35 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker).
35 // A version number is added on so that major structure changes won't try to 36 // A version number is added on so that major structure changes won't try to
36 // read an older version (since the cookie won't match). 37 // read an older version (since the cookie won't match).
37 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2 38 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2
38 39
39 // The minimum depth a stack should support. 40 // The minimum depth a stack should support.
40 const int kMinStackDepth = 2; 41 const int kMinStackDepth = 2;
41 42
42 // The amount of memory set aside for holding arbitrary user data (key/value 43 // The amount of memory set aside for holding arbitrary user data (key/value
43 // pairs) globally or associated with ActivityData entries. 44 // pairs) globally or associated with ActivityData entries.
44 const size_t kUserDataSize = 1024; // bytes 45 const size_t kUserDataSize = 1 << 10; // 1 KiB
45 const size_t kGlobalDataSize = 4096; // bytes 46 const size_t kGlobalDataSize = 16 << 10; // 16 KiB
46 const size_t kMaxUserDataNameLength = 47 const size_t kMaxUserDataNameLength =
47 static_cast<size_t>(std::numeric_limits<uint8_t>::max()); 48 static_cast<size_t>(std::numeric_limits<uint8_t>::max());
48 49
49 // A constant used to indicate that module information is changing. 50 // A constant used to indicate that module information is changing.
50 const uint32_t kModuleInformationChanging = 0x80000000; 51 const uint32_t kModuleInformationChanging = 0x80000000;
51 52
52 union ThreadRef { 53 union ThreadRef {
53 int64_t as_id; 54 int64_t as_id;
54 #if defined(OS_WIN) 55 #if defined(OS_WIN)
55 // On Windows, the handle itself is often a pseudo-handle with a common 56 // On Windows, the handle itself is often a pseudo-handle with a common
56 // value meaning "this thread" and so the thread-id is used. The former 57 // value meaning "this thread" and so the thread-id is used. The former
57 // can be converted to a thread-id with a system call. 58 // can be converted to a thread-id with a system call.
58 PlatformThreadId as_tid; 59 PlatformThreadId as_tid;
59 #elif defined(OS_POSIX) 60 #elif defined(OS_POSIX)
60 // On Posix, the handle is always a unique identifier so no conversion 61 // On Posix, the handle is always a unique identifier so no conversion
61 // needs to be done. However, it's value is officially opaque so there 62 // needs to be done. However, it's value is officially opaque so there
62 // is no one correct way to convert it to a numerical identifier. 63 // is no one correct way to convert it to a numerical identifier.
63 PlatformThreadHandle::Handle as_handle; 64 PlatformThreadHandle::Handle as_handle;
64 #endif 65 #endif
65 }; 66 };
66 67
68 // A class to collect field-trial information and dump it to a global
69 // tracker. If no GlobalActivityTracker exists, the information is held
70 // locally for dumping later.
71 class FieldTrialRecorder {
72 public:
73 FieldTrialRecorder() : enabled_(true) {}
74 ~FieldTrialRecorder() {}
75
76 void Disable() {
77 enabled_.store(false, std::memory_order_relaxed);
78 AutoLock lock(trial_group_lock_);
79 trial_group_map_.clear();
80 }
81
82 void DumpCollectedActivity() {
Alexei Svitkine (slow) 2017/02/07 19:28:10 Thinking about this more, I think we can simplify
bcwhite 2017/02/07 21:18:03 Done.
83 DCHECK(GlobalActivityTracker::Get());
84
85 // OnFieldTrialGroupFinalized will insert into trial_group_map_ if the
86 // global tracker goes away. Be safe and move its contents to a local
87 // variable first.
88 std::map<std::string, std::string> collected;
89 {
90 AutoLock lock(trial_group_lock_);
91 collected.swap(trial_group_map_);
92 }
93
94 // Store collected information to the global tracker.
95 for (const auto& kv : collected)
96 Record(kv.first, kv.second);
97 }
98
99 void Record(const std::string& trial_name, const std::string& group_name) {
100 if (!enabled_.load(std::memory_order_relaxed))
101 return;
102
103 const std::string key = std::string("FieldTrial.") + trial_name;
104
105 GlobalActivityTracker* tracker = GlobalActivityTracker::Get();
106 if (tracker) {
107 // global_data is thread-safe.
108 tracker->global_data().SetString(key, group_name);
109 } else {
110 AutoLock lock(trial_group_lock_);
111 trial_group_map_.insert(std::make_pair(key, group_name));
112 }
113 }
114
115 private:
116 std::atomic<bool> enabled_;
117 std::map<std::string, std::string> trial_group_map_;
118 Lock trial_group_lock_;
119
120 DISALLOW_COPY_AND_ASSIGN(FieldTrialRecorder);
121 };
122
123 LazyInstance<FieldTrialRecorder>::Leaky g_field_trial_recorder =
124 LAZY_INSTANCE_INITIALIZER;
125
67 // Determines the previous aligned index. 126 // Determines the previous aligned index.
68 size_t RoundDownToAlignment(size_t index, size_t alignment) { 127 size_t RoundDownToAlignment(size_t index, size_t alignment) {
69 return index & (0 - alignment); 128 return index & (0 - alignment);
70 } 129 }
71 130
72 // Determines the next aligned index. 131 // Determines the next aligned index.
73 size_t RoundUpToAlignment(size_t index, size_t alignment) { 132 size_t RoundUpToAlignment(size_t index, size_t alignment) {
74 return (index + (alignment - 1)) & (0 - alignment); 133 return (index + (alignment - 1)) & (0 - alignment);
75 } 134 }
76 135
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
281 // to be used for analysis through snapshots. 340 // to be used for analysis through snapshots.
282 ImportExistingData(); 341 ImportExistingData();
283 } 342 }
284 343
285 ActivityUserData::~ActivityUserData() {} 344 ActivityUserData::~ActivityUserData() {}
286 345
287 void ActivityUserData::Set(StringPiece name, 346 void ActivityUserData::Set(StringPiece name,
288 ValueType type, 347 ValueType type,
289 const void* memory, 348 const void* memory,
290 size_t size) { 349 size_t size) {
291 DCHECK(thread_checker_.CalledOnValidThread());
292 DCHECK_GE(std::numeric_limits<uint8_t>::max(), name.length()); 350 DCHECK_GE(std::numeric_limits<uint8_t>::max(), name.length());
293 size = std::min(std::numeric_limits<uint16_t>::max() - (kMemoryAlignment - 1), 351 size = std::min(std::numeric_limits<uint16_t>::max() - (kMemoryAlignment - 1),
294 size); 352 size);
295 353
296 // It's possible that no user data is being stored. 354 // It's possible that no user data is being stored.
297 if (!memory_) 355 if (!memory_)
298 return; 356 return;
299 357
300 // The storage of a name is limited so use that limit during lookup. 358 // The storage of a name is limited so use that limit during lookup.
301 if (name.length() > kMaxUserDataNameLength) 359 if (name.length() > kMaxUserDataNameLength)
(...skipping 747 matching lines...) Expand 10 before | Expand all | Expand 10 after
1049 AutoLock lock(global->user_data_allocator_lock_); 1107 AutoLock lock(global->user_data_allocator_lock_);
1050 user_data_ = 1108 user_data_ =
1051 tracker_->GetUserData(activity_id_, &global->user_data_allocator_); 1109 tracker_->GetUserData(activity_id_, &global->user_data_allocator_);
1052 } else { 1110 } else {
1053 user_data_ = MakeUnique<ActivityUserData>(nullptr, 0); 1111 user_data_ = MakeUnique<ActivityUserData>(nullptr, 0);
1054 } 1112 }
1055 } 1113 }
1056 return *user_data_; 1114 return *user_data_;
1057 } 1115 }
1058 1116
1117 GlobalActivityTracker::GlobalUserData::GlobalUserData(void* memory, size_t size)
1118 : ActivityUserData(memory, size) {}
1119
1120 GlobalActivityTracker::GlobalUserData::~GlobalUserData() {}
1121
1122 void GlobalActivityTracker::GlobalUserData::Set(StringPiece name,
1123 ValueType type,
1124 const void* memory,
1125 size_t size) {
1126 AutoLock lock(data_lock_);
1127 ActivityUserData::Set(name, type, memory, size);
1128 }
1129
1059 GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker( 1130 GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker(
1060 PersistentMemoryAllocator::Reference mem_reference, 1131 PersistentMemoryAllocator::Reference mem_reference,
1061 void* base, 1132 void* base,
1062 size_t size) 1133 size_t size)
1063 : ThreadActivityTracker(base, size), 1134 : ThreadActivityTracker(base, size),
1064 mem_reference_(mem_reference), 1135 mem_reference_(mem_reference),
1065 mem_base_(base) {} 1136 mem_base_(base) {}
1066 1137
1067 GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() { 1138 GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() {
1068 // The global |g_tracker_| must point to the owner of this class since all 1139 // The global |g_tracker_| must point to the owner of this class since all
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
1209 allocator_->AllocateObject<ModuleInfoRecord>(required_size); 1280 allocator_->AllocateObject<ModuleInfoRecord>(required_size);
1210 if (!record) 1281 if (!record)
1211 return; 1282 return;
1212 1283
1213 bool success = record->EncodeFrom(info, required_size); 1284 bool success = record->EncodeFrom(info, required_size);
1214 DCHECK(success); 1285 DCHECK(success);
1215 allocator_->MakeIterable(record); 1286 allocator_->MakeIterable(record);
1216 modules_.insert(std::make_pair(info.file, record)); 1287 modules_.insert(std::make_pair(info.file, record));
1217 } 1288 }
1218 1289
1290 // static
1291 void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name,
1292 const std::string& group_name) {
1293 g_field_trial_recorder.Get().Record(trial_name, group_name);
1294 }
1295
1296 // static
1297 void GlobalActivityTracker::DisableFieldTrialRecording() {
1298 g_field_trial_recorder.Get().Disable();
1299 }
1300
1219 GlobalActivityTracker::GlobalActivityTracker( 1301 GlobalActivityTracker::GlobalActivityTracker(
1220 std::unique_ptr<PersistentMemoryAllocator> allocator, 1302 std::unique_ptr<PersistentMemoryAllocator> allocator,
1221 int stack_depth) 1303 int stack_depth)
1222 : allocator_(std::move(allocator)), 1304 : allocator_(std::move(allocator)),
1223 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), 1305 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)),
1224 this_thread_tracker_(&OnTLSDestroy), 1306 this_thread_tracker_(&OnTLSDestroy),
1225 thread_tracker_count_(0), 1307 thread_tracker_count_(0),
1226 thread_tracker_allocator_(allocator_.get(), 1308 thread_tracker_allocator_(allocator_.get(),
1227 kTypeIdActivityTracker, 1309 kTypeIdActivityTracker,
1228 kTypeIdActivityTrackerFree, 1310 kTypeIdActivityTrackerFree,
1229 stack_memory_size_, 1311 stack_memory_size_,
1230 kCachedThreadMemories, 1312 kCachedThreadMemories,
1231 /*make_iterable=*/true), 1313 /*make_iterable=*/true),
1232 user_data_allocator_(allocator_.get(), 1314 user_data_allocator_(allocator_.get(),
1233 kTypeIdUserDataRecord, 1315 kTypeIdUserDataRecord,
1234 kTypeIdUserDataRecordFree, 1316 kTypeIdUserDataRecordFree,
1235 kUserDataSize, 1317 kUserDataSize,
1236 kCachedUserDataMemories, 1318 kCachedUserDataMemories,
1237 /*make_iterable=*/false), 1319 /*make_iterable=*/false),
1238 user_data_( 1320 global_data_(
1239 allocator_->GetAsArray<char>( 1321 allocator_->GetAsArray<char>(
1240 allocator_->Allocate(kGlobalDataSize, kTypeIdGlobalDataRecord), 1322 allocator_->Allocate(kGlobalDataSize, kTypeIdGlobalDataRecord),
1241 kTypeIdGlobalDataRecord, 1323 kTypeIdGlobalDataRecord,
1242 PersistentMemoryAllocator::kSizeAny), 1324 PersistentMemoryAllocator::kSizeAny),
1243 kGlobalDataSize) { 1325 kGlobalDataSize) {
1244 // Ensure the passed memory is valid and empty (iterator finds nothing). 1326 // Ensure the passed memory is valid and empty (iterator finds nothing).
1245 uint32_t type; 1327 uint32_t type;
1246 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); 1328 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type));
1247 1329
1248 // Ensure that there is no other global object and then make this one such. 1330 // Ensure that there is no other global object and then make this one such.
1249 DCHECK(!g_tracker_); 1331 DCHECK(!g_tracker_);
1250 subtle::NoBarrier_Store(&g_tracker_, reinterpret_cast<uintptr_t>(this)); 1332 subtle::NoBarrier_Store(&g_tracker_, reinterpret_cast<uintptr_t>(this));
1251 1333
1252 // The global records must be iterable in order to be found by an analyzer. 1334 // The global records must be iterable in order to be found by an analyzer.
1253 allocator_->MakeIterable(allocator_->GetAsReference( 1335 allocator_->MakeIterable(allocator_->GetAsReference(
1254 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); 1336 global_data_.GetBaseAddress(), kTypeIdGlobalDataRecord));
1337
1338 // Enable tracking (if not done previously) and dump previously collected
1339 // field-trial information to this global tracker.
1340 g_field_trial_recorder.Get().DumpCollectedActivity();
1255 } 1341 }
1256 1342
1257 GlobalActivityTracker::~GlobalActivityTracker() { 1343 GlobalActivityTracker::~GlobalActivityTracker() {
1258 DCHECK_EQ(Get(), this); 1344 DCHECK_EQ(Get(), this);
1259 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); 1345 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed));
1260 subtle::NoBarrier_Store(&g_tracker_, 0); 1346 subtle::NoBarrier_Store(&g_tracker_, 0);
1261 } 1347 }
1262 1348
1263 void GlobalActivityTracker::ReturnTrackerMemory( 1349 void GlobalActivityTracker::ReturnTrackerMemory(
1264 ManagedActivityTracker* tracker) { 1350 ManagedActivityTracker* tracker) {
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
1359 : GlobalActivityTracker::ScopedThreadActivity( 1445 : GlobalActivityTracker::ScopedThreadActivity(
1360 program_counter, 1446 program_counter,
1361 nullptr, 1447 nullptr,
1362 Activity::ACT_PROCESS_WAIT, 1448 Activity::ACT_PROCESS_WAIT,
1363 ActivityData::ForProcess(process->Pid()), 1449 ActivityData::ForProcess(process->Pid()),
1364 /*lock_allowed=*/true) {} 1450 /*lock_allowed=*/true) {}
1365 #endif 1451 #endif
1366 1452
1367 } // namespace debug 1453 } // namespace debug
1368 } // namespace base 1454 } // namespace base
OLDNEW
« no previous file with comments | « base/debug/activity_tracker.h ('k') | base/metrics/field_trial.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698