Show flags in Observatory.

Add vm support for setting flags through vm service (UI coming soon).
e.g.
  http://127.0.0.1:8181/#/flags/set?name=some_flag&value=100

--

I reworked the Flags class to use a growing array rather than a linked list to store flags.  This means that we don't need to go through the hassle of building an array and sorting every time that we print/printjson.

R=johnmccutchan@google.com

Review URL: https://codereview.chromium.org//299143007

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@36712 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
turnidge@google.com 2014-05-27 21:27:17 +00:00
parent 2b06c73841
commit 8db11b79ac
23 changed files with 6780 additions and 4576 deletions

File diff suppressed because it is too large Load diff

View file

@ -1 +1 @@
{"experimental_bootstrap":false,"script_ids":[["observatory","lib/src/elements/curly_block.dart"],["observatory","lib/src/elements/observatory_element.dart"],["observatory","lib/src/elements/service_ref.dart"],["observatory","lib/src/elements/instance_ref.dart"],["observatory","lib/src/elements/action_link.dart"],["observatory","lib/src/elements/nav_bar.dart"],["observatory","lib/src/elements/breakpoint_list.dart"],["observatory","lib/src/elements/class_ref.dart"],["observatory","lib/src/elements/eval_box.dart"],["observatory","lib/src/elements/eval_link.dart"],["observatory","lib/src/elements/field_ref.dart"],["observatory","lib/src/elements/function_ref.dart"],["observatory","lib/src/elements/library_ref.dart"],["observatory","lib/src/elements/script_ref.dart"],["observatory","lib/src/elements/class_view.dart"],["observatory","lib/src/elements/code_ref.dart"],["observatory","lib/src/elements/code_view.dart"],["observatory","lib/src/elements/collapsible_content.dart"],["observatory","lib/src/elements/error_view.dart"],["observatory","lib/src/elements/field_view.dart"],["observatory","lib/src/elements/script_inset.dart"],["observatory","lib/src/elements/function_view.dart"],["observatory","lib/src/elements/heap_map.dart"],["observatory","lib/src/elements/io_view.dart"],["observatory","lib/src/elements/isolate_ref.dart"],["observatory","lib/src/elements/isolate_summary.dart"],["observatory","lib/src/elements/isolate_view.dart"],["observatory","lib/src/elements/instance_view.dart"],["observatory","lib/src/elements/json_view.dart"],["observatory","lib/src/elements/library_view.dart"],["observatory","lib/src/elements/heap_profile.dart"],["observatory","lib/src/elements/sliding_checkbox.dart"],["observatory","lib/src/elements/isolate_profile.dart"],["observatory","lib/src/elements/script_view.dart"],["observatory","lib/src/elements/stack_frame.dart"],["observatory","lib/src/elements/stack_trace.dart"],["observatory","lib/src/elements/vm_view.dart"],["observatory","lib/src/elements/service_view.dart"],["observatory","lib/src/elements/response_viewer.dart"],["observatory","lib/src/elements/observatory_application.dart"],["observatory","lib/src/elements/service_exception_view.dart"],["observatory","lib/src/elements/service_error_view.dart"],["observatory","lib/src/elements/vm_ref.dart"],["observatory","web/main.dart"]]} {"experimental_bootstrap":false,"script_ids":[["observatory","lib/src/elements/curly_block.dart"],["observatory","lib/src/elements/observatory_element.dart"],["observatory","lib/src/elements/service_ref.dart"],["observatory","lib/src/elements/instance_ref.dart"],["observatory","lib/src/elements/action_link.dart"],["observatory","lib/src/elements/nav_bar.dart"],["observatory","lib/src/elements/breakpoint_list.dart"],["observatory","lib/src/elements/class_ref.dart"],["observatory","lib/src/elements/eval_box.dart"],["observatory","lib/src/elements/eval_link.dart"],["observatory","lib/src/elements/field_ref.dart"],["observatory","lib/src/elements/function_ref.dart"],["observatory","lib/src/elements/library_ref.dart"],["observatory","lib/src/elements/script_ref.dart"],["observatory","lib/src/elements/class_view.dart"],["observatory","lib/src/elements/code_ref.dart"],["observatory","lib/src/elements/code_view.dart"],["observatory","lib/src/elements/collapsible_content.dart"],["observatory","lib/src/elements/error_view.dart"],["observatory","lib/src/elements/field_view.dart"],["observatory","lib/src/elements/stack_frame.dart"],["observatory","lib/src/elements/flag_list.dart"],["observatory","lib/src/elements/script_inset.dart"],["observatory","lib/src/elements/function_view.dart"],["observatory","lib/src/elements/heap_map.dart"],["observatory","lib/src/elements/io_view.dart"],["observatory","lib/src/elements/isolate_ref.dart"],["observatory","lib/src/elements/isolate_summary.dart"],["observatory","lib/src/elements/isolate_view.dart"],["observatory","lib/src/elements/instance_view.dart"],["observatory","lib/src/elements/json_view.dart"],["observatory","lib/src/elements/library_view.dart"],["observatory","lib/src/elements/heap_profile.dart"],["observatory","lib/src/elements/sliding_checkbox.dart"],["observatory","lib/src/elements/isolate_profile.dart"],["observatory","lib/src/elements/script_view.dart"],["observatory","lib/src/elements/stack_trace.dart"],["observatory","lib/src/elements/vm_view.dart"],["observatory","lib/src/elements/service_view.dart"],["observatory","lib/src/elements/response_viewer.dart"],["observatory","lib/src/elements/observatory_application.dart"],["observatory","lib/src/elements/service_exception_view.dart"],["observatory","lib/src/elements/service_error_view.dart"],["observatory","lib/src/elements/vm_ref.dart"],["observatory","web/main.dart"]]}

File diff suppressed because it is too large Load diff

View file

@ -1 +1 @@
{"experimental_bootstrap":false,"script_ids":[["observatory","lib/src/elements/curly_block.dart"],["observatory","lib/src/elements/observatory_element.dart"],["observatory","lib/src/elements/service_ref.dart"],["observatory","lib/src/elements/instance_ref.dart"],["observatory","lib/src/elements/action_link.dart"],["observatory","lib/src/elements/nav_bar.dart"],["observatory","lib/src/elements/breakpoint_list.dart"],["observatory","lib/src/elements/class_ref.dart"],["observatory","lib/src/elements/eval_box.dart"],["observatory","lib/src/elements/eval_link.dart"],["observatory","lib/src/elements/field_ref.dart"],["observatory","lib/src/elements/function_ref.dart"],["observatory","lib/src/elements/library_ref.dart"],["observatory","lib/src/elements/script_ref.dart"],["observatory","lib/src/elements/class_view.dart"],["observatory","lib/src/elements/code_ref.dart"],["observatory","lib/src/elements/code_view.dart"],["observatory","lib/src/elements/collapsible_content.dart"],["observatory","lib/src/elements/error_view.dart"],["observatory","lib/src/elements/field_view.dart"],["observatory","lib/src/elements/script_inset.dart"],["observatory","lib/src/elements/function_view.dart"],["observatory","lib/src/elements/heap_map.dart"],["observatory","lib/src/elements/io_view.dart"],["observatory","lib/src/elements/isolate_ref.dart"],["observatory","lib/src/elements/isolate_summary.dart"],["observatory","lib/src/elements/isolate_view.dart"],["observatory","lib/src/elements/instance_view.dart"],["observatory","lib/src/elements/json_view.dart"],["observatory","lib/src/elements/library_view.dart"],["observatory","lib/src/elements/heap_profile.dart"],["observatory","lib/src/elements/sliding_checkbox.dart"],["observatory","lib/src/elements/isolate_profile.dart"],["observatory","lib/src/elements/script_view.dart"],["observatory","lib/src/elements/stack_frame.dart"],["observatory","lib/src/elements/stack_trace.dart"],["observatory","lib/src/elements/vm_view.dart"],["observatory","lib/src/elements/service_view.dart"],["observatory","lib/src/elements/response_viewer.dart"],["observatory","lib/src/elements/observatory_application.dart"],["observatory","lib/src/elements/service_exception_view.dart"],["observatory","lib/src/elements/service_error_view.dart"],["observatory","lib/src/elements/vm_ref.dart"],["observatory","web/main.dart"]]} {"experimental_bootstrap":false,"script_ids":[["observatory","lib/src/elements/curly_block.dart"],["observatory","lib/src/elements/observatory_element.dart"],["observatory","lib/src/elements/service_ref.dart"],["observatory","lib/src/elements/instance_ref.dart"],["observatory","lib/src/elements/action_link.dart"],["observatory","lib/src/elements/nav_bar.dart"],["observatory","lib/src/elements/breakpoint_list.dart"],["observatory","lib/src/elements/class_ref.dart"],["observatory","lib/src/elements/eval_box.dart"],["observatory","lib/src/elements/eval_link.dart"],["observatory","lib/src/elements/field_ref.dart"],["observatory","lib/src/elements/function_ref.dart"],["observatory","lib/src/elements/library_ref.dart"],["observatory","lib/src/elements/script_ref.dart"],["observatory","lib/src/elements/class_view.dart"],["observatory","lib/src/elements/code_ref.dart"],["observatory","lib/src/elements/code_view.dart"],["observatory","lib/src/elements/collapsible_content.dart"],["observatory","lib/src/elements/error_view.dart"],["observatory","lib/src/elements/field_view.dart"],["observatory","lib/src/elements/stack_frame.dart"],["observatory","lib/src/elements/flag_list.dart"],["observatory","lib/src/elements/script_inset.dart"],["observatory","lib/src/elements/function_view.dart"],["observatory","lib/src/elements/heap_map.dart"],["observatory","lib/src/elements/io_view.dart"],["observatory","lib/src/elements/isolate_ref.dart"],["observatory","lib/src/elements/isolate_summary.dart"],["observatory","lib/src/elements/isolate_view.dart"],["observatory","lib/src/elements/instance_view.dart"],["observatory","lib/src/elements/json_view.dart"],["observatory","lib/src/elements/library_view.dart"],["observatory","lib/src/elements/heap_profile.dart"],["observatory","lib/src/elements/sliding_checkbox.dart"],["observatory","lib/src/elements/isolate_profile.dart"],["observatory","lib/src/elements/script_view.dart"],["observatory","lib/src/elements/stack_trace.dart"],["observatory","lib/src/elements/vm_view.dart"],["observatory","lib/src/elements/service_view.dart"],["observatory","lib/src/elements/response_viewer.dart"],["observatory","lib/src/elements/observatory_application.dart"],["observatory","lib/src/elements/service_exception_view.dart"],["observatory","lib/src/elements/service_error_view.dart"],["observatory","lib/src/elements/vm_ref.dart"],["observatory","web/main.dart"]]}

View file

@ -14,6 +14,7 @@
<link rel="import" href="src/elements/eval_link.html"> <link rel="import" href="src/elements/eval_link.html">
<link rel="import" href="src/elements/field_ref.html"> <link rel="import" href="src/elements/field_ref.html">
<link rel="import" href="src/elements/field_view.html"> <link rel="import" href="src/elements/field_view.html">
<link rel="import" href="src/elements/flag_list.html">
<link rel="import" href="src/elements/function_ref.html"> <link rel="import" href="src/elements/function_ref.html">
<link rel="import" href="src/elements/function_view.html"> <link rel="import" href="src/elements/function_view.html">
<link rel="import" href="src/elements/heap_map.html"> <link rel="import" href="src/elements/heap_map.html">

View file

@ -56,7 +56,12 @@ a:hover {
em { em {
color: inherit; color: inherit;
font-style:italic; font-style: italic;
}
b {
color: inherit;
font-weight: bold;
} }
hr { hr {

View file

@ -0,0 +1,55 @@
<head>
<link rel="import" href="nav_bar.html">
<link rel="import" href="observatory_element.html">
<link rel="import" href="stack_frame.html">
</head>
<polymer-element name="flag-list" extends="observatory-element">
<template>
<link rel="stylesheet" href="css/shared.css">
<nav-bar>
<top-nav-menu></top-nav-menu>
<nav-menu link="{{ flagList.isolate.relativeHashLink('flags') }}" anchor="flags" last="{{ true }}"></nav-menu>
<nav-refresh callback="{{ refresh }}"></nav-refresh>
</nav-bar>
<div class="content-centered">
<template if="{{ flagList['modifiedFlags'].isNotEmpty }}">
<h1>Modified Flags</h1>
<br>
<template repeat="{{ flag in flagList['modifiedFlags'] }}">
<flag-item flag="{{ flag }}"></flag-item>
<br>
</template>
<hr>
</template>
<h1>Unmodified Flags</h1>
<br>
<template if="{{ flagList['unmodifiedFlags'].isEmpty }}">
<em>None</em>
</template>
<template if="{{ flagList['unmodifiedFlags'].isNotEmpty }}">
<template repeat="{{ flag in flagList['unmodifiedFlags'] }}">
<flag-item flag="{{ flag }}"></flag-item>
<br>
</template>
</template>
</div>
</template>
</polymer-element>
<polymer-element name="flag-item" extends="observatory-element">
<template>
<link rel="stylesheet" href="css/shared.css">
<span style="color:#aaa">// {{ flag['comment'] }}</span>
<div style="padding: 3px 0">
<b>{{ flag['name'] }}</b>
&nbsp;=&nbsp;
{{ flag['valueAsString'] }}
</div>
</template>
</polymer-element>
<script type="application/dart" src="flag_list.dart"></script>

View file

@ -61,6 +61,21 @@
</template> </template>
</polymer-element> </polymer-element>
<polymer-element name="io-ref" extends="service-ref">
<template>
<link rel="stylesheet" href="css/shared.css">
<template if="{{ ref.serviceType == 'Socket' }}">
<io-socket-ref ref="{{ ref }}"></io-socket-ref>
</template>
<template if="{{ ref.serviceType == 'HttpServer' }}">
<io-http-server-ref ref="{{ ref }}"></io-http-server-ref>
</template>
<template if="{{ ref.serviceType == 'WebSocket' }}">
<io-web-socket-ref ref="{{ ref }}"></io-web-socket-ref>
</template>
</template>
</polymer-element>
<polymer-element name="io-http-server-list-view" extends="observatory-element"> <polymer-element name="io-http-server-list-view" extends="observatory-element">
<template> <template>
<link rel="stylesheet" href="css/shared.css"> <link rel="stylesheet" href="css/shared.css">
@ -186,6 +201,12 @@
<br> <br>
<div class="memberList"> <div class="memberList">
<template if="{{ socket['owner'] != null }}">
<div class="memberItem">
<div class="memberName">Owner</div>
<div class="memberValue"><io-ref ref="{{ socket['owner'] }}"></io-ref></div>
</div>
</template>
<div class="memberItem"> <div class="memberItem">
<div class="memberName">Address</div> <div class="memberName">Address</div>
<div class="memberValue">{{ socket['address'] }}</div> <div class="memberValue">{{ socket['address'] }}</div>

View file

@ -36,6 +36,12 @@
<div class="memberName">asserts enabled</div> <div class="memberName">asserts enabled</div>
<div class="memberValue">{{ vm.assertsEnabled }}</div> <div class="memberValue">{{ vm.assertsEnabled }}</div>
</div> </div>
<br>
<div class="memberItem">
<div class="memberValue">
See <a href="#/flags">flags</a>
</div>
</div>
</div> </div>
</div> </div>

View file

@ -14,6 +14,7 @@ export 'package:observatory/src/elements/eval_box.dart';
export 'package:observatory/src/elements/eval_link.dart'; export 'package:observatory/src/elements/eval_link.dart';
export 'package:observatory/src/elements/field_ref.dart'; export 'package:observatory/src/elements/field_ref.dart';
export 'package:observatory/src/elements/field_view.dart'; export 'package:observatory/src/elements/field_view.dart';
export 'package:observatory/src/elements/flag_list.dart';
export 'package:observatory/src/elements/function_ref.dart'; export 'package:observatory/src/elements/function_ref.dart';
export 'package:observatory/src/elements/function_view.dart'; export 'package:observatory/src/elements/function_view.dart';
export 'package:observatory/src/elements/heap_map.dart'; export 'package:observatory/src/elements/heap_map.dart';

View file

@ -14,6 +14,7 @@
<link rel="import" href="src/elements/eval_link.html"> <link rel="import" href="src/elements/eval_link.html">
<link rel="import" href="src/elements/field_ref.html"> <link rel="import" href="src/elements/field_ref.html">
<link rel="import" href="src/elements/field_view.html"> <link rel="import" href="src/elements/field_view.html">
<link rel="import" href="src/elements/flag_list.html">
<link rel="import" href="src/elements/function_ref.html"> <link rel="import" href="src/elements/function_ref.html">
<link rel="import" href="src/elements/function_view.html"> <link rel="import" href="src/elements/function_view.html">
<link rel="import" href="src/elements/heap_map.html"> <link rel="import" href="src/elements/heap_map.html">

View file

@ -56,7 +56,12 @@ a:hover {
em { em {
color: inherit; color: inherit;
font-style:italic; font-style: italic;
}
b {
color: inherit;
font-weight: bold;
} }
hr { hr {

View file

@ -0,0 +1,27 @@
// 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.
library flag_list_element;
import 'package:polymer/polymer.dart';
import 'observatory_element.dart';
import 'package:observatory/service.dart';
@CustomTag('flag-list')
class FlagListElement extends ObservatoryElement {
@published ServiceMap flagList;
FlagListElement.created() : super.created();
void refresh(var done) {
flagList.reload().whenComplete(done);
}
}
@CustomTag('flag-item')
class FlagItemElement extends ObservatoryElement {
@published ObservableMap flag;
FlagItemElement.created() : super.created();
}

View file

@ -0,0 +1,55 @@
<head>
<link rel="import" href="nav_bar.html">
<link rel="import" href="observatory_element.html">
<link rel="import" href="stack_frame.html">
</head>
<polymer-element name="flag-list" extends="observatory-element">
<template>
<link rel="stylesheet" href="css/shared.css">
<nav-bar>
<top-nav-menu></top-nav-menu>
<nav-menu link="{{ flagList.isolate.relativeHashLink('flags') }}" anchor="flags" last="{{ true }}"></nav-menu>
<nav-refresh callback="{{ refresh }}"></nav-refresh>
</nav-bar>
<div class="content-centered">
<template if="{{ flagList['modifiedFlags'].isNotEmpty }}">
<h1>Modified Flags</h1>
<br>
<template repeat="{{ flag in flagList['modifiedFlags'] }}">
<flag-item flag="{{ flag }}"></flag-item>
<br>
</template>
<hr>
</template>
<h1>Unmodified Flags</h1>
<br>
<template if="{{ flagList['unmodifiedFlags'].isEmpty }}">
<em>None</em>
</template>
<template if="{{ flagList['unmodifiedFlags'].isNotEmpty }}">
<template repeat="{{ flag in flagList['unmodifiedFlags'] }}">
<flag-item flag="{{ flag }}"></flag-item>
<br>
</template>
</template>
</div>
</template>
</polymer-element>
<polymer-element name="flag-item" extends="observatory-element">
<template>
<link rel="stylesheet" href="css/shared.css">
<span style="color:#aaa">// {{ flag['comment'] }}</span>
<div style="padding: 3px 0">
<b>{{ flag['name'] }}</b>
&nbsp;=&nbsp;
{{ flag['valueAsString'] }}
</div>
</template>
</polymer-element>
<script type="application/dart" src="flag_list.dart"></script>

View file

@ -44,6 +44,10 @@ class ServiceObjectViewElement extends ObservatoryElement {
FieldViewElement element = new Element.tag('field-view'); FieldViewElement element = new Element.tag('field-view');
element.field = object; element.field = object;
return element; return element;
case 'FlagList':
FlagListElement element = new Element.tag('flag-list');
element.flagList = object;
return element;
case 'Function': case 'Function':
FunctionViewElement element = new Element.tag('function-view'); FunctionViewElement element = new Element.tag('function-view');
element.function = object; element.function = object;

View file

@ -36,6 +36,12 @@
<div class="memberName">asserts enabled</div> <div class="memberName">asserts enabled</div>
<div class="memberValue">{{ vm.assertsEnabled }}</div> <div class="memberValue">{{ vm.assertsEnabled }}</div>
</div> </div>
<br>
<div class="memberItem">
<div class="memberValue">
See <a href="#/flags">flags</a>
</div>
</div>
</div> </div>
</div> </div>

View file

@ -27,7 +27,7 @@ abstract class ServiceObject extends Observable {
String _serviceType; String _serviceType;
/// The complete service url of this object. /// The complete service url of this object.
@reflectable String get link => isolate.relativeLink(_id); @reflectable String get link => _owner.relativeLink(_id);
/// The complete service url of this object with a '#/' prefix. /// The complete service url of this object with a '#/' prefix.
// TODO(turnidge): Figure out why using a getter here messes up polymer. // TODO(turnidge): Figure out why using a getter here messes up polymer.
@ -179,6 +179,7 @@ abstract class VM extends ServiceObjectOwner {
@reflectable Iterable<Isolate> get isolates => _isolateCache.values; @reflectable Iterable<Isolate> get isolates => _isolateCache.values;
@reflectable String get link => '$id'; @reflectable String get link => '$id';
@reflectable String relativeLink(String id) => '$id';
@observable String version = 'unknown'; @observable String version = 'unknown';
@observable String architecture = 'unknown'; @observable String architecture = 'unknown';

View file

@ -5,6 +5,7 @@
#include "vm/flags.h" #include "vm/flags.h"
#include "platform/assert.h" #include "platform/assert.h"
#include "vm/json_stream.h"
#include "vm/os.h" #include "vm/os.h"
namespace dart { namespace dart {
@ -13,11 +14,13 @@ DEFINE_FLAG(bool, print_flags, false, "Print flags as they are being parsed.");
DEFINE_FLAG(bool, ignore_unrecognized_flags, false, DEFINE_FLAG(bool, ignore_unrecognized_flags, false,
"Ignore unrecognized flags."); "Ignore unrecognized flags.");
// List of registered flags.
Flag* Flags::flags_ = NULL;
bool Flags::initialized_ = false; bool Flags::initialized_ = false;
// List of registered flags.
Flag** Flags::flags_ = NULL;
intptr_t Flags::capacity_ = 0;
intptr_t Flags::num_flags_ = 0;
class Flag { class Flag {
public: public:
enum FlagType { enum FlagType {
@ -72,7 +75,6 @@ class Flag {
return (type_ == kBoolean) && (bool_ptr_ == NULL); return (type_ == kBoolean) && (bool_ptr_ == NULL);
} }
Flag* next_;
const char* name_; const char* name_;
const char* comment_; const char* comment_;
union { union {
@ -83,16 +85,16 @@ class Flag {
FlagHandler handler_; FlagHandler handler_;
}; };
FlagType type_; FlagType type_;
bool changed_;
}; };
Flag* Flags::Lookup(const char* name) { Flag* Flags::Lookup(const char* name) {
Flag* cur = Flags::flags_; for (intptr_t i = 0; i < num_flags_; i++) {
while (cur != NULL) { Flag* flag = flags_[i];
if (strcmp(cur->name_, name) == 0) { if (strcmp(flag->name_, name) == 0) {
return cur; return flag;
} }
cur = cur->next_;
} }
return NULL; return NULL;
} }
@ -107,6 +109,27 @@ bool Flags::IsSet(const char* name) {
} }
void Flags::AddFlag(Flag* flag) {
ASSERT(!initialized_);
if (num_flags_ == capacity_) {
if (flags_ == NULL) {
capacity_ = 256;
flags_ = new Flag*[capacity_];
} else {
intptr_t new_capacity = capacity_ * 2;
Flag** new_flags = new Flag*[new_capacity];
for (intptr_t i = 0; i < num_flags_; i++) {
new_flags[i] = flags_[i];
}
delete [] flags_;
flags_ = new_flags;
capacity_ = new_capacity;
}
}
flags_[num_flags_++] = flag;
}
bool Flags::Register_bool(bool* addr, bool Flags::Register_bool(bool* addr,
const char* name, const char* name,
bool default_value, bool default_value,
@ -117,9 +140,7 @@ bool Flags::Register_bool(bool* addr,
return default_value; return default_value;
} }
flag = new Flag(name, comment, addr, Flag::kBoolean); flag = new Flag(name, comment, addr, Flag::kBoolean);
flag->next_ = Flags::flags_; AddFlag(flag);
Flags::flags_ = flag;
return default_value; return default_value;
} }
@ -131,8 +152,7 @@ int Flags::Register_int(int* addr,
ASSERT(Lookup(name) == NULL); ASSERT(Lookup(name) == NULL);
Flag* flag = new Flag(name, comment, addr, Flag::kInteger); Flag* flag = new Flag(name, comment, addr, Flag::kInteger);
flag->next_ = Flags::flags_; AddFlag(flag);
Flags::flags_ = flag;
return default_value; return default_value;
} }
@ -144,8 +164,7 @@ const char* Flags::Register_charp(charp* addr,
const char* comment) { const char* comment) {
ASSERT(Lookup(name) == NULL); ASSERT(Lookup(name) == NULL);
Flag* flag = new Flag(name, comment, addr, Flag::kString); Flag* flag = new Flag(name, comment, addr, Flag::kString);
flag->next_ = Flags::flags_; AddFlag(flag);
Flags::flags_ = flag;
return default_value; return default_value;
} }
@ -155,8 +174,7 @@ bool Flags::Register_func(FlagHandler handler,
const char* comment) { const char* comment) {
ASSERT(Lookup(name) == NULL); ASSERT(Lookup(name) == NULL);
Flag* flag = new Flag(name, comment, handler); Flag* flag = new Flag(name, comment, handler);
flag->next_ = Flags::flags_; AddFlag(flag);
Flags::flags_ = flag;
return false; return false;
} }
@ -171,6 +189,51 @@ static void Normalize(char* s) {
} }
bool Flags::SetFlagFromString(Flag* flag, const char* argument) {
ASSERT(!flag->IsUnrecognized());
switch (flag->type_) {
case Flag::kBoolean: {
if (strcmp(argument, "true") == 0) {
*flag->bool_ptr_ = true;
} else if (strcmp(argument, "false") == 0) {
*flag->bool_ptr_ = false;
} else {
return false;
}
break;
}
case Flag::kString: {
*flag->charp_ptr_ = argument == NULL ? NULL : strdup(argument);
break;
}
case Flag::kInteger: {
char* endptr = NULL;
int val = strtol(argument, &endptr, 10);
if (endptr != argument) {
*flag->int_ptr_ = val;
}
break;
}
case Flag::kFunc: {
if (strcmp(argument, "true") == 0) {
(flag->handler_)(true);
} else if (strcmp(argument, "false") == 0) {
(flag->handler_)(false);
} else {
return false;
}
break;
}
default: {
UNREACHABLE();
return false;
}
}
flag->changed_ = true;
return true;
}
void Flags::Parse(const char* option) { void Flags::Parse(const char* option) {
// Find the beginning of the option argument, if it exists. // Find the beginning of the option argument, if it exists.
const char* equals = option; const char* equals = option;
@ -220,43 +283,9 @@ void Flags::Parse(const char* option) {
// Only set values for recognized flags, skip collected // Only set values for recognized flags, skip collected
// unrecognized flags. // unrecognized flags.
if (!flag->IsUnrecognized()) { if (!flag->IsUnrecognized()) {
switch (flag->type_) { if (!SetFlagFromString(flag, argument)) {
case Flag::kBoolean: { OS::Print("Ignoring flag: %s is an invalid value for flag %s\n",
if (strcmp(argument, "true") == 0) { argument, name);
*flag->bool_ptr_ = true;
} else if (strcmp(argument, "false") == 0) {
*flag->bool_ptr_ = false;
} else {
OS::Print("Ignoring flag: %s is a bool flag.\n", name);
}
break;
}
case Flag::kString: {
*flag->charp_ptr_ = argument == NULL ? NULL : strdup(argument);
break;
}
case Flag::kInteger: {
char* endptr = NULL;
int val = strtol(argument, &endptr, 10);
if (endptr != argument) {
*flag->int_ptr_ = val;
}
break;
}
case Flag::kFunc: {
if (strcmp(argument, "true") == 0) {
(flag->handler_)(true);
} else if (strcmp(argument, "false") == 0) {
(flag->handler_)(false);
} else {
OS::Print("Ignoring flag: %s is a bool flag.\n", name);
}
break;
}
default: {
UNREACHABLE();
break;
}
} }
} }
} }
@ -274,13 +303,20 @@ static bool IsValidFlag(const char* name,
} }
int Flags::CompareFlagNames(const void* left, const void* right) {
const Flag* left_flag = *reinterpret_cast<const Flag* const *>(left);
const Flag* right_flag = *reinterpret_cast<const Flag* const *>(right);
return strcmp(left_flag->name_, right_flag->name_);
}
bool Flags::ProcessCommandLineFlags(int number_of_vm_flags, bool Flags::ProcessCommandLineFlags(int number_of_vm_flags,
const char** vm_flags) { const char** vm_flags) {
if (initialized_) { if (initialized_) {
return false; return false;
} }
initialized_ = true; qsort(flags_, num_flags_, sizeof flags_[0], CompareFlagNames);
const char* kPrefix = "--"; const char* kPrefix = "--";
const intptr_t kPrefixLen = strlen(kPrefix); const intptr_t kPrefixLen = strlen(kPrefix);
@ -295,8 +331,8 @@ bool Flags::ProcessCommandLineFlags(int number_of_vm_flags,
if (!FLAG_ignore_unrecognized_flags) { if (!FLAG_ignore_unrecognized_flags) {
int unrecognized_count = 0; int unrecognized_count = 0;
Flag* flag = Flags::flags_; for (intptr_t j = 0; j < num_flags_; j++) {
while (flag != NULL) { Flag* flag = flags_[j];
if (flag->IsUnrecognized()) { if (flag->IsUnrecognized()) {
if (unrecognized_count == 0) { if (unrecognized_count == 0) {
OS::PrintErr("Unrecognized flags: %s", flag->name_); OS::PrintErr("Unrecognized flags: %s", flag->name_);
@ -305,7 +341,6 @@ bool Flags::ProcessCommandLineFlags(int number_of_vm_flags,
} }
unrecognized_count++; unrecognized_count++;
} }
flag = flag->next_;
} }
if (unrecognized_count > 0) { if (unrecognized_count > 0) {
OS::PrintErr("\n"); OS::PrintErr("\n");
@ -315,36 +350,93 @@ bool Flags::ProcessCommandLineFlags(int number_of_vm_flags,
if (FLAG_print_flags) { if (FLAG_print_flags) {
PrintFlags(); PrintFlags();
} }
initialized_ = true;
return true;
}
bool Flags::SetFlag(const char* name,
const char* value,
const char** error) {
Flag* flag = Lookup(name);
if (flag == NULL) {
*error = "Cannot set flag: flag not found";
return false;
}
if (!SetFlagFromString(flag, value)) {
*error = "Cannot set flag: invalid value";
return false;
}
return true; return true;
} }
int Flags::CompareFlagNames(const void* left, const void* right) { void Flags::PrintFlags() {
const Flag* left_flag = *reinterpret_cast<const Flag* const *>(left); OS::Print("Flag settings:\n");
const Flag* right_flag = *reinterpret_cast<const Flag* const *>(right); for (intptr_t i = 0; i < num_flags_; ++i) {
return strcmp(left_flag->name_, right_flag->name_); flags_[i]->Print();
}
} }
void Flags::PrintFlags() { void Flags::PrintFlagToJSONArray(JSONArray* jsarr, const Flag* flag) {
OS::Print("Flag settings:\n"); if (flag->IsUnrecognized() || flag->type_ == Flag::kFunc) {
Flag* flag = Flags::flags_; return;
int num_flags = 0;
while (flag != NULL) {
num_flags++;
flag = flag->next_;
}
Flag** flag_array = new Flag*[num_flags];
flag = Flags::flags_;
for (int i = 0; i < num_flags; ++i, flag = flag->next_) {
flag_array[i] = flag;
}
qsort(flag_array, num_flags, sizeof flag_array[0], CompareFlagNames);
for (int i = 0; i < num_flags; ++i) {
flag_array[i]->Print();
}
delete[] flag_array;
} }
JSONObject jsflag(jsarr);
jsflag.AddProperty("name", flag->name_);
jsflag.AddProperty("comment", flag->comment_);
switch (flag->type_) {
case Flag::kBoolean: {
jsflag.AddProperty("flagType", "bool");
jsflag.AddProperty("valueAsString",
(*flag->bool_ptr_ ? "true" : "false"));
break;
}
case Flag::kInteger: {
jsflag.AddProperty("flagType", "int");
jsflag.AddPropertyF("valueAsString", "%d", *flag->int_ptr_);
break;
}
case Flag::kString: {
jsflag.AddProperty("flagType", "string");
if (flag->charp_ptr_ != NULL) {
jsflag.AddPropertyF("valueAsString", "%s", *flag->charp_ptr_);
} else {
// valueAsString missing means NULL.
}
break;
}
default:
UNREACHABLE();
break;
}
}
void Flags::PrintJSON(JSONStream* js) {
JSONObject jsobj(js);
jsobj.AddProperty("type", "FlagList");
jsobj.AddProperty("id", "flags");
{
JSONArray jsarr(&jsobj, "unmodifiedFlags");
for (intptr_t i = 0; i < num_flags_; ++i) {
Flag* flag = flags_[i];
if (!flag->changed_) {
PrintFlagToJSONArray(&jsarr, flag);
}
}
}
{
JSONArray jsarr(&jsobj, "modifiedFlags");
for (intptr_t i = 0; i < num_flags_; ++i) {
Flag* flag = flags_[i];
if (flag->changed_) {
PrintFlagToJSONArray(&jsarr, flag);
}
}
}
}
} // namespace dart } // namespace dart

View file

@ -36,8 +36,10 @@ namespace dart {
typedef void (*FlagHandler)(bool value); typedef void (*FlagHandler)(bool value);
// Forward declaration. // Forward declarations.
class Flag; class Flag;
class JSONArray;
class JSONStream;
class Flags { class Flags {
public: public:
@ -68,17 +70,31 @@ class Flags {
static bool Initialized() { return initialized_; } static bool Initialized() { return initialized_; }
static void PrintJSON(JSONStream* js);
static bool SetFlag(const char* name,
const char* value,
const char** error);
private: private:
static Flag* flags_; static Flag** flags_;
static intptr_t capacity_;
static intptr_t num_flags_;
static bool initialized_; static bool initialized_;
static void AddFlag(Flag* flag);
static bool SetFlagFromString(Flag* flag, const char* argument);
static void Parse(const char* option); static void Parse(const char* option);
static int CompareFlagNames(const void* left, const void* right); static int CompareFlagNames(const void* left, const void* right);
static void PrintFlags(); static void PrintFlags();
static void PrintFlagToJSONArray(JSONArray* jsarr, const Flag* flag);
// Testing needs direct access to private methods. // Testing needs direct access to private methods.
friend void Dart_TestParseFlags(); friend void Dart_TestParseFlags();

View file

@ -1933,7 +1933,8 @@ static bool HandleVM(JSONStream* js) {
JSONObject jsobj(js); JSONObject jsobj(js);
jsobj.AddProperty("type", "VM"); jsobj.AddProperty("type", "VM");
jsobj.AddProperty("id", "vm"); jsobj.AddProperty("id", "vm");
jsobj.AddProperty("architecture", CPU::Id()); jsobj.AddProperty("targetCPU", CPU::Id());
jsobj.AddProperty("hostCPU", HostCPUFeatures::hardware());
jsobj.AddProperty("version", Version::String()); jsobj.AddProperty("version", Version::String());
jsobj.AddProperty("assertsEnabled", FLAG_enable_asserts); jsobj.AddProperty("assertsEnabled", FLAG_enable_asserts);
jsobj.AddProperty("typeChecksEnabled", FLAG_enable_type_checks); jsobj.AddProperty("typeChecksEnabled", FLAG_enable_type_checks);
@ -1953,9 +1954,47 @@ static bool HandleVM(JSONStream* js) {
} }
static bool HandleFlags(JSONStream* js) {
if (js->num_arguments() == 1) {
Flags::PrintJSON(js);
return true;
} else if (js->num_arguments() == 2) {
const char* arg = js->GetArgument(1);
if (strcmp(arg, "set") == 0) {
if (js->num_arguments() > 2) {
PrintError(js, "expected at most 2 arguments but found %" Pd "\n",
js->num_arguments());
} else {
if (js->HasOption("name") && js->HasOption("value")) {
JSONObject jsobj(js);
const char* flag_name = js->LookupOption("name");
const char* flag_value = js->LookupOption("value");
const char* error = NULL;
if (Flags::SetFlag(flag_name, flag_value, &error)) {
jsobj.AddProperty("type", "Success");
jsobj.AddProperty("id", "");
} else {
jsobj.AddProperty("type", "Failure");
jsobj.AddProperty("id", "");
jsobj.AddProperty("message", error);
}
} else {
PrintError(js, "expected to find 'name' and 'value' options");
}
}
}
return true;
} else {
PrintError(js, "Command too long");
return true;
}
}
static RootMessageHandlerEntry root_handlers[] = { static RootMessageHandlerEntry root_handlers[] = {
{ "_echo", HandleRootEcho }, { "_echo", HandleRootEcho },
{ "vm", HandleVM }, { "vm", HandleVM },
{ "flags", HandleFlags },
}; };

View file

@ -18,6 +18,9 @@
namespace dart { namespace dart {
// This flag is used in the Service_Flags test below.
DEFINE_FLAG(bool, service_testing_flag, false, "Comment");
class ServiceTestMessageHandler : public MessageHandler { class ServiceTestMessageHandler : public MessageHandler {
public: public:
ServiceTestMessageHandler() : _msg(NULL) {} ServiceTestMessageHandler() : _msg(NULL) {}
@ -1065,13 +1068,63 @@ TEST_CASE(Service_VM) {
Service::HandleRootMessage(service_msg); Service::HandleRootMessage(service_msg);
handler.HandleNextMessage(); handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"VM\",\"id\":\"vm\"", handler.msg()); EXPECT_SUBSTRING("\"type\":\"VM\",\"id\":\"vm\"", handler.msg());
EXPECT_SUBSTRING("\"architecture\"", handler.msg()); EXPECT_SUBSTRING("\"targetCPU\"", handler.msg());
EXPECT_SUBSTRING("\"hostCPU\"", handler.msg());
EXPECT_SUBSTRING("\"version\"", handler.msg()); EXPECT_SUBSTRING("\"version\"", handler.msg());
EXPECT_SUBSTRING("\"uptime\"", handler.msg()); EXPECT_SUBSTRING("\"uptime\"", handler.msg());
EXPECT_SUBSTRING("\"isolates\"", handler.msg()); EXPECT_SUBSTRING("\"isolates\"", handler.msg());
} }
TEST_CASE(Service_Flags) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
// Build a mock message handler and wrap it in a dart port.
ServiceTestMessageHandler handler;
Dart_Port port_id = PortMap::CreatePort(&handler);
Dart_Handle port = Api::NewHandle(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Instance& service_msg = Instance::Handle();
service_msg = Eval(lib, "[port, ['flags'], [], []]");
// Make sure we can get the FlagList.
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"FlagList\",\"id\":\"flags\"", handler.msg());
EXPECT_SUBSTRING(
"\"name\":\"service_testing_flag\",\"comment\":\"Comment\","
"\"flagType\":\"bool\",\"valueAsString\":\"false\"",
handler.msg());
// Modify a flag through the vm service.
service_msg = Eval(lib,
"[port, ['flags', 'set'], "
"['name', 'value'], ['service_testing_flag', 'true']]");
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("Success", handler.msg());
// Make sure that the flag changed.
service_msg = Eval(lib, "[port, ['flags'], [], []]");
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING(
"\"name\":\"service_testing_flag\",\"comment\":\"Comment\","
"\"flagType\":\"bool\",\"valueAsString\":\"true\"",
handler.msg());
}
TEST_CASE(Service_Scripts) { TEST_CASE(Service_Scripts) {
const char* kScript = const char* kScript =
"var port;\n" // Set to our mock port by C++. "var port;\n" // Set to our mock port by C++.