mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 15:42:20 +00:00
Created methods to surface zone memory information for each isolate and thread in JSON.
BUG= R=asiva@google.com, johnmccutchan@google.com, zra@google.com Review URL: https://codereview.chromium.org/2554983002 .
This commit is contained in:
parent
fb2dbfce21
commit
35a49bc1ff
|
@ -2090,6 +2090,8 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsobj.AddProperty("threads", thread_registry_);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "vm/safepoint.h"
|
||||
#include "vm/service.h"
|
||||
#include "vm/service_event.h"
|
||||
#include "vm/thread_registry.h"
|
||||
#include "vm/timeline.h"
|
||||
#include "vm/unicode.h"
|
||||
|
||||
|
@ -540,6 +541,24 @@ void JSONStream::PrintValue(Isolate* isolate, bool ref) {
|
|||
}
|
||||
|
||||
|
||||
void JSONStream::PrintValue(ThreadRegistry* reg) {
|
||||
PrintCommaIfNeeded();
|
||||
reg->PrintJSON(this);
|
||||
}
|
||||
|
||||
|
||||
void JSONStream::PrintValue(Thread* thread) {
|
||||
PrintCommaIfNeeded();
|
||||
thread->PrintJSON(this);
|
||||
}
|
||||
|
||||
|
||||
void JSONStream::PrintValue(Zone* zone) {
|
||||
PrintCommaIfNeeded();
|
||||
zone->PrintJSON(this);
|
||||
}
|
||||
|
||||
|
||||
void JSONStream::PrintValue(const TimelineEvent* timeline_event) {
|
||||
PrintCommaIfNeeded();
|
||||
timeline_event->PrintJSON(this);
|
||||
|
@ -663,6 +682,24 @@ void JSONStream::PrintProperty(const char* name, Isolate* isolate) {
|
|||
}
|
||||
|
||||
|
||||
void JSONStream::PrintProperty(const char* name, ThreadRegistry* reg) {
|
||||
PrintPropertyName(name);
|
||||
PrintValue(reg);
|
||||
}
|
||||
|
||||
|
||||
void JSONStream::PrintProperty(const char* name, Thread* thread) {
|
||||
PrintPropertyName(name);
|
||||
PrintValue(thread);
|
||||
}
|
||||
|
||||
|
||||
void JSONStream::PrintProperty(const char* name, Zone* zone) {
|
||||
PrintPropertyName(name);
|
||||
PrintValue(zone);
|
||||
}
|
||||
|
||||
|
||||
void JSONStream::PrintProperty(const char* name,
|
||||
const TimelineEvent* timeline_event) {
|
||||
PrintPropertyName(name);
|
||||
|
|
|
@ -30,6 +30,8 @@ class ServiceEvent;
|
|||
class String;
|
||||
class TimelineEvent;
|
||||
class TimelineEventBlock;
|
||||
class Thread;
|
||||
class ThreadRegistry;
|
||||
class Zone;
|
||||
|
||||
|
||||
|
@ -181,6 +183,9 @@ class JSONStream : ValueObject {
|
|||
void PrintValue(Metric* metric);
|
||||
void PrintValue(MessageQueue* queue);
|
||||
void PrintValue(Isolate* isolate, bool ref = true);
|
||||
void PrintValue(ThreadRegistry* reg);
|
||||
void PrintValue(Thread* thread);
|
||||
void PrintValue(Zone* zone);
|
||||
bool PrintValueStr(const String& s, intptr_t offset, intptr_t count);
|
||||
void PrintValue(const TimelineEvent* timeline_event);
|
||||
void PrintValue(const TimelineEventBlock* timeline_event_block);
|
||||
|
@ -212,6 +217,9 @@ class JSONStream : ValueObject {
|
|||
void PrintProperty(const char* name, Metric* metric);
|
||||
void PrintProperty(const char* name, MessageQueue* queue);
|
||||
void PrintProperty(const char* name, Isolate* isolate);
|
||||
void PrintProperty(const char* name, ThreadRegistry* reg);
|
||||
void PrintProperty(const char* name, Thread* thread);
|
||||
void PrintProperty(const char* name, Zone* zone);
|
||||
void PrintProperty(const char* name, const TimelineEvent* timeline_event);
|
||||
void PrintProperty(const char* name,
|
||||
const TimelineEventBlock* timeline_event_block);
|
||||
|
@ -331,6 +339,15 @@ class JSONObject : public ValueObject {
|
|||
void AddProperty(const char* name, Isolate* isolate) const {
|
||||
stream_->PrintProperty(name, isolate);
|
||||
}
|
||||
void AddProperty(const char* name, ThreadRegistry* reg) const {
|
||||
stream_->PrintProperty(name, reg);
|
||||
}
|
||||
void AddProperty(const char* name, Thread* thread) const {
|
||||
stream_->PrintProperty(name, thread);
|
||||
}
|
||||
void AddProperty(const char* name, Zone* zone) const {
|
||||
stream_->PrintProperty(name, zone);
|
||||
}
|
||||
void AddProperty(const char* name,
|
||||
const TimelineEvent* timeline_event) const {
|
||||
stream_->PrintProperty(name, timeline_event);
|
||||
|
@ -386,6 +403,9 @@ class JSONArray : public ValueObject {
|
|||
void AddValue(Isolate* isolate, bool ref = true) const {
|
||||
stream_->PrintValue(isolate, ref);
|
||||
}
|
||||
void AddValue(ThreadRegistry* reg) const { stream_->PrintValue(reg); }
|
||||
void AddValue(Thread* thread) const { stream_->PrintValue(thread); }
|
||||
void AddValue(Zone* zone) const { stream_->PrintValue(zone); }
|
||||
void AddValue(Breakpoint* bpt) const { stream_->PrintValue(bpt); }
|
||||
void AddValue(TokenPosition tp) const { stream_->PrintValue(tp); }
|
||||
void AddValue(const ServiceEvent* event) const { stream_->PrintValue(event); }
|
||||
|
|
|
@ -3759,13 +3759,12 @@ static bool GetVersion(Thread* thread, JSONStream* js) {
|
|||
class ServiceIsolateVisitor : public IsolateVisitor {
|
||||
public:
|
||||
explicit ServiceIsolateVisitor(JSONArray* jsarr) : jsarr_(jsarr) {}
|
||||
|
||||
virtual ~ServiceIsolateVisitor() {}
|
||||
|
||||
void VisitIsolate(Isolate* isolate) {
|
||||
if ((isolate != Dart::vm_isolate()) &&
|
||||
!ServiceIsolate::IsServiceIsolateDescendant(isolate)) {
|
||||
jsarr_->AddValue(isolate);
|
||||
jsarr_->AddValue(isolate, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "vm/dart_api_state.h"
|
||||
#include "vm/growable_array.h"
|
||||
#include "vm/isolate.h"
|
||||
#include "vm/json_stream.h"
|
||||
#include "vm/lockers.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/message_handler.h"
|
||||
|
@ -205,6 +206,25 @@ void Thread::InitVMConstants() {
|
|||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Collect information about each individual zone associated with this thread.
|
||||
void Thread::PrintJSON(JSONStream* stream) const {
|
||||
JSONObject jsobj(stream);
|
||||
jsobj.AddProperty("type", "_Thread");
|
||||
jsobj.AddPropertyF("id", "threads/%" Pd64 "", os_thread()->trace_id());
|
||||
Zone* zone = zone_;
|
||||
{
|
||||
JSONArray zone_info_array(&jsobj, "zones");
|
||||
zone = zone_;
|
||||
while (zone != NULL) {
|
||||
zone_info_array.AddValue(zone);
|
||||
zone = zone->previous();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
RawGrowableObjectArray* Thread::pending_functions() {
|
||||
if (pending_functions_ == GrowableObjectArray::null()) {
|
||||
pending_functions_ = GrowableObjectArray::New(Heap::kOld);
|
||||
|
|
|
@ -37,6 +37,7 @@ class Library;
|
|||
class LongJumpScope;
|
||||
class Object;
|
||||
class OSThread;
|
||||
class JSONObject;
|
||||
class PcDescriptors;
|
||||
class RawBool;
|
||||
class RawObject;
|
||||
|
@ -646,6 +647,10 @@ class Thread : public BaseThread {
|
|||
|
||||
void InitVMConstants();
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* stream) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
T* AllocateReusableHandle();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "vm/thread_registry.h"
|
||||
|
||||
#include "vm/isolate.h"
|
||||
#include "vm/json_stream.h"
|
||||
#include "vm/lockers.h"
|
||||
|
||||
namespace dart {
|
||||
|
@ -91,6 +92,19 @@ void ThreadRegistry::PrepareForGC() {
|
|||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
void ThreadRegistry::PrintJSON(JSONStream* stream) const {
|
||||
MonitorLocker ml(threads_lock());
|
||||
JSONArray threads(stream);
|
||||
Thread* current = active_list_;
|
||||
while (current != NULL) {
|
||||
threads.AddValue(current);
|
||||
current = current->next_;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void ThreadRegistry::AddToActiveListLocked(Thread* thread) {
|
||||
ASSERT(thread != NULL);
|
||||
ASSERT(threads_lock()->IsOwnedByCurrentThread());
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
#ifndef PRODUCT
|
||||
class JSONStream;
|
||||
class JSONArray;
|
||||
#endif
|
||||
|
||||
// Unordered collection of threads relating to a particular isolate.
|
||||
class ThreadRegistry {
|
||||
public:
|
||||
|
@ -28,6 +33,10 @@ class ThreadRegistry {
|
|||
void PrepareForGC();
|
||||
Thread* mutator_thread() const { return mutator_thread_; }
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* stream) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
Thread* active_list() const { return active_list_; }
|
||||
Monitor* threads_lock() const { return threads_lock_; }
|
||||
|
|
|
@ -205,6 +205,179 @@ VM_TEST_CASE(ManyTasksWithZones) {
|
|||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
class SimpleTaskWithZoneAllocation : public ThreadPool::Task {
|
||||
public:
|
||||
SimpleTaskWithZoneAllocation(intptr_t id,
|
||||
Isolate* isolate,
|
||||
Thread** thread_ptr,
|
||||
Monitor* sync,
|
||||
Monitor* monitor,
|
||||
intptr_t* done_count,
|
||||
bool* wait)
|
||||
: id_(id),
|
||||
isolate_(isolate),
|
||||
thread_ptr_(thread_ptr),
|
||||
sync_(sync),
|
||||
monitor_(monitor),
|
||||
done_count_(done_count),
|
||||
wait_(wait) {}
|
||||
|
||||
virtual void Run() {
|
||||
Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask);
|
||||
{
|
||||
Thread* thread = Thread::Current();
|
||||
*thread_ptr_ = thread;
|
||||
CreateStackZones(id_);
|
||||
}
|
||||
Thread::ExitIsolateAsHelper();
|
||||
// Notify the main thread that this thread has exited.
|
||||
{
|
||||
MonitorLocker ml(monitor_);
|
||||
*done_count_ += 1;
|
||||
ml.Notify();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateStackZones(intptr_t num) {
|
||||
Thread* thread = Thread::Current();
|
||||
*thread_ptr_ = thread;
|
||||
|
||||
StackZone stack_zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Zone* zone = thread->zone();
|
||||
EXPECT_EQ(zone, stack_zone.GetZone());
|
||||
|
||||
// Create a zone (which is also a stack resource) and exercise it a bit.
|
||||
ZoneGrowableArray<bool>* a0 = new (zone) ZoneGrowableArray<bool>(zone, 1);
|
||||
GrowableArray<bool> a1(zone, 1);
|
||||
for (intptr_t i = 0; i < 1000 * num + id_; ++i) {
|
||||
a0->Add(true);
|
||||
a1.Add(true);
|
||||
}
|
||||
|
||||
num -= 1;
|
||||
if (num != 0) {
|
||||
CreateStackZones(num);
|
||||
return;
|
||||
}
|
||||
{
|
||||
// Let the main thread know we're done with memory ops on this thread.
|
||||
MonitorLocker ml(monitor_);
|
||||
*done_count_ += 1;
|
||||
ml.Notify();
|
||||
}
|
||||
// Wait for the go-ahead from the main thread to exit.
|
||||
{
|
||||
MonitorLocker sync_ml(sync_);
|
||||
while (*wait_) {
|
||||
sync_ml.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t id_;
|
||||
Isolate* isolate_;
|
||||
Thread** thread_ptr_;
|
||||
Monitor* sync_;
|
||||
Monitor* monitor_;
|
||||
intptr_t* done_count_;
|
||||
bool* wait_;
|
||||
};
|
||||
|
||||
|
||||
TEST_CASE(ManySimpleTasksWithZones) {
|
||||
const int kTaskCount = 10;
|
||||
Monitor monitor;
|
||||
Monitor sync;
|
||||
Thread* threads[kTaskCount + 1];
|
||||
Isolate* isolate = Thread::Current()->isolate();
|
||||
intptr_t done_count = 0;
|
||||
bool wait = true;
|
||||
threads[kTaskCount] = Thread::Current();
|
||||
|
||||
EXPECT(isolate->heap()->GrowthControlState());
|
||||
isolate->heap()->DisableGrowthControl();
|
||||
for (intptr_t i = 0; i < kTaskCount; i++) {
|
||||
Dart::thread_pool()->Run(new SimpleTaskWithZoneAllocation(
|
||||
(i + 1), isolate, &threads[i], &sync, &monitor, &done_count, &wait));
|
||||
}
|
||||
// Wait until all spawned tasks finish their memory operations.
|
||||
{
|
||||
MonitorLocker ml(&monitor);
|
||||
while (done_count < kTaskCount) {
|
||||
ml.Wait();
|
||||
}
|
||||
// Reset the done counter for use later.
|
||||
done_count = 0;
|
||||
}
|
||||
|
||||
JSONStream stream;
|
||||
Service::PrintJSONForVM(&stream, false);
|
||||
const char* json = stream.ToCString();
|
||||
|
||||
// Confirm all expected entries are in the JSON output.
|
||||
for (intptr_t i = 0; i < kTaskCount + 1; i++) {
|
||||
Thread* thread = threads[i];
|
||||
Isolate* thread_isolate = thread->isolate();
|
||||
// Buffer can handle any possible input length given types.
|
||||
// char thread_address_buf[96];
|
||||
// char isolate_address_buf[64];
|
||||
Zone* top_zone = thread->zone();
|
||||
|
||||
Thread* current_thread = Thread::Current();
|
||||
StackZone stack_zone(current_thread);
|
||||
Zone* current_zone = current_thread->zone();
|
||||
|
||||
// Check that all zones are present with correct sizes.
|
||||
while (top_zone != NULL) {
|
||||
char* zone_info_buf =
|
||||
OS::SCreate(current_zone,
|
||||
"\"type\":\"_Zone\","
|
||||
"\"capacity\":%ld,"
|
||||
"\"used\":%ld",
|
||||
top_zone->SizeInBytes(), top_zone->UsedSizeInBytes());
|
||||
EXPECT_SUBSTRING(zone_info_buf, json);
|
||||
top_zone = top_zone->previous();
|
||||
}
|
||||
|
||||
// Check the thread exists and is the correct size.
|
||||
char* thread_info_buf = OS::SCreate(current_zone,
|
||||
"\"type\":\"_Thread\","
|
||||
"\"id\":\"threads\\/%" Pd64 "",
|
||||
thread->os_thread()->trace_id());
|
||||
|
||||
// Ensure the isolate for each thread is valid.
|
||||
|
||||
char* isolate_info_buf =
|
||||
OS::SCreate(current_zone,
|
||||
"\"type\":\"Isolate\","
|
||||
"\"fixedId\":true,"
|
||||
"\"id\":\"isolates\\/%" Pd64 "",
|
||||
static_cast<int64_t>(thread_isolate->main_port()));
|
||||
|
||||
EXPECT_SUBSTRING(thread_info_buf, json);
|
||||
EXPECT_SUBSTRING(isolate_info_buf, json);
|
||||
}
|
||||
|
||||
// Unblock the tasks so they can finish.
|
||||
{
|
||||
MonitorLocker sync_ml(&sync);
|
||||
wait = false;
|
||||
sync_ml.NotifyAll();
|
||||
}
|
||||
// Now wait for them all to exit before destroying the isolate.
|
||||
{
|
||||
MonitorLocker ml(&monitor);
|
||||
while (done_count < kTaskCount) {
|
||||
ml.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
TEST_CASE(ThreadRegistry) {
|
||||
Isolate* orig = Thread::Current()->isolate();
|
||||
Zone* orig_zone = Thread::Current()->zone();
|
||||
|
|
|
@ -260,6 +260,18 @@ char* Zone::VPrint(const char* format, va_list args) {
|
|||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
void Zone::PrintJSON(JSONStream* stream) const {
|
||||
JSONObject jsobj(stream);
|
||||
intptr_t capacity = SizeInBytes();
|
||||
intptr_t used_size = UsedSizeInBytes();
|
||||
jsobj.AddProperty("type", "_Zone");
|
||||
jsobj.AddProperty("capacity", capacity);
|
||||
jsobj.AddProperty("used", used_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
StackZone::StackZone(Thread* thread) : StackResource(thread), zone_() {
|
||||
if (FLAG_trace_zones) {
|
||||
OS::PrintErr("*** Starting a new Stack zone 0x%" Px "(0x%" Px ")\n",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "platform/utils.h"
|
||||
#include "vm/allocation.h"
|
||||
#include "vm/handles.h"
|
||||
#include "vm/json_stream.h"
|
||||
#include "vm/thread.h"
|
||||
#include "vm/memory_region.h"
|
||||
|
||||
|
@ -65,6 +66,11 @@ class Zone {
|
|||
// due to internal fragmentation in the segments.
|
||||
intptr_t SizeInBytes() const;
|
||||
|
||||
// Computes the amount of space used in the zone.
|
||||
intptr_t UsedSizeInBytes() const {
|
||||
return SizeInBytes() - (limit_ - position_);
|
||||
}
|
||||
|
||||
// Structure for managing handles allocation.
|
||||
VMHandles* handles() { return &handles_; }
|
||||
|
||||
|
@ -72,6 +78,10 @@ class Zone {
|
|||
|
||||
Zone* previous() const { return previous_; }
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* stream) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
Zone();
|
||||
~Zone(); // Delete all memory associated with the zone.
|
||||
|
@ -174,6 +184,9 @@ class StackZone : public StackResource {
|
|||
// due to internal fragmentation in the segments.
|
||||
intptr_t SizeInBytes() const { return zone_.SizeInBytes(); }
|
||||
|
||||
// Computes the used space in the zone.
|
||||
intptr_t UsedSizeInBytes() const { return zone_.UsedSizeInBytes(); }
|
||||
|
||||
Zone* GetZone() { return &zone_; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -169,4 +169,59 @@ TEST_CASE(PrintToString) {
|
|||
EXPECT_STREQ("Hello World!", result);
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
UNIT_TEST_CASE(PrintZoneMemoryInfoToJSON) {
|
||||
#if defined(DEBUG)
|
||||
FLAG_trace_zones = true;
|
||||
#endif
|
||||
Dart_CreateIsolate(NULL, NULL, bin::isolate_snapshot_buffer, NULL, NULL,
|
||||
NULL);
|
||||
Thread* thread = Thread::Current();
|
||||
EXPECT(thread->zone() == NULL);
|
||||
{
|
||||
StackZone zone(thread);
|
||||
StackZone string_stack_zone(thread);
|
||||
EXPECT(thread->zone() != NULL);
|
||||
|
||||
intptr_t allocated_size = 0;
|
||||
const intptr_t kNumElements = 1000;
|
||||
|
||||
zone.GetZone()->Alloc<uint32_t>(kNumElements);
|
||||
allocated_size += sizeof(uint32_t) * kNumElements;
|
||||
|
||||
EXPECT_LE(allocated_size, zone.SizeInBytes());
|
||||
{
|
||||
JSONStream stream;
|
||||
// Get the JSON formated zone information.
|
||||
zone.GetZone()->PrintJSON(&stream);
|
||||
const char* json = stream.ToCString();
|
||||
// Ensure that matches actual values.
|
||||
char* size_buf = OS::SCreate(string_stack_zone.GetZone(),
|
||||
"\"capacity\":%ld,"
|
||||
"\"used\":%ld",
|
||||
zone.SizeInBytes(), zone.UsedSizeInBytes());
|
||||
EXPECT_SUBSTRING(size_buf, json);
|
||||
}
|
||||
|
||||
// Expand the zone to ensure that JSON is updated accordingly.
|
||||
zone.GetZone()->Alloc<uint32_t>(kNumElements);
|
||||
allocated_size += sizeof(uint32_t) * kNumElements;
|
||||
EXPECT_LE(allocated_size, zone.SizeInBytes());
|
||||
{
|
||||
JSONStream stream;
|
||||
zone.GetZone()->PrintJSON(&stream);
|
||||
const char* json = stream.ToCString();
|
||||
char* size_buf = OS::SCreate(string_stack_zone.GetZone(),
|
||||
"\"capacity\":%ld,"
|
||||
"\"used\":%ld",
|
||||
zone.SizeInBytes(), zone.UsedSizeInBytes());
|
||||
EXPECT_SUBSTRING(size_buf, json);
|
||||
}
|
||||
}
|
||||
EXPECT(thread->zone() == NULL);
|
||||
Dart_ShutdownIsolate();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace dart
|
||||
|
|
Loading…
Reference in a new issue