Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/trace_event/process_memory_dump.h" | 5 #include "base/trace_event/process_memory_dump.h" |
| 6 | 6 |
| 7 #include <ctype.h> | |
| 7 #include <errno.h> | 8 #include <errno.h> |
| 8 | 9 |
| 9 #include <vector> | 10 #include <vector> |
| 10 | 11 |
| 11 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 12 #include "base/process/process_metrics.h" | 13 #include "base/process/process_metrics.h" |
| 13 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
| 14 #include "base/trace_event/heap_profiler_heap_dump_writer.h" | 15 #include "base/trace_event/heap_profiler_heap_dump_writer.h" |
| 16 #include "base/trace_event/memory_dump_request_args.h" | |
| 15 #include "base/trace_event/process_memory_totals.h" | 17 #include "base/trace_event/process_memory_totals.h" |
| 16 #include "base/trace_event/trace_event_argument.h" | 18 #include "base/trace_event/trace_event_argument.h" |
| 17 #include "build/build_config.h" | 19 #include "build/build_config.h" |
| 18 | 20 |
| 19 #if defined(OS_IOS) | 21 #if defined(OS_IOS) |
| 20 #include <sys/sysctl.h> | 22 #include <sys/sysctl.h> |
| 21 #endif | 23 #endif |
| 22 | 24 |
| 23 #if defined(OS_POSIX) | 25 #if defined(OS_POSIX) |
| 24 #include <sys/mman.h> | 26 #include <sys/mman.h> |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 39 const MemoryAllocatorDumpGuid& guid) { | 41 const MemoryAllocatorDumpGuid& guid) { |
| 40 return "global/" + guid.ToString(); | 42 return "global/" + guid.ToString(); |
| 41 } | 43 } |
| 42 | 44 |
| 43 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED) | 45 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED) |
| 44 size_t GetSystemPageCount(size_t mapped_size, size_t page_size) { | 46 size_t GetSystemPageCount(size_t mapped_size, size_t page_size) { |
| 45 return (mapped_size + page_size - 1) / page_size; | 47 return (mapped_size + page_size - 1) / page_size; |
| 46 } | 48 } |
| 47 #endif | 49 #endif |
| 48 | 50 |
| 51 // A list of string names that are allowed for the allocator dumps in background | |
| 52 // mode. | |
| 53 const char* const kDumpNameWhitelist[] = { | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
Ok I think at this point we want a memory_infra_ba
ssid
2016/05/31 22:33:20
Done.
| |
| 54 // TODO(ssid): Fill this list with dump names, crbug.com/613198. | |
| 55 "", | |
| 56 }; | |
| 57 | |
| 58 // Check if the string is in the whitelist. | |
| 59 bool IsNameValidForBackgroundMode(const std::string& name, | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
s/Valid/Whitelisted/ ... which makes then the comm
ssid
2016/05/31 22:33:20
Acknowledged.
| |
| 60 const char* whitelisted_name_for_testing) { | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
see my comment in the other cl, having to pass thi
ssid
2016/05/31 22:33:20
Done.
| |
| 61 static size_t kListSize = arraysize(kDumpNameWhitelist); | |
| 62 | |
| 63 // Remove special characters, numbers (including hexadecimal which are makred | |
|
Dmitry Skiba
2016/05/31 06:48:46
*marked
ssid
2016/05/31 22:33:20
Done.
| |
| 64 // by '0x') from the given string. | |
| 65 const size_t length = name.size(); | |
| 66 std::unique_ptr<char[]> stripped_str(new char[length]); | |
|
Dmitry Skiba
2016/05/31 06:48:46
Uhm, why not std::string? And push_back() instead
ssid
2016/05/31 22:33:20
Done.
| |
| 67 size_t str_index = 0; | |
| 68 bool parsing_hex = false; | |
| 69 for (size_t i = 0; i < length; ++i) { | |
| 70 if (parsing_hex) { | |
| 71 if (isxdigit(name[i])) { | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
instead of having this state machine here, can you
ssid
2016/05/27 17:42:48
Sorry, I don't get it.
Yeah I would still need thi
Primiano Tucci (use gerrit)
2016/05/27 17:51:19
Ok sorry this was a stupid comment from my side. I
ssid
2016/05/31 22:33:20
isalpha is there because there could be cases wher
Primiano Tucci (use gerrit)
2016/06/02 20:24:04
Ah right, I think it's ok.
| |
| 72 continue; | |
| 73 } else { | |
| 74 parsing_hex = false; | |
| 75 } | |
| 76 } | |
| 77 if (i + 1 < length && name[i] == '0' && name[i + 1] == 'x') { | |
| 78 parsing_hex = true; | |
| 79 ++i; | |
| 80 } else if (isalpha(name[i])) { | |
| 81 stripped_str[str_index++] = name[i]; | |
| 82 } | |
| 83 } | |
| 84 stripped_str[str_index] = '\0'; | |
| 85 | |
| 86 if (whitelisted_name_for_testing) | |
| 87 return strcmp(stripped_str.get(), whitelisted_name_for_testing) == 0; | |
| 88 | |
| 89 for (size_t i = 0; i < kListSize; ++i) { | |
| 90 if (strcmp(stripped_str.get(), kDumpNameWhitelist[i]) == 0) | |
| 91 return true; | |
| 92 } | |
| 93 return false; | |
| 94 } | |
| 95 | |
| 49 } // namespace | 96 } // namespace |
| 50 | 97 |
| 51 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED) | 98 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED) |
| 52 // static | 99 // static |
| 53 size_t ProcessMemoryDump::GetSystemPageSize() { | 100 size_t ProcessMemoryDump::GetSystemPageSize() { |
| 54 #if defined(OS_IOS) | 101 #if defined(OS_IOS) |
| 55 // On iOS, getpagesize() returns the user page sizes, but for allocating | 102 // On iOS, getpagesize() returns the user page sizes, but for allocating |
| 56 // arrays for mincore(), kernel page sizes is needed. sysctlbyname() should | 103 // arrays for mincore(), kernel page sizes is needed. sysctlbyname() should |
| 57 // be used for this. Refer to crbug.com/542671 and Apple rdar://23651782 | 104 // be used for this. Refer to crbug.com/542671 and Apple rdar://23651782 |
| 58 int pagesize; | 105 int pagesize; |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 141 DCHECK(!failure); | 188 DCHECK(!failure); |
| 142 if (failure) { | 189 if (failure) { |
| 143 total_resident_size = 0; | 190 total_resident_size = 0; |
| 144 LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid"; | 191 LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid"; |
| 145 } | 192 } |
| 146 return total_resident_size; | 193 return total_resident_size; |
| 147 } | 194 } |
| 148 #endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED) | 195 #endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED) |
| 149 | 196 |
| 150 ProcessMemoryDump::ProcessMemoryDump( | 197 ProcessMemoryDump::ProcessMemoryDump( |
| 151 scoped_refptr<MemoryDumpSessionState> session_state) | 198 scoped_refptr<MemoryDumpSessionState> session_state, |
| 199 MemoryDumpLevelOfDetail level_of_detail) | |
| 152 : has_process_totals_(false), | 200 : has_process_totals_(false), |
| 153 has_process_mmaps_(false), | 201 has_process_mmaps_(false), |
| 154 session_state_(std::move(session_state)) {} | 202 session_state_(std::move(session_state)), |
| 203 level_of_detail_(level_of_detail), | |
| 204 whitelisted_name_for_testing_(nullptr) { | |
| 205 if (level_of_detail_ == MemoryDumpLevelOfDetail::BACKGROUND) { | |
| 206 dummy_mad_.reset(new MemoryAllocatorDump("dummy", this)); | |
| 207 } | |
| 208 } | |
| 155 | 209 |
| 156 ProcessMemoryDump::~ProcessMemoryDump() {} | 210 ProcessMemoryDump::~ProcessMemoryDump() {} |
| 157 | 211 |
| 158 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump( | 212 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump( |
| 159 const std::string& absolute_name) { | 213 const std::string& absolute_name) { |
| 160 return AddAllocatorDumpInternal( | 214 return AddAllocatorDumpInternal( |
| 161 WrapUnique(new MemoryAllocatorDump(absolute_name, this))); | 215 WrapUnique(new MemoryAllocatorDump(absolute_name, this))); |
| 162 } | 216 } |
| 163 | 217 |
| 164 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump( | 218 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump( |
| 165 const std::string& absolute_name, | 219 const std::string& absolute_name, |
| 166 const MemoryAllocatorDumpGuid& guid) { | 220 const MemoryAllocatorDumpGuid& guid) { |
| 167 return AddAllocatorDumpInternal( | 221 return AddAllocatorDumpInternal( |
| 168 WrapUnique(new MemoryAllocatorDump(absolute_name, this, guid))); | 222 WrapUnique(new MemoryAllocatorDump(absolute_name, this, guid))); |
| 169 } | 223 } |
| 170 | 224 |
| 171 MemoryAllocatorDump* ProcessMemoryDump::AddAllocatorDumpInternal( | 225 MemoryAllocatorDump* ProcessMemoryDump::AddAllocatorDumpInternal( |
| 172 std::unique_ptr<MemoryAllocatorDump> mad) { | 226 std::unique_ptr<MemoryAllocatorDump> mad) { |
| 227 // In background mode return dummy dump if invalid dump name is given. | |
| 228 if (level_of_detail_ == MemoryDumpLevelOfDetail::BACKGROUND && | |
| 229 !IsNameValidForBackgroundMode(mad->absolute_name(), | |
| 230 whitelisted_name_for_testing_)) { | |
| 231 return dummy_mad_.get(); | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
maybe NOTREACHED also here to help debugging?
ssid
2016/05/31 22:33:20
I have changed the tests to use AddAllocatorDumpIn
Primiano Tucci (use gerrit)
2016/06/02 20:24:03
You can just check the allocator_dumps().count("na
| |
| 232 } | |
| 233 | |
| 173 auto insertion_result = allocator_dumps_.insert( | 234 auto insertion_result = allocator_dumps_.insert( |
| 174 std::make_pair(mad->absolute_name(), std::move(mad))); | 235 std::make_pair(mad->absolute_name(), std::move(mad))); |
| 175 MemoryAllocatorDump* inserted_mad = insertion_result.first->second.get(); | 236 MemoryAllocatorDump* inserted_mad = insertion_result.first->second.get(); |
| 176 DCHECK(insertion_result.second) << "Duplicate name: " | 237 DCHECK(insertion_result.second) << "Duplicate name: " |
| 177 << inserted_mad->absolute_name(); | 238 << inserted_mad->absolute_name(); |
| 178 return inserted_mad; | 239 return inserted_mad; |
| 179 } | 240 } |
| 180 | 241 |
| 181 MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump( | 242 MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump( |
| 182 const std::string& absolute_name) const { | 243 const std::string& absolute_name) const { |
| 183 auto it = allocator_dumps_.find(absolute_name); | 244 auto it = allocator_dumps_.find(absolute_name); |
| 184 return it == allocator_dumps_.end() ? nullptr : it->second.get(); | 245 if (it != allocator_dumps_.end()) |
| 246 return it->second.get(); | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
should you just leave this as it is and return nul
ssid
2016/05/27 17:42:48
I can't do that. Some dump providers assume that t
Primiano Tucci (use gerrit)
2016/06/02 20:24:03
Acknowledged.
| |
| 247 return level_of_detail_ == MemoryDumpLevelOfDetail::BACKGROUND | |
| 248 ? dummy_mad_.get() | |
| 249 : nullptr; | |
| 185 } | 250 } |
| 186 | 251 |
| 187 MemoryAllocatorDump* ProcessMemoryDump::GetOrCreateAllocatorDump( | 252 MemoryAllocatorDump* ProcessMemoryDump::GetOrCreateAllocatorDump( |
| 188 const std::string& absolute_name) { | 253 const std::string& absolute_name) { |
| 189 MemoryAllocatorDump* mad = GetAllocatorDump(absolute_name); | 254 MemoryAllocatorDump* mad = GetAllocatorDump(absolute_name); |
| 190 return mad ? mad : CreateAllocatorDump(absolute_name); | 255 return mad ? mad : CreateAllocatorDump(absolute_name); |
| 191 } | 256 } |
| 192 | 257 |
| 193 MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump( | 258 MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump( |
| 194 const MemoryAllocatorDumpGuid& guid) { | 259 const MemoryAllocatorDumpGuid& guid) { |
| 260 // Global dumps are disabled in background mode. | |
| 261 if (level_of_detail_ == MemoryDumpLevelOfDetail::BACKGROUND) | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
I don't think global dumps are a problem right? th
ssid
2016/05/27 17:42:48
Global dumps are not required. Anyway we report ju
| |
| 262 return dummy_mad_.get(); | |
| 263 | |
| 195 // A shared allocator dump can be shared within a process and the guid could | 264 // A shared allocator dump can be shared within a process and the guid could |
| 196 // have been created already. | 265 // have been created already. |
| 197 MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid); | 266 MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid); |
| 198 if (mad) { | 267 if (mad) { |
| 199 // The weak flag is cleared because this method should create a non-weak | 268 // The weak flag is cleared because this method should create a non-weak |
| 200 // dump. | 269 // dump. |
| 201 mad->clear_flags(MemoryAllocatorDump::Flags::WEAK); | 270 mad->clear_flags(MemoryAllocatorDump::Flags::WEAK); |
| 202 return mad; | 271 return mad; |
| 203 } | 272 } |
| 204 return CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid); | 273 return CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid); |
| 205 } | 274 } |
| 206 | 275 |
| 207 MemoryAllocatorDump* ProcessMemoryDump::CreateWeakSharedGlobalAllocatorDump( | 276 MemoryAllocatorDump* ProcessMemoryDump::CreateWeakSharedGlobalAllocatorDump( |
| 208 const MemoryAllocatorDumpGuid& guid) { | 277 const MemoryAllocatorDumpGuid& guid) { |
| 278 // Global dumps are disabled in background mode. | |
| 279 if (level_of_detail_ == MemoryDumpLevelOfDetail::BACKGROUND) | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
ditto don't think you need this
| |
| 280 return dummy_mad_.get(); | |
| 281 | |
| 209 MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid); | 282 MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid); |
| 210 if (mad) | 283 if (mad) |
| 211 return mad; | 284 return mad; |
| 212 mad = CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid); | 285 mad = CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid); |
| 213 mad->set_flags(MemoryAllocatorDump::Flags::WEAK); | 286 mad->set_flags(MemoryAllocatorDump::Flags::WEAK); |
| 214 return mad; | 287 return mad; |
| 215 } | 288 } |
| 216 | 289 |
| 217 MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump( | 290 MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump( |
| 218 const MemoryAllocatorDumpGuid& guid) const { | 291 const MemoryAllocatorDumpGuid& guid) const { |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 326 } | 399 } |
| 327 | 400 |
| 328 void ProcessMemoryDump::AddOwnershipEdge( | 401 void ProcessMemoryDump::AddOwnershipEdge( |
| 329 const MemoryAllocatorDumpGuid& source, | 402 const MemoryAllocatorDumpGuid& source, |
| 330 const MemoryAllocatorDumpGuid& target) { | 403 const MemoryAllocatorDumpGuid& target) { |
| 331 AddOwnershipEdge(source, target, 0 /* importance */); | 404 AddOwnershipEdge(source, target, 0 /* importance */); |
| 332 } | 405 } |
| 333 | 406 |
| 334 void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source, | 407 void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source, |
| 335 const std::string& target_node_name) { | 408 const std::string& target_node_name) { |
| 409 // Do not create new dumps for suballocations in background mode. | |
| 410 if (level_of_detail_ == MemoryDumpLevelOfDetail::BACKGROUND) | |
|
Primiano Tucci (use gerrit)
2016/05/27 17:23:35
ditto
ssid
2016/05/27 17:42:48
Why create unnecessary dumps when we are not inter
| |
| 411 return; | |
| 412 | |
| 336 std::string child_mad_name = target_node_name + "/__" + source.ToString(); | 413 std::string child_mad_name = target_node_name + "/__" + source.ToString(); |
| 337 MemoryAllocatorDump* target_child_mad = CreateAllocatorDump(child_mad_name); | 414 MemoryAllocatorDump* target_child_mad = CreateAllocatorDump(child_mad_name); |
| 338 AddOwnershipEdge(source, target_child_mad->guid()); | 415 AddOwnershipEdge(source, target_child_mad->guid()); |
| 339 } | 416 } |
| 340 | 417 |
| 341 } // namespace trace_event | 418 } // namespace trace_event |
| 342 } // namespace base | 419 } // namespace base |
| OLD | NEW |