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

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

Issue 2566983009: Support storing information about what modules are loaded in the process. (Closed)
Patch Set: Created 4 years 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
« base/debug/activity_tracker.h ('K') | « base/debug/activity_tracker.h ('k') | no next file » | 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 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
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
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
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
OLDNEW
« base/debug/activity_tracker.h ('K') | « base/debug/activity_tracker.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698