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/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 Loading... | |
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 Loading... | |
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 information. | |
925 info->address = static_cast<uintptr_t>(address); | |
926 info->size = static_cast<size_t>(size); | |
927 info->is_loaded = loaded != 0; | |
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 // Pickled information never changes so can always be returned. | |
940 if (offsetof(ModuleInfoRecord, pickle) + pickle_size > record_size) | |
941 return false; | |
942 Pickle pickler(pickle, pickle_size); | |
943 PickleIterator iter(pickler); | |
944 return iter.ReadString(&info->file) && iter.ReadString(&info->version) && | |
945 iter.ReadString(&info->identifier) && | |
946 iter.ReadString(&info->debug_file) && | |
947 iter.ReadString(&info->debug_identifier); | |
948 } | |
949 | |
950 bool GlobalActivityTracker::ModuleInfoRecord::EncodeFrom( | |
951 const GlobalActivityTracker::ModuleInfo& info, | |
952 size_t record_size) { | |
953 Pickle pickler; | |
954 bool okay = pickler.WriteString(info.file) && | |
955 pickler.WriteString(info.version) && | |
956 pickler.WriteString(info.identifier) && | |
957 pickler.WriteString(info.debug_file) && | |
958 pickler.WriteString(info.debug_identifier); | |
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 // The pickle never changes and this is done before the record is made | |
969 // iterable so no thread protection is necessary. | |
970 memcpy(pickle, pickler.data(), pickler.size()); | |
971 pickle_size = pickler.size(); | |
972 changes.store(0, std::memory_order_relaxed); | |
973 return UpdateFrom(info); | |
974 } | |
975 | |
976 bool GlobalActivityTracker::ModuleInfoRecord::UpdateFrom( | |
977 const GlobalActivityTracker::ModuleInfo& info) { | |
978 // Updates can occur after the record is made visible so make changes atomic. | |
979 // A "strong" exchange ensures no false failures. | |
980 uint32_t old_changes = changes.load(std::memory_order_relaxed); | |
981 uint32_t new_changes = old_changes | kModuleInformationChanging; | |
982 if ((old_changes & kModuleInformationChanging) != 0 || | |
983 !changes.compare_exchange_strong(old_changes, new_changes, | |
984 std::memory_order_acquire, | |
985 std::memory_order_acquire)) { | |
986 NOTREACHED() << "Multiple sources are updating module information."; | |
987 return false; | |
988 } | |
989 | |
990 address = info.address; | |
991 size = info.size; | |
992 loaded = info.is_loaded ? 1 : 0; | |
993 | |
994 bool success = changes.compare_exchange_strong(new_changes, old_changes + 1, | |
995 std::memory_order_release, | |
996 std::memory_order_relaxed); | |
997 DCHECK(success); | |
998 return true; | |
999 } | |
1000 | |
1001 // static | |
1002 size_t GlobalActivityTracker::ModuleInfoRecord::EncodedSize( | |
1003 const GlobalActivityTracker::ModuleInfo& info) { | |
1004 PickleSizer sizer; | |
1005 sizer.AddString(info.file); | |
1006 sizer.AddString(info.version); | |
1007 sizer.AddString(info.identifier); | |
1008 sizer.AddString(info.debug_file); | |
1009 sizer.AddString(info.debug_identifier); | |
manzagop (departed)
2017/01/13 23:01:02
Siggi was suggesting we do as little work for stor
bcwhite
2017/01/14 01:23:24
According to my brief conversation with Chris, it
| |
1010 | |
1011 return offsetof(ModuleInfoRecord, pickle) + sizeof(Pickle::Header) + | |
1012 sizer.payload_size(); | |
1013 } | |
1014 | |
897 GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity( | 1015 GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity( |
898 const void* program_counter, | 1016 const void* program_counter, |
899 const void* origin, | 1017 const void* origin, |
900 Activity::Type type, | 1018 Activity::Type type, |
901 const ActivityData& data, | 1019 const ActivityData& data, |
902 bool lock_allowed) | 1020 bool lock_allowed) |
903 : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed), | 1021 : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed), |
904 program_counter, | 1022 program_counter, |
905 origin, | 1023 origin, |
906 type, | 1024 type, |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1015 // Convert the memory block found above into an actual memory address. | 1133 // 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 | 1134 // Doing the conversion as a Header object enacts the 32/64-bit size |
1017 // consistency checks which would not otherwise be done. Unfortunately, | 1135 // consistency checks which would not otherwise be done. Unfortunately, |
1018 // some older compilers and MSVC don't have standard-conforming definitions | 1136 // 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 | 1137 // 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 | 1138 // those platforms assuming that the checks on other platforms will be |
1021 // sufficient. | 1139 // sufficient. |
1022 // TODO(bcwhite): Review this after major compiler releases. | 1140 // TODO(bcwhite): Review this after major compiler releases. |
1023 DCHECK(mem_reference); | 1141 DCHECK(mem_reference); |
1024 void* mem_base; | 1142 void* mem_base; |
1025 #if 0 // TODO(bcwhite): Update this for new GetAsObject functionality. | 1143 mem_base = |
1026 mem_base = allocator_->GetAsObject<ThreadActivityTracker::Header>( | 1144 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 | 1145 |
1033 DCHECK(mem_base); | 1146 DCHECK(mem_base); |
1034 DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference)); | 1147 DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference)); |
1035 | 1148 |
1036 // Create a tracker with the acquired memory and set it as the tracker | 1149 // Create a tracker with the acquired memory and set it as the tracker |
1037 // for this particular thread in thread-local-storage. | 1150 // for this particular thread in thread-local-storage. |
1038 ManagedActivityTracker* tracker = | 1151 ManagedActivityTracker* tracker = |
1039 new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_); | 1152 new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_); |
1040 DCHECK(tracker->IsValid()); | 1153 DCHECK(tracker->IsValid()); |
1041 this_thread_tracker_.Set(tracker); | 1154 this_thread_tracker_.Set(tracker); |
(...skipping 17 matching lines...) Expand all Loading... | |
1059 PersistentMemoryAllocator::Reference ref = | 1172 PersistentMemoryAllocator::Reference ref = |
1060 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); | 1173 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); |
1061 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage, | 1174 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage, |
1062 message.size() + 1); | 1175 message.size() + 1); |
1063 if (memory) { | 1176 if (memory) { |
1064 memcpy(memory, message.data(), message.size()); | 1177 memcpy(memory, message.data(), message.size()); |
1065 allocator_->MakeIterable(ref); | 1178 allocator_->MakeIterable(ref); |
1066 } | 1179 } |
1067 } | 1180 } |
1068 | 1181 |
1182 void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) { | |
1183 AutoLock lock(modules_lock_); | |
1184 auto found = modules_.find(info.file); | |
1185 if (found != modules_.end()) { | |
1186 ModuleInfoRecord* record = found->second; | |
1187 DCHECK(record); | |
1188 | |
1189 // Update the basic state of module information that has been already | |
1190 // recorded. It is assumed that the string information (identifier, | |
1191 // version, etc.) remain unchanged which means that there's no need | |
1192 // to create a new record to accommodate a possibly longer length. | |
1193 record->UpdateFrom(info); | |
1194 return; | |
1195 } | |
1196 | |
1197 size_t required_size = ModuleInfoRecord::EncodedSize(info); | |
1198 ModuleInfoRecord* record = | |
1199 allocator_->AllocateObject<ModuleInfoRecord>(required_size); | |
1200 if (!record) | |
1201 return; | |
1202 | |
1203 bool success = record->EncodeFrom(info, required_size); | |
1204 DCHECK(success); | |
1205 allocator_->MakeIterable(record); | |
1206 modules_.insert(std::make_pair(info.file, record)); | |
1207 } | |
1208 | |
1069 GlobalActivityTracker::GlobalActivityTracker( | 1209 GlobalActivityTracker::GlobalActivityTracker( |
1070 std::unique_ptr<PersistentMemoryAllocator> allocator, | 1210 std::unique_ptr<PersistentMemoryAllocator> allocator, |
1071 int stack_depth) | 1211 int stack_depth) |
1072 : allocator_(std::move(allocator)), | 1212 : allocator_(std::move(allocator)), |
1073 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), | 1213 stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), |
1074 this_thread_tracker_(&OnTLSDestroy), | 1214 this_thread_tracker_(&OnTLSDestroy), |
1075 thread_tracker_count_(0), | 1215 thread_tracker_count_(0), |
1076 thread_tracker_allocator_(allocator_.get(), | 1216 thread_tracker_allocator_(allocator_.get(), |
1077 kTypeIdActivityTracker, | 1217 kTypeIdActivityTracker, |
1078 kTypeIdActivityTrackerFree, | 1218 kTypeIdActivityTrackerFree, |
(...skipping 13 matching lines...) Expand all Loading... | |
1092 PersistentMemoryAllocator::kSizeAny), | 1232 PersistentMemoryAllocator::kSizeAny), |
1093 kGlobalDataSize) { | 1233 kGlobalDataSize) { |
1094 // Ensure the passed memory is valid and empty (iterator finds nothing). | 1234 // Ensure the passed memory is valid and empty (iterator finds nothing). |
1095 uint32_t type; | 1235 uint32_t type; |
1096 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); | 1236 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); |
1097 | 1237 |
1098 // Ensure that there is no other global object and then make this one such. | 1238 // Ensure that there is no other global object and then make this one such. |
1099 DCHECK(!g_tracker_); | 1239 DCHECK(!g_tracker_); |
1100 g_tracker_ = this; | 1240 g_tracker_ = this; |
1101 | 1241 |
1102 // The global user-data record must be iterable in order to be found by an | 1242 // The global records must be iterable in order to be found by an analyzer. |
1103 // analyzer. | |
1104 allocator_->MakeIterable(allocator_->GetAsReference( | 1243 allocator_->MakeIterable(allocator_->GetAsReference( |
1105 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); | 1244 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); |
1106 } | 1245 } |
1107 | 1246 |
1108 GlobalActivityTracker::~GlobalActivityTracker() { | 1247 GlobalActivityTracker::~GlobalActivityTracker() { |
1109 DCHECK_EQ(g_tracker_, this); | 1248 DCHECK_EQ(g_tracker_, this); |
1110 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); | 1249 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); |
1111 g_tracker_ = nullptr; | 1250 g_tracker_ = nullptr; |
1112 } | 1251 } |
1113 | 1252 |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1210 : GlobalActivityTracker::ScopedThreadActivity( | 1349 : GlobalActivityTracker::ScopedThreadActivity( |
1211 program_counter, | 1350 program_counter, |
1212 nullptr, | 1351 nullptr, |
1213 Activity::ACT_PROCESS_WAIT, | 1352 Activity::ACT_PROCESS_WAIT, |
1214 ActivityData::ForProcess(process->Pid()), | 1353 ActivityData::ForProcess(process->Pid()), |
1215 /*lock_allowed=*/true) {} | 1354 /*lock_allowed=*/true) {} |
1216 #endif | 1355 #endif |
1217 | 1356 |
1218 } // namespace debug | 1357 } // namespace debug |
1219 } // namespace base | 1358 } // namespace base |
OLD | NEW |