Reapply "[vm, service] Gather used and capacity from various mallocs."

Avoid weak linking on iOS.

TEST=ci
Change-Id: Iafcb5bfdea0520d50363486e67031e1d5302d32b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161461
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Ryan Macnak 2021-04-14 21:50:31 +00:00 committed by commit-bot@chromium.org
parent c3c371b971
commit e67dd9c2c5
10 changed files with 239 additions and 141 deletions

View file

@ -174,14 +174,28 @@ class VMViewElement extends CustomElement implements Renderable {
..children = <Element>[ ..children = <Element>[
new DivElement() new DivElement()
..classes = ['memberName'] ..classes = ['memberName']
..text = 'malloc memory', ..text = 'malloc used memory',
new DivElement() new DivElement()
..classes = ['memberValue'] ..classes = ['memberValue']
..text = _vm.heapAllocatedMemoryUsage != null ..text = _vm.mallocUsed != null
? Utils.formatSize(_vm.heapAllocatedMemoryUsage) ? Utils.formatSize(_vm.mallocUsed)
: 'unavailable' : 'unavailable'
..title = _vm.heapAllocatedMemoryUsage != null ..title =
? '${_vm.heapAllocatedMemoryUsage} bytes' _vm.mallocUsed != null ? '${_vm.mallocUsed} bytes' : null
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'malloc capacity memory',
new DivElement()
..classes = ['memberValue']
..text = _vm.mallocCapacity != null
? Utils.formatSize(_vm.mallocCapacity)
: 'unavailable'
..title = _vm.mallocCapacity != null
? '${_vm.mallocCapacity} bytes'
: null : null
], ],
new DivElement() new DivElement()
@ -189,12 +203,10 @@ class VMViewElement extends CustomElement implements Renderable {
..children = <Element>[ ..children = <Element>[
new DivElement() new DivElement()
..classes = ['memberName'] ..classes = ['memberName']
..text = 'malloc allocation count', ..text = 'malloc implementation',
new DivElement() new DivElement()
..classes = ['memberValue'] ..classes = ['memberValue']
..text = _vm.heapAllocationCount != null ..text = _vm.mallocImplementation
? '${_vm.heapAllocationCount}'
: 'unavailable'
], ],
new DivElement() new DivElement()
..classes = ['memberItem'] ..classes = ['memberItem']

View file

@ -35,10 +35,9 @@ abstract class VM implements VMRef {
int get pid; int get pid;
/// The current amount of native heap allocated memory within the VM. /// The current amount of native heap allocated memory within the VM.
int get heapAllocatedMemoryUsage; int get mallocUsed;
int get mallocCapacity;
/// The current number of allocations on the native heap within the VM. String get mallocImplementation;
int get heapAllocationCount;
int get currentMemory; int get currentMemory;
int get maxRSS; int get maxRSS;

View file

@ -680,8 +680,9 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
bool typeChecksEnabled = false; bool typeChecksEnabled = false;
int nativeZoneMemoryUsage = 0; int nativeZoneMemoryUsage = 0;
int pid = 0; int pid = 0;
int heapAllocatedMemoryUsage = 0; int mallocUsed = 0;
int heapAllocationCount = 0; int mallocCapacity = 0;
String mallocImplementation = 'unknown';
int currentMemory = 0; int currentMemory = 0;
int maxRSS = 0; int maxRSS = 0;
int currentRSS = 0; int currentRSS = 0;
@ -1041,8 +1042,9 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
nativeZoneMemoryUsage = map['_nativeZoneMemoryUsage']; nativeZoneMemoryUsage = map['_nativeZoneMemoryUsage'];
} }
pid = map['pid']; pid = map['pid'];
heapAllocatedMemoryUsage = map['_heapAllocatedMemoryUsage']; mallocUsed = map['_mallocUsed'];
heapAllocationCount = map['_heapAllocationCount']; mallocCapacity = map['_mallocCapacity'];
mallocImplementation = map['_mallocImplementation'];
embedder = map['_embedder']; embedder = map['_embedder'];
currentMemory = map['_currentMemory']; currentMemory = map['_currentMemory'];
maxRSS = map['_maxRSS']; maxRSS = map['_maxRSS'];

View file

@ -174,14 +174,28 @@ class VMViewElement extends CustomElement implements Renderable {
..children = <Element>[ ..children = <Element>[
new DivElement() new DivElement()
..classes = ['memberName'] ..classes = ['memberName']
..text = 'malloc memory', ..text = 'malloc used memory',
new DivElement() new DivElement()
..classes = ['memberValue'] ..classes = ['memberValue']
..text = _vm.heapAllocatedMemoryUsage != null ..text = _vm.mallocUsed != null
? Utils.formatSize(_vm.heapAllocatedMemoryUsage) ? Utils.formatSize(_vm.mallocUsed)
: 'unavailable' : 'unavailable'
..title = _vm.heapAllocatedMemoryUsage != null ..title =
? '${_vm.heapAllocatedMemoryUsage} bytes' _vm.mallocUsed != null ? '${_vm.mallocUsed} bytes' : null
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'malloc capacity memory',
new DivElement()
..classes = ['memberValue']
..text = _vm.mallocCapacity != null
? Utils.formatSize(_vm.mallocCapacity)
: 'unavailable'
..title = _vm.mallocCapacity != null
? '${_vm.mallocCapacity} bytes'
: null : null
], ],
new DivElement() new DivElement()
@ -189,12 +203,10 @@ class VMViewElement extends CustomElement implements Renderable {
..children = <Element>[ ..children = <Element>[
new DivElement() new DivElement()
..classes = ['memberName'] ..classes = ['memberName']
..text = 'malloc allocation count', ..text = 'malloc implementation',
new DivElement() new DivElement()
..classes = ['memberValue'] ..classes = ['memberValue']
..text = _vm.heapAllocationCount != null ..text = _vm.mallocImplementation
? '${_vm.heapAllocationCount}'
: 'unavailable'
], ],
new DivElement() new DivElement()
..classes = ['memberItem'] ..classes = ['memberItem']

View file

@ -35,10 +35,9 @@ abstract class VM implements VMRef {
int get pid; int get pid;
/// The current amount of native heap allocated memory within the VM. /// The current amount of native heap allocated memory within the VM.
int get heapAllocatedMemoryUsage; int get mallocUsed;
int get mallocCapacity;
/// The current number of allocations on the native heap within the VM. String get mallocImplementation;
int get heapAllocationCount;
int get currentMemory; int get currentMemory;
int get maxRSS; int get maxRSS;

View file

@ -681,8 +681,9 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
bool typeChecksEnabled = false; bool typeChecksEnabled = false;
int nativeZoneMemoryUsage = 0; int nativeZoneMemoryUsage = 0;
int pid = 0; int pid = 0;
int heapAllocatedMemoryUsage = 0; int mallocUsed = 0;
int heapAllocationCount = 0; int mallocCapacity = 0;
String mallocImplementation = 'unknown';
int currentMemory; int currentMemory;
int maxRSS; int maxRSS;
int currentRSS; int currentRSS;
@ -1044,8 +1045,9 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
nativeZoneMemoryUsage = map['_nativeZoneMemoryUsage']; nativeZoneMemoryUsage = map['_nativeZoneMemoryUsage'];
} }
pid = map['pid']; pid = map['pid'];
heapAllocatedMemoryUsage = map['_heapAllocatedMemoryUsage']; mallocUsed = map['_mallocUsed'];
heapAllocationCount = map['_heapAllocationCount']; mallocCapacity = map['_mallocCapacity'];
mallocImplementation = map['_mallocImplementation'];
embedder = map['_embedder']; embedder = map['_embedder'];
currentMemory = map['_currentMemory']; currentMemory = map['_currentMemory'];
maxRSS = map['_maxRSS']; maxRSS = map['_maxRSS'];

View file

@ -30,7 +30,9 @@ class MallocHooks : public AllStatic {
static void set_stack_trace_collection_enabled(bool enabled); static void set_stack_trace_collection_enabled(bool enabled);
static void ResetStats(); static void ResetStats();
static bool Active(); static bool Active();
static void PrintToJSONObject(JSONObject* jsobj); static bool GetStats(intptr_t* used,
intptr_t* capacity,
const char** implementation);
static Sample* GetSample(const void* ptr); static Sample* GetSample(const void* ptr);
static intptr_t allocation_count(); static intptr_t allocation_count();

View file

@ -10,6 +10,8 @@
#include "gperftools/malloc_hook.h" #include "gperftools/malloc_hook.h"
#include <malloc.h>
#include "platform/assert.h" #include "platform/assert.h"
#include "vm/hash_map.h" #include "vm/hash_map.h"
#include "vm/json_stream.h" #include "vm/json_stream.h"
@ -336,29 +338,14 @@ bool MallocHooks::Active() {
return MallocHooksState::Active(); return MallocHooksState::Active();
} }
void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { bool MallocHooks::GetStats(intptr_t* used,
if (!FLAG_profiler_native_memory) { intptr_t* capacity,
return; const char** implementation) {
} struct mallinfo info = mallinfo();
intptr_t allocated_memory = 0; *used = info.uordblks;
intptr_t allocation_count = 0; *capacity = *used + info.fordblks;
bool add_usage = false; *implementation = "tcmalloc";
// AddProperty may call malloc which would result in an attempt return true;
// to acquire the lock recursively so we extract the values first
// and then add the JSON properties.
{
MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
MallocHooksState::malloc_hook_mutex_owner());
if (MallocHooksState::Active()) {
allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes();
allocation_count = MallocHooksState::allocation_count();
add_usage = true;
}
}
if (add_usage) {
jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory);
jsobj->AddProperty("_heapAllocationCount", allocation_count);
}
} }
intptr_t MallocHooks::allocation_count() { intptr_t MallocHooks::allocation_count() {

View file

@ -8,6 +8,24 @@
#include "vm/malloc_hooks.h" #include "vm/malloc_hooks.h"
#include "vm/json_stream.h"
#if defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID)
#include <malloc.h>
#elif defined(HOST_OS_MACOS)
#include <malloc/malloc.h>
#endif
#if !defined(HOST_OS_WINDOWS) && !defined(TARGET_OS_IOS)
extern "C" {
__attribute__((weak)) uintptr_t __sanitizer_get_current_allocated_bytes();
__attribute__((weak)) uintptr_t __sanitizer_get_heap_size();
__attribute__((weak)) int __sanitizer_install_malloc_and_free_hooks(
void (*malloc_hook)(const void*, uintptr_t),
void (*free_hook)(const void*));
}
#endif
namespace dart { namespace dart {
void MallocHooks::Init() { void MallocHooks::Init() {
@ -38,8 +56,37 @@ bool MallocHooks::Active() {
return false; return false;
} }
void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { bool MallocHooks::GetStats(intptr_t* used,
// Do nothing. intptr_t* capacity,
const char** implementation) {
#if !defined(PRODUCT)
#if !defined(HOST_OS_WINDOWS) && !defined(TARGET_OS_IOS)
if (__sanitizer_get_current_allocated_bytes != nullptr &&
__sanitizer_get_heap_size != nullptr) {
*used = __sanitizer_get_current_allocated_bytes();
*capacity = __sanitizer_get_heap_size();
*implementation = "scudo";
return true;
}
#endif
#if defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID)
struct mallinfo info = mallinfo();
*used = info.uordblks;
*capacity = *used + info.fordblks;
*implementation = "unknown";
return true;
#elif defined(HOST_OS_MACOS)
struct mstats stats = mstats();
*used = stats.bytes_used;
*capacity = stats.bytes_total;
*implementation = "macos";
return true;
#else
return false;
#endif
#else
return false;
#endif
} }
Sample* MallocHooks::GetSample(const void* ptr) { Sample* MallocHooks::GetSample(const void* ptr) {

View file

@ -4329,94 +4329,122 @@ static intptr_t GetProcessMemoryUsageHelper(JSONStream* js) {
rss.AddProperty64("size", Service::CurrentRSS()); rss.AddProperty64("size", Service::CurrentRSS());
JSONArray rss_children(&rss, "children"); JSONArray rss_children(&rss, "children");
JSONObject vm(&rss_children);
intptr_t vm_size = 0; intptr_t vm_size = 0;
{ {
JSONArray vm_children(&vm, "children"); JSONObject vm(&rss_children);
{
JSONArray vm_children(&vm, "children");
{
JSONObject profiler(&vm_children);
profiler.AddProperty("name", "Profiler");
profiler.AddProperty("description",
"Samples from the Dart VM's profiler");
intptr_t size = Profiler::Size();
vm_size += size;
profiler.AddProperty64("size", size);
JSONArray(&profiler, "children");
}
{
JSONObject timeline(&vm_children);
timeline.AddProperty("name", "Timeline");
timeline.AddProperty(
"description",
"Timeline events from dart:developer and Dart_TimelineEvent");
intptr_t size = Timeline::recorder()->Size();
vm_size += size;
timeline.AddProperty64("size", size);
JSONArray(&timeline, "children");
}
{
JSONObject zone(&vm_children);
zone.AddProperty("name", "Zone");
zone.AddProperty("description", "Arena allocation in the Dart VM");
intptr_t size = Zone::Size();
vm_size += size;
zone.AddProperty64("size", size);
JSONArray(&zone, "children");
}
{
JSONObject semi(&vm_children);
semi.AddProperty("name", "SemiSpace Cache");
semi.AddProperty("description", "Cached heap regions");
intptr_t size = SemiSpace::CachedSize();
vm_size += size;
semi.AddProperty64("size", size);
JSONArray(&semi, "children");
}
IsolateGroup::ForEach([&vm_children,
&vm_size](IsolateGroup* isolate_group) {
// Note: new_space()->CapacityInWords() includes memory that hasn't been
// allocated from the OS yet.
int64_t capacity =
(isolate_group->heap()->new_space()->UsedInWords() +
isolate_group->heap()->old_space()->CapacityInWords()) *
kWordSize;
int64_t used = isolate_group->heap()->TotalUsedInWords() * kWordSize;
int64_t free = capacity - used;
JSONObject group(&vm_children);
group.AddPropertyF("name", "IsolateGroup %s",
isolate_group->source()->name);
group.AddProperty("description", "Dart heap capacity");
vm_size += capacity;
group.AddProperty64("size", capacity);
JSONArray group_children(&group, "children");
{
JSONObject jsused(&group_children);
jsused.AddProperty("name", "Used");
jsused.AddProperty("description", "");
jsused.AddProperty64("size", used);
JSONArray(&jsused, "children");
}
{
JSONObject jsfree(&group_children);
jsfree.AddProperty("name", "Free");
jsfree.AddProperty("description", "");
jsfree.AddProperty64("size", free);
JSONArray(&jsfree, "children");
}
});
} // vm_children
vm.AddProperty("name", "Dart VM");
vm.AddProperty("description", "");
vm.AddProperty64("size", vm_size);
}
intptr_t used, capacity;
const char* implementation;
if (MallocHooks::GetStats(&used, &capacity, &implementation)) {
JSONObject malloc(&rss_children);
malloc.AddPropertyF("name", "Malloc (%s)", implementation);
malloc.AddProperty("description", "");
malloc.AddProperty64("size", capacity);
JSONArray malloc_children(&malloc, "children");
{ {
JSONObject profiler(&vm_children); JSONObject malloc_used(&malloc_children);
profiler.AddProperty("name", "Profiler"); malloc_used.AddProperty("name", "Used");
profiler.AddProperty("description", malloc_used.AddProperty("description", "");
"Samples from the Dart VM's profiler"); malloc_used.AddProperty64("size", used);
intptr_t size = Profiler::Size(); JSONArray(&malloc_used, "children");
vm_size += size;
profiler.AddProperty64("size", size);
JSONArray(&profiler, "children");
} }
{ {
JSONObject timeline(&vm_children); JSONObject malloc_free(&malloc_children);
timeline.AddProperty("name", "Timeline"); malloc_free.AddProperty("name", "Free");
timeline.AddProperty( malloc_free.AddProperty("description", "");
"description", malloc_free.AddProperty64("size", capacity - used);
"Timeline events from dart:developer and Dart_TimelineEvent"); JSONArray(&malloc_free, "children");
intptr_t size = Timeline::recorder()->Size();
vm_size += size;
timeline.AddProperty64("size", size);
JSONArray(&timeline, "children");
} }
}
{
JSONObject zone(&vm_children);
zone.AddProperty("name", "Zone");
zone.AddProperty("description", "Arena allocation in the Dart VM");
intptr_t size = Zone::Size();
vm_size += size;
zone.AddProperty64("size", size);
JSONArray(&zone, "children");
}
{
JSONObject semi(&vm_children);
semi.AddProperty("name", "SemiSpace Cache");
semi.AddProperty("description", "Cached heap regions");
intptr_t size = SemiSpace::CachedSize();
vm_size += size;
semi.AddProperty64("size", size);
JSONArray(&semi, "children");
}
IsolateGroup::ForEach(
[&vm_children, &vm_size](IsolateGroup* isolate_group) {
// Note: new_space()->CapacityInWords() includes memory that hasn't
// been allocated from the OS yet.
int64_t capacity =
(isolate_group->heap()->new_space()->UsedInWords() +
isolate_group->heap()->old_space()->CapacityInWords()) *
kWordSize;
int64_t used = isolate_group->heap()->TotalUsedInWords() * kWordSize;
int64_t free = capacity - used;
JSONObject group(&vm_children);
group.AddPropertyF("name", "IsolateGroup %s",
isolate_group->source()->name);
group.AddProperty("description", "Dart heap capacity");
vm_size += capacity;
group.AddProperty64("size", capacity);
JSONArray group_children(&group, "children");
{
JSONObject jsused(&group_children);
jsused.AddProperty("name", "Used");
jsused.AddProperty("description", "");
jsused.AddProperty64("size", used);
JSONArray(&jsused, "children");
}
{
JSONObject jsfree(&group_children);
jsfree.AddProperty("name", "Free");
jsfree.AddProperty("description", "");
jsfree.AddProperty64("size", free);
JSONArray(&jsfree, "children");
}
});
} // vm_children
vm.AddProperty("name", "Dart VM");
vm.AddProperty("description", "");
vm.AddProperty64("size", vm_size);
return vm_size; return vm_size;
} }
@ -4848,7 +4876,15 @@ void Service::PrintJSONForVM(JSONStream* js, bool ref) {
jsobj.AddProperty64("pid", OS::ProcessId()); jsobj.AddProperty64("pid", OS::ProcessId());
jsobj.AddPropertyTimeMillis( jsobj.AddPropertyTimeMillis(
"startTime", OS::GetCurrentTimeMillis() - Dart::UptimeMillis()); "startTime", OS::GetCurrentTimeMillis() - Dart::UptimeMillis());
MallocHooks::PrintToJSONObject(&jsobj); {
intptr_t used, capacity;
const char* implementation;
if (MallocHooks::GetStats(&used, &capacity, &implementation)) {
jsobj.AddProperty("_mallocUsed", used);
jsobj.AddProperty("_mallocCapacity", capacity);
jsobj.AddProperty("_mallocImplementation", implementation);
}
}
PrintJSONForEmbedderInformation(&jsobj); PrintJSONForEmbedderInformation(&jsobj);
// Construct the isolate and isolate_groups list. // Construct the isolate and isolate_groups list.
{ {