Files
sdk/runtime/vm/json_stream.cc
T
Tess Strickland e2df4d30a0 [vm] Remove non-private uses of TokenPosition::value().
Instead, split each old use into the following cases:

* If the TokenPosition value is expected to be a real token position,
  then use TokenPosition::Pos().

* If the TokenPosition is being serialized in some way, then use
  TokenPosition::Serialize() and change the place where the
  TokenPosition is recreated to use TokenPosition::Deserialize().

* If the value of the TokenPosition is being printed for debugging
  purposes, then just use TokenPosition::ToCString() instead.

That is, we try to pin down when token positions are expected to
be real vs. when other types of token positions can be found.

Another source of possible error when using token positions is to
convert between synthetic and real token positions. In the past,
synthetic token positions may have been based off real token positions,
but that is no longer the case. Thus, all methods that allow
that conversion have been removed, and instead there is a new static
method for constructing synthetic tokens from valid nonces.

This CL also makes it so that Pos() and relational operators on token
positions are only defined on real token positions, to avoid any
assumptions about what the value encoded in synthetic positions mean. To
help with cases where non-real token positions may occur, four helper
methods are added:

* TokenPosition::Min(a, b): A static method that returns the smallest
  real token position provided. If neither `a` or `b` are real,
  returns `a`.

* TokenPosition::Max(a, b): A static method that returns the largest
  real token position provided. If neither `a` or `b` are real,
  returns `a`.

* TokenPosition::IsWithin(start, end): Determines whether `this` falls
  between `start` and `end` (inclusive). If `this` is non-real, then it
  must be either `start` or `end` if synthetic, otherwise false.
  Otherwise, we mimic the old style of range checking, which means that
  non-real starts and ends are treated as less than every real token.

* TokenPosition::CompareForSorting(other): Unlike the relational
  operators, provides a comparison between any types of token positions
  for purposes such as sorting.  Currently only used in the profiler.

It also changes TokenPosition::ToCString() to tag synthetic token
positions, so they can be distinguished from real ones at a glance.

TEST=Existing test suite on trybots, especially the observatory tests
which make heavy use of the debugger and the unit tests for the
profiler/source report modules.

Bug: https://github.com/dart-lang/sdk/issues/44436

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try
Change-Id: Ic06aa0bc7a1f0fbac7257ed22ca5e7e0ccd7f3f2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/174924
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
2020-12-16 08:27:32 +00:00

564 lines
16 KiB
C++

// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/assert.h"
#include "include/dart_native_api.h"
#include "platform/unicode.h"
#include "vm/dart_entry.h"
#include "vm/debugger.h"
#include "vm/heap/safepoint.h"
#include "vm/json_stream.h"
#include "vm/message.h"
#include "vm/metrics.h"
#include "vm/object.h"
#include "vm/service.h"
#include "vm/service_event.h"
#include "vm/thread_registry.h"
#include "vm/timeline.h"
namespace dart {
#ifndef PRODUCT
DECLARE_FLAG(bool, trace_service);
JSONStream::JSONStream(intptr_t buf_size)
: writer_(buf_size),
default_id_zone_(),
id_zone_(&default_id_zone_),
reply_port_(ILLEGAL_PORT),
seq_(NULL),
parameter_keys_(NULL),
parameter_values_(NULL),
method_(""),
param_keys_(NULL),
param_values_(NULL),
num_params_(0),
offset_(0),
count_(-1) {
ObjectIdRing* ring = NULL;
Isolate* isolate = Isolate::Current();
if (isolate != NULL) {
ring = isolate->EnsureObjectIdRing();
}
default_id_zone_.Init(ring, ObjectIdRing::kAllocateId);
}
void JSONStream::Setup(Zone* zone,
Dart_Port reply_port,
const Instance& seq,
const String& method,
const Array& param_keys,
const Array& param_values,
bool parameters_are_dart_objects) {
set_reply_port(reply_port);
seq_ = &Instance::ZoneHandle(seq.raw());
method_ = method.ToCString();
if (parameters_are_dart_objects) {
parameter_keys_ = &Array::ZoneHandle(param_keys.raw());
parameter_values_ = &Array::ZoneHandle(param_values.raw());
ASSERT(parameter_keys_->Length() == parameter_values_->Length());
} else if (param_keys.Length() > 0) {
String& string_iterator = String::Handle();
ASSERT(param_keys.Length() == param_values.Length());
const char** param_keys_native =
zone->Alloc<const char*>(param_keys.Length());
const char** param_values_native =
zone->Alloc<const char*>(param_keys.Length());
for (intptr_t i = 0; i < param_keys.Length(); i++) {
string_iterator ^= param_keys.At(i);
param_keys_native[i] =
zone->MakeCopyOfString(string_iterator.ToCString());
string_iterator ^= param_values.At(i);
param_values_native[i] =
zone->MakeCopyOfString(string_iterator.ToCString());
}
SetParams(param_keys_native, param_values_native, param_keys.Length());
}
if (FLAG_trace_service) {
Isolate* isolate = Isolate::Current();
ASSERT(isolate != NULL);
int64_t main_port = static_cast<int64_t>(isolate->main_port());
const char* isolate_name = isolate->name();
setup_time_micros_ = OS::GetCurrentTimeMicros();
OS::PrintErr("[+%" Pd64 "ms] Isolate (%" Pd64
") %s processing service "
"request %s\n",
Dart::UptimeMillis(), main_port, isolate_name, method_);
}
buffer()->Printf("{\"jsonrpc\":\"2.0\", \"result\":");
}
void JSONStream::SetupError() {
Clear();
buffer()->Printf("{\"jsonrpc\":\"2.0\", \"error\":");
}
static const char* GetJSONRpcErrorMessage(intptr_t code) {
switch (code) {
case kParseError:
return "Parse error";
case kInvalidRequest:
return "Invalid Request";
case kMethodNotFound:
return "Method not found";
case kInvalidParams:
return "Invalid params";
case kInternalError:
return "Internal error";
case kFeatureDisabled:
return "Feature is disabled";
case kCannotAddBreakpoint:
return "Cannot add breakpoint";
case kStreamAlreadySubscribed:
return "Stream already subscribed";
case kStreamNotSubscribed:
return "Stream not subscribed";
case kIsolateMustBeRunnable:
return "Isolate must be runnable";
case kIsolateMustBePaused:
return "Isolate must be paused";
case kCannotResume:
return "Cannot resume execution";
case kIsolateIsReloading:
return "Isolate is reloading";
case kIsolateReloadBarred:
return "Isolate cannot be reloaded";
case kIsolateMustHaveReloaded:
return "Isolate must have reloaded";
case kFileSystemAlreadyExists:
return "File system already exists";
case kFileSystemDoesNotExist:
return "File system does not exist";
case kFileDoesNotExist:
return "File does not exist";
case kInvalidTimelineRequest:
return "The timeline related request could not be completed due to the "
"current configuration";
default:
return "Extension error";
}
}
static void PrintRequest(JSONObject* obj, JSONStream* js) {
JSONObject jsobj(obj, "request");
jsobj.AddProperty("method", js->method());
{
JSONObject params(&jsobj, "params");
for (intptr_t i = 0; i < js->num_params(); i++) {
params.AddProperty(js->GetParamKey(i), js->GetParamValue(i));
}
}
}
void JSONStream::PrintError(intptr_t code, const char* details_format, ...) {
SetupError();
JSONObject jsobj(this);
jsobj.AddProperty("code", code);
jsobj.AddProperty("message", GetJSONRpcErrorMessage(code));
{
JSONObject data(&jsobj, "data");
PrintRequest(&data, this);
if (details_format != NULL) {
va_list args;
va_start(args, details_format);
intptr_t len = Utils::VSNPrint(NULL, 0, details_format, args);
va_end(args);
char* buffer = Thread::Current()->zone()->Alloc<char>(len + 1);
va_list args2;
va_start(args2, details_format);
Utils::VSNPrint(buffer, (len + 1), details_format, args2);
va_end(args2);
data.AddProperty("details", buffer);
}
}
}
void JSONStream::PostNullReply(Dart_Port port) {
PortMap::PostMessage(
Message::New(port, Object::null(), Message::kNormalPriority));
}
static void Finalizer(void* isolate_callback_data, void* buffer) {
free(buffer);
}
void JSONStream::PostReply() {
ASSERT(seq_ != NULL);
Dart_Port port = reply_port();
set_reply_port(ILLEGAL_PORT); // Prevent double replies.
if (seq_->IsString()) {
const String& str = String::Cast(*seq_);
PrintProperty("id", str.ToCString());
} else if (seq_->IsInteger()) {
const Integer& integer = Integer::Cast(*seq_);
PrintProperty64("id", integer.AsInt64Value());
} else if (seq_->IsDouble()) {
const Double& dbl = Double::Cast(*seq_);
PrintProperty("id", dbl.value());
} else if (seq_->IsNull()) {
if (port == ILLEGAL_PORT) {
// This path is only used in tests.
buffer()->AddChar('}'); // Finish our message.
char* cstr;
intptr_t length;
Steal(&cstr, &length);
OS::PrintErr("-----\nDropping reply:\n%s\n-----\n", cstr);
free(cstr);
}
// JSON-RPC 2.0 says that a request with a null ID shouldn't get a reply.
PostNullReply(port);
return;
}
ASSERT(port != ILLEGAL_PORT);
buffer()->AddChar('}'); // Finish our message.
char* cstr;
intptr_t length;
Steal(&cstr, &length);
bool result;
{
TransitionVMToNative transition(Thread::Current());
Dart_CObject bytes;
bytes.type = Dart_CObject_kExternalTypedData;
bytes.value.as_external_typed_data.type = Dart_TypedData_kUint8;
bytes.value.as_external_typed_data.length = length;
bytes.value.as_external_typed_data.data = reinterpret_cast<uint8_t*>(cstr);
bytes.value.as_external_typed_data.peer = cstr;
bytes.value.as_external_typed_data.callback = Finalizer;
Dart_CObject* elements[1];
elements[0] = &bytes;
Dart_CObject message;
message.type = Dart_CObject_kArray;
message.value.as_array.length = 1;
message.value.as_array.values = elements;
result = Dart_PostCObject(port, &message);
}
if (!result) {
free(cstr);
}
if (FLAG_trace_service) {
Isolate* isolate = Isolate::Current();
ASSERT(isolate != NULL);
int64_t main_port = static_cast<int64_t>(isolate->main_port());
const char* isolate_name = isolate->name();
int64_t total_time = OS::GetCurrentTimeMicros() - setup_time_micros_;
if (result) {
OS::PrintErr("[+%" Pd64 "ms] Isolate (%" Pd64
") %s processed service request %s (%" Pd64 "us)\n",
Dart::UptimeMillis(), main_port, isolate_name, method_,
total_time);
} else {
OS::PrintErr("[+%" Pd64 "ms] Isolate (%" Pd64
") %s processed service request %s (%" Pd64 "us) FAILED\n",
Dart::UptimeMillis(), main_port, isolate_name, method_,
total_time);
}
}
}
const char* JSONStream::LookupParam(const char* key) const {
for (int i = 0; i < num_params(); i++) {
if (strcmp(key, param_keys_[i]) == 0) {
return param_values_[i];
}
}
return NULL;
}
bool JSONStream::HasParam(const char* key) const {
ASSERT(key);
return LookupParam(key) != NULL;
}
bool JSONStream::ParamIs(const char* key, const char* value) const {
ASSERT(key);
ASSERT(value);
const char* key_value = LookupParam(key);
return (key_value != NULL) && (strcmp(key_value, value) == 0);
}
void JSONStream::ComputeOffsetAndCount(intptr_t length,
intptr_t* offset,
intptr_t* count) {
// This function is written to avoid adding (count + offset) in case
// that triggers an integer overflow.
*offset = offset_;
if (*offset > length) {
*offset = length;
}
intptr_t remaining = length - *offset;
*count = count_;
if (*count < 0 || *count > remaining) {
*count = remaining;
}
}
void JSONStream::PrintfValue(const char* format, ...) {
va_list args;
va_start(args, format);
VPrintfValue(format, args);
va_end(args);
}
void JSONStream::PrintValue(const Object& o, bool ref) {
PrintCommaIfNeeded();
o.PrintJSON(this, ref);
}
void JSONStream::PrintValue(Breakpoint* bpt) {
PrintCommaIfNeeded();
bpt->PrintJSON(this);
}
void JSONStream::PrintValue(TokenPosition tp) {
PrintCommaIfNeeded();
PrintValue(static_cast<intptr_t>(tp.Serialize()));
}
void JSONStream::PrintValue(const ServiceEvent* event) {
PrintCommaIfNeeded();
event->PrintJSON(this);
}
void JSONStream::PrintValue(Metric* metric) {
PrintCommaIfNeeded();
metric->PrintJSON(this);
}
void JSONStream::PrintValue(MessageQueue* queue) {
PrintCommaIfNeeded();
queue->PrintJSON(this);
}
void JSONStream::PrintValue(Isolate* isolate, bool ref) {
PrintCommaIfNeeded();
isolate->PrintJSON(this, ref);
}
void JSONStream::PrintValue(IsolateGroup* isolate_group, bool ref) {
PrintCommaIfNeeded();
isolate_group->PrintJSON(this, ref);
}
void JSONStream::PrintValue(const TimelineEvent* timeline_event) {
PrintCommaIfNeeded();
timeline_event->PrintJSON(this);
}
void JSONStream::PrintValue(const TimelineEventBlock* timeline_event_block) {
PrintCommaIfNeeded();
timeline_event_block->PrintJSON(this);
}
void JSONStream::PrintValueVM(bool ref) {
PrintCommaIfNeeded();
Service::PrintJSONForVM(this, ref);
}
void JSONStream::PrintServiceId(const Object& o) {
ASSERT(id_zone_ != NULL);
PrintProperty("id", id_zone_->GetServiceId(o));
}
void JSONStream::PrintProperty(const char* name, const ServiceEvent* event) {
PrintPropertyName(name);
PrintValue(event);
}
void JSONStream::PrintProperty(const char* name, Breakpoint* bpt) {
PrintPropertyName(name);
PrintValue(bpt);
}
void JSONStream::PrintProperty(const char* name, TokenPosition tp) {
PrintPropertyName(name);
PrintValue(tp);
}
void JSONStream::PrintProperty(const char* name, Metric* metric) {
PrintPropertyName(name);
PrintValue(metric);
}
void JSONStream::PrintProperty(const char* name, MessageQueue* queue) {
PrintPropertyName(name);
PrintValue(queue);
}
void JSONStream::PrintProperty(const char* name, Isolate* isolate) {
PrintPropertyName(name);
PrintValue(isolate);
}
void JSONStream::PrintProperty(const char* name,
const TimelineEvent* timeline_event) {
PrintPropertyName(name);
PrintValue(timeline_event);
}
void JSONStream::PrintProperty(const char* name,
const TimelineEventBlock* timeline_event_block) {
PrintPropertyName(name);
PrintValue(timeline_event_block);
}
void JSONStream::PrintfProperty(const char* name, const char* format, ...) {
va_list args;
va_start(args, format);
writer_.VPrintfProperty(name, format, args);
va_end(args);
}
void JSONStream::set_reply_port(Dart_Port port) {
reply_port_ = port;
}
intptr_t JSONStream::NumObjectParameters() const {
if (parameter_keys_ == NULL) {
return 0;
}
ASSERT(parameter_keys_ != NULL);
ASSERT(parameter_values_ != NULL);
return parameter_keys_->Length();
}
ObjectPtr JSONStream::GetObjectParameterKey(intptr_t i) const {
ASSERT((i >= 0) && (i < NumObjectParameters()));
return parameter_keys_->At(i);
}
ObjectPtr JSONStream::GetObjectParameterValue(intptr_t i) const {
ASSERT((i >= 0) && (i < NumObjectParameters()));
return parameter_values_->At(i);
}
ObjectPtr JSONStream::LookupObjectParam(const char* c_key) const {
const String& key = String::Handle(String::New(c_key));
Object& test = Object::Handle();
const intptr_t num_object_parameters = NumObjectParameters();
for (intptr_t i = 0; i < num_object_parameters; i++) {
test = GetObjectParameterKey(i);
if (test.IsString() && String::Cast(test).Equals(key)) {
return GetObjectParameterValue(i);
}
}
return Object::null();
}
void JSONStream::SetParams(const char** param_keys,
const char** param_values,
intptr_t num_params) {
param_keys_ = param_keys;
param_values_ = param_values;
num_params_ = num_params;
}
void JSONStream::PrintProperty(const char* name, const Object& o, bool ref) {
PrintPropertyName(name);
PrintValue(o, ref);
}
void JSONStream::PrintPropertyVM(const char* name, bool ref) {
PrintPropertyName(name);
PrintValueVM(ref);
}
JSONObject::JSONObject(const JSONArray* arr) : stream_(arr->stream_) {
stream_->OpenObject();
}
void JSONObject::AddFixedServiceId(const char* format, ...) const {
// Mark that this id is fixed.
AddProperty("fixedId", true);
// Add the id property.
va_list args;
va_start(args, format);
stream_->VPrintfProperty("id", format, args);
va_end(args);
}
void JSONObject::AddServiceId(const char* format, ...) const {
// Add the id property.
va_list args;
va_start(args, format);
stream_->VPrintfProperty("id", format, args);
va_end(args);
}
void JSONObject::AddLocation(const Script& script,
TokenPosition token_pos,
TokenPosition end_token_pos) const {
JSONObject location(this, "location");
location.AddProperty("type", "SourceLocation");
location.AddProperty("script", script);
location.AddProperty("tokenPos", token_pos);
if (end_token_pos.IsReal()) {
location.AddProperty("endTokenPos", end_token_pos);
}
}
void JSONObject::AddLocation(const BreakpointLocation* bpt_loc) const {
ASSERT(bpt_loc->IsResolved());
Zone* zone = Thread::Current()->zone();
Script& script = Script::Handle(zone);
TokenPosition token_pos = TokenPosition::kNoSource;
bpt_loc->GetCodeLocation(&script, &token_pos);
AddLocation(script, token_pos);
}
void JSONObject::AddUnresolvedLocation(
const BreakpointLocation* bpt_loc) const {
ASSERT(!bpt_loc->IsResolved());
Zone* zone = Thread::Current()->zone();
Script& script = Script::Handle(zone);
TokenPosition token_pos = TokenPosition::kNoSource;
bpt_loc->GetCodeLocation(&script, &token_pos);
JSONObject location(this, "location");
location.AddProperty("type", "UnresolvedSourceLocation");
if (!script.IsNull()) {
location.AddProperty("script", script);
} else {
const String& scriptUri = String::Handle(zone, bpt_loc->url());
location.AddPropertyStr("scriptUri", scriptUri);
}
if (bpt_loc->requested_line_number() >= 0) {
// This unresolved breakpoint was specified at a particular line.
location.AddProperty("line", bpt_loc->requested_line_number());
if (bpt_loc->requested_column_number() >= 0) {
location.AddProperty("column", bpt_loc->requested_column_number());
}
} else {
// This unresolved breakpoint was requested at some function entry.
location.AddProperty("tokenPos", token_pos);
}
}
void JSONObject::AddPropertyF(const char* name, const char* format, ...) const {
va_list args;
va_start(args, format);
stream_->VPrintfProperty(name, format, args);
va_end(args);
}
void JSONArray::AddValueF(const char* format, ...) const {
va_list args;
va_start(args, format);
stream_->VPrintfValue(format, args);
va_end(args);
}
#endif // !PRODUCT
} // namespace dart