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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -30,7 +30,9 @@ class MallocHooks : public AllStatic {
static void set_stack_trace_collection_enabled(bool enabled);
static void ResetStats();
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 intptr_t allocation_count();

View file

@ -10,6 +10,8 @@
#include "gperftools/malloc_hook.h"
#include <malloc.h>
#include "platform/assert.h"
#include "vm/hash_map.h"
#include "vm/json_stream.h"
@ -336,29 +338,14 @@ bool MallocHooks::Active() {
return MallocHooksState::Active();
}
void MallocHooks::PrintToJSONObject(JSONObject* jsobj) {
if (!FLAG_profiler_native_memory) {
return;
}
intptr_t allocated_memory = 0;
intptr_t allocation_count = 0;
bool add_usage = false;
// AddProperty may call malloc which would result in an attempt
// 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);
}
bool MallocHooks::GetStats(intptr_t* used,
intptr_t* capacity,
const char** implementation) {
struct mallinfo info = mallinfo();
*used = info.uordblks;
*capacity = *used + info.fordblks;
*implementation = "tcmalloc";
return true;
}
intptr_t MallocHooks::allocation_count() {

View file

@ -8,6 +8,24 @@
#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 {
void MallocHooks::Init() {
@ -38,8 +56,37 @@ bool MallocHooks::Active() {
return false;
}
void MallocHooks::PrintToJSONObject(JSONObject* jsobj) {
// Do nothing.
bool MallocHooks::GetStats(intptr_t* used,
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) {

View file

@ -4329,94 +4329,122 @@ static intptr_t GetProcessMemoryUsageHelper(JSONStream* js) {
rss.AddProperty64("size", Service::CurrentRSS());
JSONArray rss_children(&rss, "children");
JSONObject vm(&rss_children);
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);
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 malloc_used(&malloc_children);
malloc_used.AddProperty("name", "Used");
malloc_used.AddProperty("description", "");
malloc_used.AddProperty64("size", used);
JSONArray(&malloc_used, "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 malloc_free(&malloc_children);
malloc_free.AddProperty("name", "Free");
malloc_free.AddProperty("description", "");
malloc_free.AddProperty64("size", capacity - used);
JSONArray(&malloc_free, "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;
}
@ -4848,7 +4876,15 @@ void Service::PrintJSONForVM(JSONStream* js, bool ref) {
jsobj.AddProperty64("pid", OS::ProcessId());
jsobj.AddPropertyTimeMillis(
"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);
// Construct the isolate and isolate_groups list.
{