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

Side by Side Diff: tools/memory_watcher/memory_watcher.cc

Issue 366031: Support running memory watch under vista, plus other tweaks... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « tools/memory_watcher/memory_watcher.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 (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 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 <windows.h> 5 #include <windows.h>
6 #include <tlhelp32.h> // for CreateToolhelp32Snapshot() 6 #include <tlhelp32.h> // for CreateToolhelp32Snapshot()
7 #include <map> 7 #include <map>
8 8
9 #include "tools/memory_watcher/memory_watcher.h" 9 #include "tools/memory_watcher/memory_watcher.h"
10 #include "base/file_util.h" 10 #include "base/file_util.h"
11 #include "base/lock.h" 11 #include "base/lock.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/stats_counters.h" 13 #include "base/stats_counters.h"
14 #include "base/string_util.h" 14 #include "base/string_util.h"
15 #include "tools/memory_watcher/call_stack.h" 15 #include "tools/memory_watcher/call_stack.h"
16 #include "tools/memory_watcher/preamble_patcher.h" 16 #include "tools/memory_watcher/preamble_patcher.h"
17 17
18 static StatsCounter mem_in_use("MemoryInUse.Bytes"); 18 static StatsCounter mem_in_use("MemoryInUse.Bytes");
19 static StatsCounter mem_in_use_blocks("MemoryInUse.Blocks"); 19 static StatsCounter mem_in_use_blocks("MemoryInUse.Blocks");
20 static StatsCounter mem_in_use_allocs("MemoryInUse.Allocs"); 20 static StatsCounter mem_in_use_allocs("MemoryInUse.Allocs");
21 static StatsCounter mem_in_use_frees("MemoryInUse.Frees"); 21 static StatsCounter mem_in_use_frees("MemoryInUse.Frees");
22 22
23 // --------------------------------------------------------------------- 23 // ---------------------------------------------------------------------
24 24
25 MemoryWatcher::MemoryWatcher() 25 MemoryWatcher::MemoryWatcher()
26 : file_(NULL), 26 : file_(NULL),
27 hooked_(false), 27 hooked_(false),
28 in_track_(false), 28 active_thread_id_(0) {
29 block_map_size_(0) {
30 MemoryHook::Initialize(); 29 MemoryHook::Initialize();
31 CallStack::Initialize(); 30 CallStack::Initialize();
32 31
33 block_map_ = new CallStackMap(); 32 block_map_ = new CallStackMap();
34 stack_map_ = new CallStackIdMap();
35 33
36 // Register last - only after we're ready for notifications! 34 // Register last - only after we're ready for notifications!
37 Hook(); 35 Hook();
38 } 36 }
39 37
40 MemoryWatcher::~MemoryWatcher() { 38 MemoryWatcher::~MemoryWatcher() {
41 Unhook(); 39 Unhook();
42 40
43 CloseLogFile(); 41 CloseLogFile();
44 42
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 if (file_ != NULL) { 78 if (file_ != NULL) {
81 fclose(file_); 79 fclose(file_);
82 file_ = NULL; 80 file_ = NULL;
83 std::wstring tmp_name = ASCIIToWide(file_name_); 81 std::wstring tmp_name = ASCIIToWide(file_name_);
84 tmp_name += L".tmp"; 82 tmp_name += L".tmp";
85 file_util::Move(FilePath::FromWStringHack(tmp_name), 83 file_util::Move(FilePath::FromWStringHack(tmp_name),
86 FilePath::FromWStringHack(ASCIIToWide(file_name_))); 84 FilePath::FromWStringHack(ASCIIToWide(file_name_)));
87 } 85 }
88 } 86 }
89 87
88 bool MemoryWatcher::LockedRecursionDetected() const {
89 if (!active_thread_id_) return false;
90 DWORD thread_id = GetCurrentThreadId();
91 // TODO(jar): Perchance we should use atomic access to member.
92 return thread_id == active_thread_id_;
93 }
94
90 void MemoryWatcher::OnTrack(HANDLE heap, int32 id, int32 size) { 95 void MemoryWatcher::OnTrack(HANDLE heap, int32 id, int32 size) {
91 // Don't track zeroes. It's a waste of time. 96 // Don't track zeroes. It's a waste of time.
92 if (size == 0) 97 if (size == 0)
93 return; 98 return;
94 99
100 if (LockedRecursionDetected())
101 return;
102
95 // AllocationStack overrides new/delete to not allocate 103 // AllocationStack overrides new/delete to not allocate
96 // from the main heap. 104 // from the main heap.
97 AllocationStack* stack = new AllocationStack(); 105 AllocationStack* stack = new AllocationStack(size);
106 if (!stack->Valid()) return; // Recursion blocked generation of stack.
107
98 { 108 {
99 AutoLock lock(block_map_lock_); 109 AutoLock lock(block_map_lock_);
100 110
101 // Ideally, we'd like to verify that the block being added 111 // Ideally, we'd like to verify that the block being added
102 // here is not already in our list of tracked blocks. However, 112 // here is not already in our list of tracked blocks. However,
103 // the lookup in our hash table is expensive and slows us too 113 // the lookup in our hash table is expensive and slows us too
104 // much. Uncomment this line if you think you need it. 114 // much.
105 //DCHECK(block_map_->find(id) == block_map_->end()); 115 CallStackMap::iterator block_it = block_map_->find(id);
116 if (block_it != block_map_->end()) {
117 #if 0 // Don't do this until stack->ToString() uses ONLY our heap.
118 active_thread_id_ = GetCurrentThreadId();
119 PrivateAllocatorString output;
120 block_it->second->ToString(&output);
121 // LOG(INFO) << "First Stack size " << stack->size() << "was\n" << output;
122 stack->ToString(&output);
123 // LOG(INFO) << "Second Stack size " << stack->size() << "was\n" << output;
124 #endif // 0
125
126 // TODO(jar): We should delete one stack, and keep the other, perhaps
127 // based on size.
128 // For now, just delete the first, and keep the second?
129 delete block_it->second;
130 }
131 // TODO(jar): Perchance we should use atomic access to member.
132 active_thread_id_ = 0; // Note: Only do this AFTER exiting above scope!
106 133
107 (*block_map_)[id] = stack; 134 (*block_map_)[id] = stack;
108
109 CallStackIdMap::iterator it = stack_map_->find(stack->hash());
110 if (it != stack_map_->end()) {
111 it->second.size += size;
112 it->second.count++;
113 } else {
114 StackTrack tracker;
115 tracker.count = 1;
116 tracker.size = size;
117 tracker.stack = stack;
118 (*stack_map_)[stack->hash()] = tracker;
119 }
120
121 block_map_size_ += size;
122 } 135 }
123 136
124 mem_in_use.Set(block_map_size_); 137 mem_in_use.Add(size);
125 mem_in_use_blocks.Increment(); 138 mem_in_use_blocks.Increment();
126 mem_in_use_allocs.Increment(); 139 mem_in_use_allocs.Increment();
127 } 140 }
128 141
129 void MemoryWatcher::OnUntrack(HANDLE heap, int32 id, int32 size) { 142 void MemoryWatcher::OnUntrack(HANDLE heap, int32 id, int32 size) {
130 DCHECK_GE(size, 0); 143 DCHECK_GE(size, 0);
131 144
132 // Don't bother with these. 145 // Don't bother with these.
133 if (size == 0) 146 if (size == 0)
134 return; 147 return;
135 148
149 if (LockedRecursionDetected())
150 return;
151
136 { 152 {
137 AutoLock lock(block_map_lock_); 153 AutoLock lock(block_map_lock_);
154 active_thread_id_ = GetCurrentThreadId();
138 155
139 // First, find the block in our block_map. 156 // First, find the block in our block_map.
140 CallStackMap::iterator it = block_map_->find(id); 157 CallStackMap::iterator it = block_map_->find(id);
141 if (it != block_map_->end()) { 158 if (it != block_map_->end()) {
142 AllocationStack* stack = it->second; 159 AllocationStack* stack = it->second;
143 CallStackIdMap::iterator id_it = stack_map_->find(stack->hash()); 160 DCHECK(stack->size() == size);
144 DCHECK(id_it != stack_map_->end());
145 id_it->second.size -= size;
146 id_it->second.count--;
147 DCHECK_GE(id_it->second.count, 0);
148
149 // If there are no more callstacks with this stack, then we
150 // have cleaned up all instances, and can safely delete the
151 // StackTracker in the stack_map.
152 bool safe_to_delete = true;
153 if (id_it->second.count != 0) {
154 // See if our |StackTracker| is also using |stack|.
155 if (id_it->second.stack == stack)
156 safe_to_delete = false; // We're still using |stack|.
157 } else {
158 // See if we skipped deleting our |StackTracker|'s |stack| earlier.
159 if (id_it->second.stack != stack)
160 delete id_it->second.stack; // We skipped it earlier.
161 stack_map_->erase(id_it); // Discard our StackTracker.
162 }
163
164 block_map_size_ -= size;
165 block_map_->erase(id); 161 block_map_->erase(id);
166 if (safe_to_delete) 162 delete stack;
167 delete stack;
168 } else { 163 } else {
169 // Untracked item. This happens a fair amount, and it is 164 // Untracked item. This happens a fair amount, and it is
170 // normal. A lot of time elapses during process startup 165 // normal. A lot of time elapses during process startup
171 // before the allocation routines are hooked. 166 // before the allocation routines are hooked.
167 size = 0; // Ignore size in tallies.
172 } 168 }
169 // TODO(jar): Perchance we should use atomic access to member.
170 active_thread_id_ = 0;
173 } 171 }
174 172
175 mem_in_use.Set(block_map_size_); 173 mem_in_use.Add(-size);
176 mem_in_use_blocks.Decrement(); 174 mem_in_use_blocks.Decrement();
177 mem_in_use_frees.Increment(); 175 mem_in_use_frees.Increment();
178 } 176 }
179 177
180 void MemoryWatcher::SetLogName(char* log_name) { 178 void MemoryWatcher::SetLogName(char* log_name) {
181 if (!log_name) 179 if (!log_name)
182 return; 180 return;
183 181
184 log_name_ = log_name; 182 log_name_ = log_name;
185 } 183 }
186 184
185 // Help sort lists of stacks based on allocation cost.
186 // Note: Sort based on allocation count is interesting too!
187 static bool CompareCallStackIdItems(MemoryWatcher::StackTrack* left,
188 MemoryWatcher::StackTrack* right) {
189 return left->size > right->size;
190 }
191
192
187 void MemoryWatcher::DumpLeaks() { 193 void MemoryWatcher::DumpLeaks() {
188 // We can only dump the leaks once. We'll cleanup the hooks here. 194 // We can only dump the leaks once. We'll cleanup the hooks here.
189 DCHECK(hooked_); 195 if (!hooked_)
196 return;
190 Unhook(); 197 Unhook();
191 198
192 AutoLock lock(block_map_lock_); 199 AutoLock lock(block_map_lock_);
200 active_thread_id_ = GetCurrentThreadId();
193 201
194 OpenLogFile(); 202 OpenLogFile();
195 203
196 // Dump the stack map. 204 // Aggregate contributions from each allocated block on per-stack basis.
197 CallStackIdMap::iterator it = stack_map_->begin(); 205 CallStackIdMap stack_map;
198 while (it != stack_map_->end()) { 206 for (CallStackMap::iterator block_it = block_map_->begin();
199 fwprintf(file_, L"%d bytes, %d items (0x%x)\n", 207 block_it != block_map_->end(); ++block_it) {
200 it->second.size, it->second.count, it->first); 208 AllocationStack* stack = block_it->second;
201 CallStack* stack = it->second.stack; 209 int32 stack_hash = stack->hash();
202 std::string output; 210 int32 alloc_block_size = stack->size();
211 CallStackIdMap::iterator it = stack_map.find(stack_hash);
212 if (it == stack_map.end()) {
213 StackTrack tracker;
214 tracker.count = 1;
215 tracker.size = alloc_block_size;
216 tracker.stack = stack; // Temporary pointer into block_map_.
217 stack_map[stack_hash] = tracker;
218 } else {
219 it->second.count++;
220 it->second.size += alloc_block_size;
221 }
222 }
223 // Don't release lock yet, as block_map_ is still pointed into.
224
225 // Put references to StrackTracks into array for sorting.
226 std::vector<StackTrack*, PrivateHookAllocator<int32> >
227 stack_tracks(stack_map.size());
228 CallStackIdMap::iterator it = stack_map.begin();
229 for (size_t i = 0; i < stack_tracks.size(); ++i) {
230 stack_tracks[i] = &(it->second);
231 ++it;
232 }
233 sort(stack_tracks.begin(), stack_tracks.end(), CompareCallStackIdItems);
234
235 int32 total_bytes = 0;
236 int32 total_blocks = 0;
237 for (size_t i = 0; i < stack_tracks.size(); ++i) {
238 StackTrack* stack_track = stack_tracks[i];
239 fwprintf(file_, L"%d bytes, %d allocs, #%d\n",
240 stack_track->size, stack_track->count, i);
241 total_bytes += stack_track->size;
242 total_blocks += stack_track->count;
243
244 CallStack* stack = stack_track->stack;
245 PrivateAllocatorString output;
203 stack->ToString(&output); 246 stack->ToString(&output);
204 fprintf(file_, "%s", output.c_str()); 247 fprintf(file_, "%s", output.c_str());
205 it++;
206 } 248 }
207 fprintf(file_, "Total Leaks: %d\n", block_map_->size()); 249 fprintf(file_, "Total Leaks: %d\n", total_blocks);
208 fprintf(file_, "Total Stacks: %d\n", stack_map_->size()); 250 fprintf(file_, "Total Stacks: %d\n", stack_tracks.size());
209 fprintf(file_, "Total Bytes: %d\n", block_map_size_); 251 fprintf(file_, "Total Bytes: %d\n", total_bytes);
210 CloseLogFile(); 252 CloseLogFile();
211 } 253 }
OLDNEW
« no previous file with comments | « tools/memory_watcher/memory_watcher.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698