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 "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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |