| /* |
| * Copyright 2016 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef BASE_OBJECT_TRACKER_H_ |
| #define BASE_OBJECT_TRACKER_H_ |
| |
| #include <assert.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/compiler_specific.h" |
| #include "base/stringprintf.h" |
| #include "lb_memory_debug.h" |
| #include "lb_memory_manager.h" |
| |
| #if defined(__LB_SHELL__) |
| #include "lb_mutex.h" |
| #else // defined(__LB_SHELL__) |
| #error "Have to include platform header for mutex" |
| #endif // defined(__LB_SHELL__) |
| |
| namespace base { |
| |
| // This class can be used to track and dump all objects belongs to a certain |
| // type. For example, we can use the following steps to dump all Nodes: |
| // 1. Make Node inherit from ObjectTracker<Node>. |
| // 2. Call "Node::Dump(log_cb);" on a particular event (like a key press). |
| // We can also return false in ShouldDump() to skip certain objects and |
| // implement DumpSpecificInfo() to dump information specific to the object like |
| // ref count, etc.. |
| // This class is meant to be used for debugging only. |
| template <typename Object, uint32 BacktraceLevel = 10> |
| class ObjectTracker { |
| public: |
| #if defined(__LB_SHELL__) |
| // We implement our own Lock class to remove the dependency on base::Lock, so |
| // we can track base::Lock. |
| class Lock { |
| public: |
| Lock() { |
| int rv = lb_shell_mutex_init(&mutex_); |
| assert(rv == 0); |
| UNREFERENCED_PARAMETER(rv); |
| } |
| ~Lock() { |
| int rv = lb_shell_mutex_destroy(&mutex_); |
| assert(rv == 0); |
| UNREFERENCED_PARAMETER(rv); |
| } |
| void Acquire() { |
| int rv = lb_shell_mutex_lock(&mutex_); |
| assert(rv == 0); |
| UNREFERENCED_PARAMETER(rv); |
| } |
| void Release() { |
| int rv = lb_shell_mutex_unlock(&mutex_); |
| assert(rv == 0); |
| UNREFERENCED_PARAMETER(rv); |
| } |
| |
| private: |
| lb_shell_mutex_t mutex_; |
| }; |
| #endif // defined(__LB_SHELL__) |
| |
| typedef void LogCB(const std::string&); |
| |
| static void Dump(LogCB log_cb) { |
| lock().Acquire(); |
| log_cb("============================ start ============================\n"); |
| // Sort the objects based on their addresses. |
| std::vector<ObjectTracker*> trackers; |
| trackers.reserve(16 * 1024); |
| ObjectTracker* current = head_; |
| while (current) { |
| trackers.push_back(current); |
| current = current->next_; |
| } |
| std::sort(trackers.begin(), trackers.end()); |
| |
| int skipped = 0; |
| |
| for (size_t i = 0; i < trackers.size(); ++i) { |
| std::string line; |
| current = trackers[i]; |
| if ((static_cast<Object*>(current))->ShouldDump()) { |
| line = base::StringPrintf("%" PRIxPTR " ", |
| reinterpret_cast<intptr_t>(current)); |
| line += (static_cast<Object*>(current))->DumpSpecificInfo(); |
| for (uint32 j = 0; j < BacktraceLevel; ++j) { |
| base::StringAppendF(&line, " %" PRIxPTR, current->back_trace_[j]); |
| } |
| line += "\n"; |
| log_cb(line); |
| } else { |
| ++skipped; |
| } |
| } |
| if (skipped != 0) { |
| log_cb(base::StringPrintf("Dumped %d items, skipped %d items\n", |
| static_cast<int>(trackers.size()), skipped)); |
| } else { |
| log_cb(base::StringPrintf("Dumped %d items\n", |
| static_cast<int>(trackers.size()))); |
| } |
| log_cb("============================= end =============================\n"); |
| lock().Release(); |
| } |
| |
| protected: |
| ObjectTracker() : next_(NULL), prev_(NULL) { |
| int valid_trace_count = 0; |
| if (LB::Memory::GetBacktraceEnabled()) { |
| valid_trace_count = |
| LB::Memory::Backtrace(0, BacktraceLevel, back_trace_, NULL); |
| } |
| if (valid_trace_count != BacktraceLevel) { |
| memset(back_trace_ + valid_trace_count, 0, |
| (BacktraceLevel - valid_trace_count) * sizeof(back_trace_[0])); |
| } |
| |
| lock().Acquire(); |
| next_ = head_; |
| if (next_) { |
| next_->prev_ = this; |
| } |
| head_ = this; |
| lock().Release(); |
| } |
| |
| ~ObjectTracker() { |
| lock().Acquire(); |
| if (next_) { |
| next_->prev_ = prev_; |
| } |
| if (prev_) { |
| prev_->next_ = next_; |
| } else { |
| head_ = next_; |
| } |
| lock().Release(); |
| } |
| |
| bool ShouldDump() { return true; } |
| std::string DumpSpecificInfo() { return ""; } |
| |
| private: |
| static ObjectTracker* head_; |
| static Lock& lock() { |
| static Lock* lock; |
| // This isn't thread safe. But it should be fine as ObjectTracker is |
| // supposed to be used only for debugging and this removed its dependency |
| // on LazyInstance which is in turn depended on AtExitManager. |
| if (lock == NULL) { |
| lock = new Lock; |
| } |
| return *lock; |
| } |
| |
| ObjectTracker* prev_; |
| ObjectTracker* next_; |
| uintptr_t back_trace_[BacktraceLevel]; |
| }; |
| |
| // static |
| template <typename Object, uint32 BacktraceLevel> |
| ObjectTracker<Object, BacktraceLevel>* |
| ObjectTracker<Object, BacktraceLevel>::head_; |
| |
| } // namespace base |
| |
| #endif // BASE_OBJECT_TRACKER_H_ |