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 | 8 |
| 9 #include "base/debug/stack_trace.h" | 9 #include "base/debug/stack_trace.h" |
| 10 #include "base/files/file.h" | 10 #include "base/files/file.h" |
| 11 #include "base/files/file_path.h" | 11 #include "base/files/file_path.h" |
| 12 #include "base/files/memory_mapped_file.h" | 12 #include "base/files/memory_mapped_file.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
| 16 #include "base/metrics/field_trial.h" | 16 #include "base/metrics/field_trial.h" |
| 17 #include "base/metrics/histogram_macros.h" | 17 #include "base/metrics/histogram_macros.h" |
| 18 #include "base/pending_task.h" | 18 #include "base/pending_task.h" |
| 19 #include "base/pickle.h" | |
| 19 #include "base/process/process.h" | 20 #include "base/process/process.h" |
| 20 #include "base/process/process_handle.h" | 21 #include "base/process/process_handle.h" |
| 21 #include "base/stl_util.h" | 22 #include "base/stl_util.h" |
| 22 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
| 23 #include "base/threading/platform_thread.h" | 24 #include "base/threading/platform_thread.h" |
| 24 | 25 |
| 25 namespace base { | 26 namespace base { |
| 26 namespace debug { | 27 namespace debug { |
| 27 | 28 |
| 28 namespace { | 29 namespace { |
| 29 | 30 |
| 30 // A number that identifies the memory as having been initialized. It's | 31 // A number that identifies the memory as having been initialized. It's |
| 31 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker). | 32 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker). |
| 32 // A version number is added on so that major structure changes won't try to | 33 // A version number is added on so that major structure changes won't try to |
| 33 // read an older version (since the cookie won't match). | 34 // read an older version (since the cookie won't match). |
| 34 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2 | 35 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2 |
| 35 | 36 |
| 36 // The minimum depth a stack should support. | 37 // The minimum depth a stack should support. |
| 37 const int kMinStackDepth = 2; | 38 const int kMinStackDepth = 2; |
| 38 | 39 |
| 39 // The amount of memory set aside for holding arbitrary user data (key/value | 40 // The amount of memory set aside for holding arbitrary user data (key/value |
| 40 // pairs) globally or associated with ActivityData entries. | 41 // pairs) globally or associated with ActivityData entries. |
| 41 const size_t kUserDataSize = 1024; // bytes | 42 const size_t kUserDataSize = 1024; // bytes |
| 42 const size_t kGlobalDataSize = 4096; // bytes | 43 const size_t kGlobalDataSize = 4096; // bytes |
| 44 const size_t kGlobalModulesSize = 4096; // bytes | |
| 43 const size_t kMaxUserDataNameLength = | 45 const size_t kMaxUserDataNameLength = |
| 44 static_cast<size_t>(std::numeric_limits<uint8_t>::max()); | 46 static_cast<size_t>(std::numeric_limits<uint8_t>::max()); |
| 45 | 47 |
| 46 union ThreadRef { | 48 union ThreadRef { |
| 47 int64_t as_id; | 49 int64_t as_id; |
| 48 #if defined(OS_WIN) | 50 #if defined(OS_WIN) |
| 49 // On Windows, the handle itself is often a pseudo-handle with a common | 51 // On Windows, the handle itself is often a pseudo-handle with a common |
| 50 // value meaning "this thread" and so the thread-id is used. The former | 52 // value meaning "this thread" and so the thread-id is used. The former |
| 51 // can be converted to a thread-id with a system call. | 53 // can be converted to a thread-id with a system call. |
| 52 PlatformThreadId as_tid; | 54 PlatformThreadId as_tid; |
| 53 #elif defined(OS_POSIX) | 55 #elif defined(OS_POSIX) |
| 54 // On Posix, the handle is always a unique identifier so no conversion | 56 // On Posix, the handle is always a unique identifier so no conversion |
| 55 // needs to be done. However, it's value is officially opaque so there | 57 // needs to be done. However, it's value is officially opaque so there |
| 56 // is no one correct way to convert it to a numerical identifier. | 58 // is no one correct way to convert it to a numerical identifier. |
| 57 PlatformThreadHandle::Handle as_handle; | 59 PlatformThreadHandle::Handle as_handle; |
| 58 #endif | 60 #endif |
| 59 }; | 61 }; |
| 60 | 62 |
| 63 // State of a module as stared in persistent memory. | |
|
manzagop (departed)
2016/12/14 20:08:53
typo: stared -> stored
bcwhite
2016/12/14 21:59:05
Done.
| |
| 64 struct ModuleInfoStore { | |
| 65 // Expected size for 32/64-bit check. | |
| 66 static constexpr size_t kExpectedInstanceSize = 24; | |
|
manzagop (departed)
2016/12/14 20:08:53
nit: I like having units in variable names (kExpec
manzagop (departed)
2016/12/14 20:08:53
Unused?
bcwhite
2016/12/14 21:59:05
The constant is required (and used) by the Persist
| |
| 67 | |
| 68 uint64_t address; // The base address of the module. | |
| 69 uint64_t size; // The size of the module in bytes. | |
| 70 uint16_t pickle_size; // The size of the following pickle. | |
| 71 uint8_t loaded; // Flag indicating if module is loaded or not. | |
| 72 char pickle[5]; // Other strings; may allocate larger. | |
| 73 | |
| 74 // Decodes/encodes storage structure from more generic info structure. | |
| 75 bool DecodeTo(GlobalActivityTracker::ModuleInfo* info); | |
| 76 bool EncodeFrom(const GlobalActivityTracker::ModuleInfo& info, size_t size); | |
|
manzagop (departed)
2016/12/14 20:08:53
Why do you need the size? Ah it's the size budget.
bcwhite
2016/12/14 21:59:05
Done.
| |
| 77 | |
| 78 // Updates the core information without changing the encoded strings. This is | |
| 79 // useful when a known module changes state (i.e. new load or unload). | |
| 80 bool UpdateFrom(const GlobalActivityTracker::ModuleInfo& info); | |
| 81 | |
| 82 // Deterimes the required memory size for the encoded storage. | |
|
manzagop (departed)
2016/12/14 20:08:53
typo: Deterimes
bcwhite
2016/12/14 21:59:05
Done.
| |
| 83 static size_t EncodedSize(const GlobalActivityTracker::ModuleInfo& info); | |
| 84 }; | |
| 85 | |
| 86 static constexpr size_t kModuleInfoStoreHeaderSize = | |
| 87 sizeof(ModuleInfoStore) - sizeof(((ModuleInfoStore*)0)->pickle); | |
|
manzagop (departed)
2016/12/14 20:08:53
Size of on the null pointer smells funny to me. Wo
bcwhite
2016/12/14 21:59:05
Done.
| |
| 88 | |
| 89 bool ModuleInfoStore::DecodeTo(GlobalActivityTracker::ModuleInfo* info) { | |
| 90 std::atomic_thread_fence(std::memory_order_acquire); | |
| 91 info->is_loaded = loaded != 0; | |
| 92 info->address = static_cast<uintptr_t>(address); | |
| 93 info->size = static_cast<size_t>(size); | |
| 94 | |
| 95 Pickle pickler(pickle, pickle_size); | |
| 96 PickleIterator iter(pickler); | |
| 97 return iter.ReadString(&info->file) && iter.ReadString(&info->version) && | |
| 98 iter.ReadString(&info->identifier) && | |
| 99 iter.ReadString(&info->debug_file) && | |
| 100 iter.ReadString(&info->debug_identifier); | |
| 101 } | |
| 102 | |
| 103 bool ModuleInfoStore::EncodeFrom(const GlobalActivityTracker::ModuleInfo& info, | |
| 104 size_t size) { | |
| 105 Pickle pickler; | |
| 106 bool okay = pickler.WriteString(info.file) && | |
| 107 pickler.WriteString(info.version) && | |
| 108 pickler.WriteString(info.identifier) && | |
| 109 pickler.WriteString(info.debug_file) && | |
| 110 pickler.WriteString(info.debug_identifier); | |
| 111 if (!okay) { | |
| 112 NOTREACHED(); | |
| 113 return false; | |
| 114 } | |
| 115 if (pickler.size() + kModuleInfoStoreHeaderSize > size) { | |
| 116 NOTREACHED(); | |
| 117 return false; | |
| 118 } | |
| 119 | |
| 120 memcpy(pickle, pickler.data(), pickler.size()); | |
| 121 return UpdateFrom(info); | |
| 122 } | |
| 123 | |
| 124 bool ModuleInfoStore::UpdateFrom( | |
| 125 const GlobalActivityTracker::ModuleInfo& info) { | |
| 126 address = info.address; | |
| 127 size = info.size; | |
| 128 loaded = info.is_loaded ? 1 : 0; | |
| 129 std::atomic_thread_fence(std::memory_order_release); | |
| 130 return true; | |
| 131 } | |
| 132 | |
| 133 // static | |
| 134 size_t ModuleInfoStore::EncodedSize( | |
| 135 const GlobalActivityTracker::ModuleInfo& info) { | |
| 136 PickleSizer sizer; | |
| 137 sizer.AddString(info.file); | |
| 138 sizer.AddString(info.version); | |
| 139 sizer.AddString(info.identifier); | |
| 140 sizer.AddString(info.debug_file); | |
| 141 sizer.AddString(info.debug_identifier); | |
| 142 | |
| 143 return kModuleInfoStoreHeaderSize + sizer.payload_size(); | |
| 144 } | |
| 145 | |
| 61 // Determines the previous aligned index. | 146 // Determines the previous aligned index. |
| 62 size_t RoundDownToAlignment(size_t index, size_t alignment) { | 147 size_t RoundDownToAlignment(size_t index, size_t alignment) { |
| 63 return index & (0 - alignment); | 148 return index & (0 - alignment); |
| 64 } | 149 } |
| 65 | 150 |
| 66 // Determines the next aligned index. | 151 // Determines the next aligned index. |
| 67 size_t RoundUpToAlignment(size_t index, size_t alignment) { | 152 size_t RoundUpToAlignment(size_t index, size_t alignment) { |
| 68 return (index + (alignment - 1)) & (0 - alignment); | 153 return (index + (alignment - 1)) & (0 - alignment); |
| 69 } | 154 } |
| 70 | 155 |
| (...skipping 987 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1058 PersistentMemoryAllocator::Reference ref = | 1143 PersistentMemoryAllocator::Reference ref = |
| 1059 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); | 1144 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); |
| 1060 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage, | 1145 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage, |
| 1061 message.size() + 1); | 1146 message.size() + 1); |
| 1062 if (memory) { | 1147 if (memory) { |
| 1063 memcpy(memory, message.data(), message.size()); | 1148 memcpy(memory, message.data(), message.size()); |
| 1064 allocator_->MakeIterable(ref); | 1149 allocator_->MakeIterable(ref); |
| 1065 } | 1150 } |
| 1066 } | 1151 } |
| 1067 | 1152 |
| 1153 void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) { | |
| 1154 AutoLock lock(modules_lock_); | |
| 1155 auto found = modules_.find(info.file); | |
| 1156 if (found != modules_.end()) { | |
| 1157 ModuleInfoStore* store = allocator_->GetAsObject<ModuleInfoStore>( | |
| 1158 found->second, kTypeIdModulesRecord); | |
| 1159 DCHECK(store); | |
| 1160 | |
| 1161 // Get should never fail but need to be tolerant in production. | |
| 1162 if (store) { | |
| 1163 // Update the basic state of module information that has been already | |
| 1164 // recorded. It is assumed that the string information (identifier, | |
| 1165 // version, etc.) remain unchanged which means that there's no need | |
| 1166 // to create a new record to accomodate a possibly longer length. | |
|
manzagop (departed)
2016/12/14 20:08:53
typo: accommodate
bcwhite
2016/12/14 21:59:05
Done.
| |
| 1167 store->UpdateFrom(info); | |
| 1168 return; | |
|
manzagop (departed)
2016/12/14 20:08:53
return outside the if(store)?
bcwhite
2016/12/14 21:59:05
If it fails to load it, a new record will be creat
manzagop (departed)
2016/12/16 16:09:31
But then the map insertion will fail as the key al
bcwhite
2016/12/16 18:35:15
Good point. Always return.
| |
| 1169 } | |
| 1170 } | |
| 1171 | |
| 1172 size_t required_size = ModuleInfoStore::EncodedSize(info); | |
| 1173 PersistentMemoryAllocator::Reference ref = | |
| 1174 allocator_->Allocate(required_size, kTypeIdModulesRecord); | |
| 1175 ModuleInfoStore* store = | |
| 1176 allocator_->GetAsObject<ModuleInfoStore>(ref, kTypeIdModulesRecord); | |
| 1177 if (!store) | |
| 1178 return; | |
| 1179 | |
| 1180 bool success = store->EncodeFrom(info, allocator_->GetAllocSize(ref)); | |
| 1181 DCHECK(success); | |
| 1182 modules_.insert(std::make_pair(info.file, ref)); | |
| 1183 } | |
| 1184 | |
| 1068 GlobalActivityTracker::GlobalActivityTracker( | 1185 GlobalActivityTracker::GlobalActivityTracker( |
| 1069 std::unique_ptr<PersistentMemoryAllocator> allocator, | 1186 std::unique_ptr<PersistentMemoryAllocator> allocator, |
| 1070 int stack_depth) | 1187 int stack_depth) |
| 1071 : allocator_(std::move(allocator)), | 1188 : allocator_(std::move(allocator)), |
| 1072 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), | 1189 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), |
| 1073 this_thread_tracker_(&OnTLSDestroy), | 1190 this_thread_tracker_(&OnTLSDestroy), |
| 1074 thread_tracker_count_(0), | 1191 thread_tracker_count_(0), |
| 1075 thread_tracker_allocator_(allocator_.get(), | 1192 thread_tracker_allocator_(allocator_.get(), |
| 1076 kTypeIdActivityTracker, | 1193 kTypeIdActivityTracker, |
| 1077 kTypeIdActivityTrackerFree, | 1194 kTypeIdActivityTrackerFree, |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1091 PersistentMemoryAllocator::kSizeAny), | 1208 PersistentMemoryAllocator::kSizeAny), |
| 1092 kGlobalDataSize) { | 1209 kGlobalDataSize) { |
| 1093 // Ensure the passed memory is valid and empty (iterator finds nothing). | 1210 // Ensure the passed memory is valid and empty (iterator finds nothing). |
| 1094 uint32_t type; | 1211 uint32_t type; |
| 1095 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); | 1212 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); |
| 1096 | 1213 |
| 1097 // Ensure that there is no other global object and then make this one such. | 1214 // Ensure that there is no other global object and then make this one such. |
| 1098 DCHECK(!g_tracker_); | 1215 DCHECK(!g_tracker_); |
| 1099 g_tracker_ = this; | 1216 g_tracker_ = this; |
| 1100 | 1217 |
| 1101 // The global user-data record must be iterable in order to be found by an | 1218 // The global records must be iterable in order to be found by an analyzer. |
| 1102 // analyzer. | |
| 1103 allocator_->MakeIterable(allocator_->GetAsReference( | 1219 allocator_->MakeIterable(allocator_->GetAsReference( |
| 1104 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); | 1220 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); |
| 1105 } | 1221 } |
| 1106 | 1222 |
| 1107 GlobalActivityTracker::~GlobalActivityTracker() { | 1223 GlobalActivityTracker::~GlobalActivityTracker() { |
| 1108 DCHECK_EQ(g_tracker_, this); | 1224 DCHECK_EQ(g_tracker_, this); |
| 1109 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); | 1225 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); |
| 1110 g_tracker_ = nullptr; | 1226 g_tracker_ = nullptr; |
| 1111 } | 1227 } |
| 1112 | 1228 |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1209 : GlobalActivityTracker::ScopedThreadActivity( | 1325 : GlobalActivityTracker::ScopedThreadActivity( |
| 1210 program_counter, | 1326 program_counter, |
| 1211 nullptr, | 1327 nullptr, |
| 1212 Activity::ACT_PROCESS_WAIT, | 1328 Activity::ACT_PROCESS_WAIT, |
| 1213 ActivityData::ForProcess(process->Pid()), | 1329 ActivityData::ForProcess(process->Pid()), |
| 1214 /*lock_allowed=*/true) {} | 1330 /*lock_allowed=*/true) {} |
| 1215 #endif | 1331 #endif |
| 1216 | 1332 |
| 1217 } // namespace debug | 1333 } // namespace debug |
| 1218 } // namespace base | 1334 } // namespace base |
| OLD | NEW |