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:
Ben Konyi 2016-12-12 09:56:49 -08:00
parent fb2dbfce21
commit 35a49bc1ff
12 changed files with 361 additions and 2 deletions

View file

@ -2090,6 +2090,8 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) {
}
}
}
jsobj.AddProperty("threads", thread_registry_);
}
#endif

View file

@ -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);

View file

@ -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); }

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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());

View file

@ -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_; }

View file

@ -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();

View file

@ -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",

View file

@ -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:

View file

@ -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