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