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

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: changed GUID to generic 'identifier'; noted it and others as 'opaque' with no defined meaning Created 3 years, 11 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') | 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 #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/logging.h" 15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h" 16 #include "base/memory/ptr_util.h"
17 #include "base/metrics/field_trial.h" 17 #include "base/metrics/field_trial.h"
18 #include "base/metrics/histogram_macros.h" 18 #include "base/metrics/histogram_macros.h"
19 #include "base/pending_task.h" 19 #include "base/pending_task.h"
20 #include "base/pickle.h"
20 #include "base/process/process.h" 21 #include "base/process/process.h"
21 #include "base/process/process_handle.h" 22 #include "base/process/process_handle.h"
22 #include "base/stl_util.h" 23 #include "base/stl_util.h"
23 #include "base/strings/string_util.h" 24 #include "base/strings/string_util.h"
24 #include "base/threading/platform_thread.h" 25 #include "base/threading/platform_thread.h"
25 26
26 namespace base { 27 namespace base {
27 namespace debug { 28 namespace debug {
28 29
29 namespace { 30 namespace {
30 31
31 // A number that identifies the memory as having been initialized. It's 32 // A number that identifies the memory as having been initialized. It's
32 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker). 33 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker).
33 // A version number is added on so that major structure changes won't try to 34 // A version number is added on so that major structure changes won't try to
34 // read an older version (since the cookie won't match). 35 // read an older version (since the cookie won't match).
35 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2 36 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2
36 37
37 // The minimum depth a stack should support. 38 // The minimum depth a stack should support.
38 const int kMinStackDepth = 2; 39 const int kMinStackDepth = 2;
39 40
40 // The amount of memory set aside for holding arbitrary user data (key/value 41 // The amount of memory set aside for holding arbitrary user data (key/value
41 // pairs) globally or associated with ActivityData entries. 42 // pairs) globally or associated with ActivityData entries.
42 const size_t kUserDataSize = 1024; // bytes 43 const size_t kUserDataSize = 1024; // bytes
43 const size_t kGlobalDataSize = 4096; // bytes 44 const size_t kGlobalDataSize = 4096; // bytes
44 const size_t kMaxUserDataNameLength = 45 const size_t kMaxUserDataNameLength =
45 static_cast<size_t>(std::numeric_limits<uint8_t>::max()); 46 static_cast<size_t>(std::numeric_limits<uint8_t>::max());
46 47
48 // A constant used to indicate that module information is changing.
49 const uint32_t kModuleInformationChanging = 0x80000000;
50
47 union ThreadRef { 51 union ThreadRef {
48 int64_t as_id; 52 int64_t as_id;
49 #if defined(OS_WIN) 53 #if defined(OS_WIN)
50 // On Windows, the handle itself is often a pseudo-handle with a common 54 // On Windows, the handle itself is often a pseudo-handle with a common
51 // value meaning "this thread" and so the thread-id is used. The former 55 // value meaning "this thread" and so the thread-id is used. The former
52 // can be converted to a thread-id with a system call. 56 // can be converted to a thread-id with a system call.
53 PlatformThreadId as_tid; 57 PlatformThreadId as_tid;
54 #elif defined(OS_POSIX) 58 #elif defined(OS_POSIX)
55 // On Posix, the handle is always a unique identifier so no conversion 59 // On Posix, the handle is always a unique identifier so no conversion
56 // needs to be done. However, it's value is officially opaque so there 60 // needs to be done. However, it's value is officially opaque so there
(...skipping 414 matching lines...) Expand 10 before | Expand all | Expand 10 after
471 const void* ActivityUserData::GetBaseAddress() { 475 const void* ActivityUserData::GetBaseAddress() {
472 // The |memory_| pointer advances as elements are written but the |id_| 476 // The |memory_| pointer advances as elements are written but the |id_|
473 // value is always at the start of the block so just return that. 477 // value is always at the start of the block so just return that.
474 return id_; 478 return id_;
475 } 479 }
476 480
477 // This information is kept for every thread that is tracked. It is filled 481 // This information is kept for every thread that is tracked. It is filled
478 // the very first time the thread is seen. All fields must be of exact sizes 482 // the very first time the thread is seen. All fields must be of exact sizes
479 // so there is no issue moving between 32 and 64-bit builds. 483 // so there is no issue moving between 32 and 64-bit builds.
480 struct ThreadActivityTracker::Header { 484 struct ThreadActivityTracker::Header {
485 // Defined in .h for analyzer access. Increment this if structure changes!
486 static constexpr uint32_t kPersistentTypeId =
487 GlobalActivityTracker::kTypeIdActivityTracker;
488
481 // Expected size for 32/64-bit check. 489 // Expected size for 32/64-bit check.
482 static constexpr size_t kExpectedInstanceSize = 80; 490 static constexpr size_t kExpectedInstanceSize = 80;
483 491
484 // This unique number indicates a valid initialization of the memory. 492 // This unique number indicates a valid initialization of the memory.
485 std::atomic<uint32_t> cookie; 493 std::atomic<uint32_t> cookie;
486 494
487 // The number of Activity slots (spaces that can hold an Activity) that 495 // The number of Activity slots (spaces that can hold an Activity) that
488 // immediately follow this structure in memory. 496 // immediately follow this structure in memory.
489 uint32_t stack_slots; 497 uint32_t stack_slots;
490 498
(...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after
887 } 895 }
888 896
889 // static 897 // static
890 size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) { 898 size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) {
891 return static_cast<size_t>(stack_depth) * sizeof(Activity) + sizeof(Header); 899 return static_cast<size_t>(stack_depth) * sizeof(Activity) + sizeof(Header);
892 } 900 }
893 901
894 902
895 GlobalActivityTracker* GlobalActivityTracker::g_tracker_ = nullptr; 903 GlobalActivityTracker* GlobalActivityTracker::g_tracker_ = nullptr;
896 904
905 GlobalActivityTracker::ModuleInfo::ModuleInfo() {}
906 GlobalActivityTracker::ModuleInfo::ModuleInfo(ModuleInfo&& rhs) = default;
907 GlobalActivityTracker::ModuleInfo::ModuleInfo(const ModuleInfo& rhs) = default;
908 GlobalActivityTracker::ModuleInfo::~ModuleInfo() {}
909
910 GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=(
911 ModuleInfo&& rhs) = default;
912 GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=(
913 const ModuleInfo& rhs) = default;
914
915 GlobalActivityTracker::ModuleInfoRecord::ModuleInfoRecord() {}
916 GlobalActivityTracker::ModuleInfoRecord::~ModuleInfoRecord() {}
917
918 bool GlobalActivityTracker::ModuleInfoRecord::DecodeTo(
919 GlobalActivityTracker::ModuleInfo* info,
920 size_t record_size) const {
921 // Get the current "changes" indicator, acquiring all the other values.
922 uint32_t current_changes = changes.load(std::memory_order_acquire);
923
924 // Copy out the dynamic information.
925 info->is_loaded = loaded != 0;
926 info->address = static_cast<uintptr_t>(address);
927 info->load_time = load_time;
928
929 // Check to make sure no information changed while being read. A "seq-cst"
930 // operation is expensive but is only done during analysis and it's the only
931 // way to ensure this occurs after all the accesses above. If changes did
932 // occur then return a "not loaded" result so that |size| and |address|
933 // aren't expected to be accurate.
934 if ((current_changes & kModuleInformationChanging) != 0 ||
935 changes.load(std::memory_order_seq_cst) != current_changes) {
936 info->is_loaded = false;
937 }
938
939 // Copy out the static information. These never change so don't have to be
940 // protected by the atomic |current_changes| operations.
941 info->size = static_cast<size_t>(size);
942 info->timestamp = timestamp;
943 info->age = age;
944 memcpy(info->identifier, identifier, sizeof(info->identifier));
945
946 if (offsetof(ModuleInfoRecord, pickle) + pickle_size > record_size)
947 return false;
948 Pickle pickler(pickle, pickle_size);
949 PickleIterator iter(pickler);
950 return iter.ReadString(&info->file) && iter.ReadString(&info->debug_file);
951 }
952
953 bool GlobalActivityTracker::ModuleInfoRecord::EncodeFrom(
954 const GlobalActivityTracker::ModuleInfo& info,
955 size_t record_size) {
956 Pickle pickler;
957 bool okay =
958 pickler.WriteString(info.file) && pickler.WriteString(info.debug_file);
959 if (!okay) {
960 NOTREACHED();
961 return false;
962 }
963 if (offsetof(ModuleInfoRecord, pickle) + pickler.size() > record_size) {
964 NOTREACHED();
965 return false;
966 }
967
968 // These fields never changes and are done before the record is made
969 // iterable so no thread protection is necessary.
970 size = info.size;
971 timestamp = info.timestamp;
972 age = info.age;
973 memcpy(identifier, info.identifier, sizeof(identifier));
974 memcpy(pickle, pickler.data(), pickler.size());
975 pickle_size = pickler.size();
976 changes.store(0, std::memory_order_relaxed);
977
978 // Now set those fields that can change.
979 return UpdateFrom(info);
980 }
981
982 bool GlobalActivityTracker::ModuleInfoRecord::UpdateFrom(
983 const GlobalActivityTracker::ModuleInfo& info) {
984 // Updates can occur after the record is made visible so make changes atomic.
985 // A "strong" exchange ensures no false failures.
986 uint32_t old_changes = changes.load(std::memory_order_relaxed);
987 uint32_t new_changes = old_changes | kModuleInformationChanging;
988 if ((old_changes & kModuleInformationChanging) != 0 ||
989 !changes.compare_exchange_strong(old_changes, new_changes,
990 std::memory_order_acquire,
991 std::memory_order_acquire)) {
992 NOTREACHED() << "Multiple sources are updating module information.";
993 return false;
994 }
995
996 loaded = info.is_loaded ? 1 : 0;
997 address = info.address;
998 load_time = Time::Now().ToInternalValue();
999
1000 bool success = changes.compare_exchange_strong(new_changes, old_changes + 1,
1001 std::memory_order_release,
1002 std::memory_order_relaxed);
1003 DCHECK(success);
1004 return true;
1005 }
1006
1007 // static
1008 size_t GlobalActivityTracker::ModuleInfoRecord::EncodedSize(
1009 const GlobalActivityTracker::ModuleInfo& info) {
1010 PickleSizer sizer;
1011 sizer.AddString(info.file);
1012 sizer.AddString(info.debug_file);
1013
1014 return offsetof(ModuleInfoRecord, pickle) + sizeof(Pickle::Header) +
1015 sizer.payload_size();
1016 }
1017
897 GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity( 1018 GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity(
898 const void* program_counter, 1019 const void* program_counter,
899 const void* origin, 1020 const void* origin,
900 Activity::Type type, 1021 Activity::Type type,
901 const ActivityData& data, 1022 const ActivityData& data,
902 bool lock_allowed) 1023 bool lock_allowed)
903 : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed), 1024 : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed),
904 program_counter, 1025 program_counter,
905 origin, 1026 origin,
906 type, 1027 type,
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
1015 // Convert the memory block found above into an actual memory address. 1136 // Convert the memory block found above into an actual memory address.
1016 // Doing the conversion as a Header object enacts the 32/64-bit size 1137 // Doing the conversion as a Header object enacts the 32/64-bit size
1017 // consistency checks which would not otherwise be done. Unfortunately, 1138 // consistency checks which would not otherwise be done. Unfortunately,
1018 // some older compilers and MSVC don't have standard-conforming definitions 1139 // some older compilers and MSVC don't have standard-conforming definitions
1019 // of std::atomic which cause it not to be plain-old-data. Don't check on 1140 // of std::atomic which cause it not to be plain-old-data. Don't check on
1020 // those platforms assuming that the checks on other platforms will be 1141 // those platforms assuming that the checks on other platforms will be
1021 // sufficient. 1142 // sufficient.
1022 // TODO(bcwhite): Review this after major compiler releases. 1143 // TODO(bcwhite): Review this after major compiler releases.
1023 DCHECK(mem_reference); 1144 DCHECK(mem_reference);
1024 void* mem_base; 1145 void* mem_base;
1025 #if 0 // TODO(bcwhite): Update this for new GetAsObject functionality. 1146 mem_base =
1026 mem_base = allocator_->GetAsObject<ThreadActivityTracker::Header>( 1147 allocator_->GetAsObject<ThreadActivityTracker::Header>(mem_reference);
1027 mem_reference, kTypeIdActivityTracker);
1028 #else
1029 mem_base = allocator_->GetAsArray<char>(mem_reference, kTypeIdActivityTracker,
1030 PersistentMemoryAllocator::kSizeAny);
1031 #endif
1032 1148
1033 DCHECK(mem_base); 1149 DCHECK(mem_base);
1034 DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference)); 1150 DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference));
1035 1151
1036 // Create a tracker with the acquired memory and set it as the tracker 1152 // Create a tracker with the acquired memory and set it as the tracker
1037 // for this particular thread in thread-local-storage. 1153 // for this particular thread in thread-local-storage.
1038 ManagedActivityTracker* tracker = 1154 ManagedActivityTracker* tracker =
1039 new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_); 1155 new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_);
1040 DCHECK(tracker->IsValid()); 1156 DCHECK(tracker->IsValid());
1041 this_thread_tracker_.Set(tracker); 1157 this_thread_tracker_.Set(tracker);
(...skipping 17 matching lines...) Expand all
1059 PersistentMemoryAllocator::Reference ref = 1175 PersistentMemoryAllocator::Reference ref =
1060 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); 1176 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage);
1061 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage, 1177 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage,
1062 message.size() + 1); 1178 message.size() + 1);
1063 if (memory) { 1179 if (memory) {
1064 memcpy(memory, message.data(), message.size()); 1180 memcpy(memory, message.data(), message.size());
1065 allocator_->MakeIterable(ref); 1181 allocator_->MakeIterable(ref);
1066 } 1182 }
1067 } 1183 }
1068 1184
1185 void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) {
1186 AutoLock lock(modules_lock_);
1187 auto found = modules_.find(info.file);
1188 if (found != modules_.end()) {
1189 ModuleInfoRecord* record = found->second;
1190 DCHECK(record);
1191
1192 // Update the basic state of module information that has been already
1193 // recorded. It is assumed that the string information (identifier,
1194 // version, etc.) remain unchanged which means that there's no need
1195 // to create a new record to accommodate a possibly longer length.
1196 record->UpdateFrom(info);
1197 return;
1198 }
1199
1200 size_t required_size = ModuleInfoRecord::EncodedSize(info);
1201 ModuleInfoRecord* record =
1202 allocator_->AllocateObject<ModuleInfoRecord>(required_size);
1203 if (!record)
1204 return;
1205
1206 bool success = record->EncodeFrom(info, required_size);
1207 DCHECK(success);
1208 allocator_->MakeIterable(record);
1209 modules_.insert(std::make_pair(info.file, record));
1210 }
1211
1069 GlobalActivityTracker::GlobalActivityTracker( 1212 GlobalActivityTracker::GlobalActivityTracker(
1070 std::unique_ptr<PersistentMemoryAllocator> allocator, 1213 std::unique_ptr<PersistentMemoryAllocator> allocator,
1071 int stack_depth) 1214 int stack_depth)
1072 : allocator_(std::move(allocator)), 1215 : allocator_(std::move(allocator)),
1073 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), 1216 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)),
1074 this_thread_tracker_(&OnTLSDestroy), 1217 this_thread_tracker_(&OnTLSDestroy),
1075 thread_tracker_count_(0), 1218 thread_tracker_count_(0),
1076 thread_tracker_allocator_(allocator_.get(), 1219 thread_tracker_allocator_(allocator_.get(),
1077 kTypeIdActivityTracker, 1220 kTypeIdActivityTracker,
1078 kTypeIdActivityTrackerFree, 1221 kTypeIdActivityTrackerFree,
(...skipping 13 matching lines...) Expand all
1092 PersistentMemoryAllocator::kSizeAny), 1235 PersistentMemoryAllocator::kSizeAny),
1093 kGlobalDataSize) { 1236 kGlobalDataSize) {
1094 // Ensure the passed memory is valid and empty (iterator finds nothing). 1237 // Ensure the passed memory is valid and empty (iterator finds nothing).
1095 uint32_t type; 1238 uint32_t type;
1096 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); 1239 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type));
1097 1240
1098 // Ensure that there is no other global object and then make this one such. 1241 // Ensure that there is no other global object and then make this one such.
1099 DCHECK(!g_tracker_); 1242 DCHECK(!g_tracker_);
1100 g_tracker_ = this; 1243 g_tracker_ = this;
1101 1244
1102 // The global user-data record must be iterable in order to be found by an 1245 // The global records must be iterable in order to be found by an analyzer.
1103 // analyzer.
1104 allocator_->MakeIterable(allocator_->GetAsReference( 1246 allocator_->MakeIterable(allocator_->GetAsReference(
1105 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); 1247 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord));
1106 } 1248 }
1107 1249
1108 GlobalActivityTracker::~GlobalActivityTracker() { 1250 GlobalActivityTracker::~GlobalActivityTracker() {
1109 DCHECK_EQ(g_tracker_, this); 1251 DCHECK_EQ(g_tracker_, this);
1110 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); 1252 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed));
1111 g_tracker_ = nullptr; 1253 g_tracker_ = nullptr;
1112 } 1254 }
1113 1255
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
1210 : GlobalActivityTracker::ScopedThreadActivity( 1352 : GlobalActivityTracker::ScopedThreadActivity(
1211 program_counter, 1353 program_counter,
1212 nullptr, 1354 nullptr,
1213 Activity::ACT_PROCESS_WAIT, 1355 Activity::ACT_PROCESS_WAIT,
1214 ActivityData::ForProcess(process->Pid()), 1356 ActivityData::ForProcess(process->Pid()),
1215 /*lock_allowed=*/true) {} 1357 /*lock_allowed=*/true) {}
1216 #endif 1358 #endif
1217 1359
1218 } // namespace debug 1360 } // namespace debug
1219 } // namespace base 1361 } // namespace base
OLDNEW
« no previous file with comments | « base/debug/activity_tracker.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698