| 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 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |