Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/api-inl.h"
6 : #include "src/builtins/builtins-utils-inl.h"
7 : #include "src/builtins/builtins.h"
8 : #include "src/counters.h"
9 : #include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop.
10 : #include "src/json-stringifier.h"
11 : #include "src/objects-inl.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 : namespace {
17 :
18 : using v8::tracing::TracedValue;
19 :
20 : #define MAX_STACK_LENGTH 100
21 :
22 50 : class MaybeUtf8 {
23 : public:
24 50 : explicit MaybeUtf8(Isolate* isolate, Handle<String> string) : buf_(data_) {
25 50 : string = String::Flatten(isolate, string);
26 : int len;
27 50 : if (string->IsOneByteRepresentation()) {
28 : // Technically this allows unescaped latin1 characters but the trace
29 : // events mechanism currently does the same and the current consuming
30 : // tools are tolerant of it. A more correct approach here would be to
31 : // escape non-ascii characters but this is easier and faster.
32 : len = string->length();
33 35 : AllocateSufficientSpace(len);
34 35 : if (len > 0) {
35 : // Why copy? Well, the trace event mechanism requires null-terminated
36 : // strings, the bytes we get from SeqOneByteString are not. buf_ is
37 : // guaranteed to be null terminated.
38 : DisallowHeapAllocation no_gc;
39 70 : memcpy(buf_, Handle<SeqOneByteString>::cast(string)->GetChars(no_gc),
40 : len);
41 : }
42 : } else {
43 : Local<v8::String> local = Utils::ToLocal(string);
44 : auto* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
45 15 : len = local->Utf8Length(v8_isolate);
46 15 : AllocateSufficientSpace(len);
47 15 : if (len > 0) {
48 15 : local->WriteUtf8(v8_isolate, reinterpret_cast<char*>(buf_));
49 : }
50 : }
51 50 : buf_[len] = 0;
52 50 : }
53 : const char* operator*() const { return reinterpret_cast<const char*>(buf_); }
54 :
55 : private:
56 50 : void AllocateSufficientSpace(int len) {
57 50 : if (len + 1 > MAX_STACK_LENGTH) {
58 0 : allocated_.reset(new uint8_t[len + 1]);
59 0 : buf_ = allocated_.get();
60 : }
61 50 : }
62 :
63 : // In the most common cases, the buffer here will be stack allocated.
64 : // A heap allocation will only occur if the data is more than MAX_STACK_LENGTH
65 : // Given that this is used primarily for trace event categories and names,
66 : // the MAX_STACK_LENGTH should be more than enough.
67 : uint8_t* buf_;
68 : uint8_t data_[MAX_STACK_LENGTH];
69 : std::unique_ptr<uint8_t> allocated_;
70 : };
71 :
72 20 : class JsonTraceValue : public ConvertableToTraceFormat {
73 : public:
74 20 : explicit JsonTraceValue(Isolate* isolate, Handle<String> object) {
75 : // object is a JSON string serialized using JSON.stringify() from within
76 : // the BUILTIN(Trace) method. This may (likely) contain UTF8 values so
77 : // to grab the appropriate buffer data we have to serialize it out. We
78 : // hold on to the bits until the AppendAsTraceFormat method is called.
79 10 : MaybeUtf8 data(isolate, object);
80 10 : data_ = *data;
81 10 : }
82 :
83 0 : void AppendAsTraceFormat(std::string* out) const override { *out += data_; }
84 :
85 : private:
86 : std::string data_;
87 : };
88 :
89 30 : const uint8_t* GetCategoryGroupEnabled(Isolate* isolate,
90 : Handle<String> string) {
91 30 : MaybeUtf8 category(isolate, string);
92 60 : return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(*category);
93 : }
94 :
95 : #undef MAX_STACK_LENGTH
96 :
97 : } // namespace
98 :
99 : // Builins::kIsTraceCategoryEnabled(category) : bool
100 75 : BUILTIN(IsTraceCategoryEnabled) {
101 : HandleScope scope(isolate);
102 : Handle<Object> category = args.atOrUndefined(isolate, 1);
103 15 : if (!category->IsString()) {
104 0 : THROW_NEW_ERROR_RETURN_FAILURE(
105 : isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError));
106 : }
107 : return isolate->heap()->ToBoolean(
108 30 : *GetCategoryGroupEnabled(isolate, Handle<String>::cast(category)));
109 : }
110 :
111 : // Builtins::kTrace(phase, category, name, id, data) : bool
112 75 : BUILTIN(Trace) {
113 : HandleScope handle_scope(isolate);
114 :
115 : Handle<Object> phase_arg = args.atOrUndefined(isolate, 1);
116 : Handle<Object> category = args.atOrUndefined(isolate, 2);
117 : Handle<Object> name_arg = args.atOrUndefined(isolate, 3);
118 : Handle<Object> id_arg = args.atOrUndefined(isolate, 4);
119 : Handle<Object> data_arg = args.atOrUndefined(isolate, 5);
120 :
121 : const uint8_t* category_group_enabled =
122 15 : GetCategoryGroupEnabled(isolate, Handle<String>::cast(category));
123 :
124 : // Exit early if the category group is not enabled.
125 15 : if (!*category_group_enabled) {
126 5 : return ReadOnlyRoots(isolate).false_value();
127 : }
128 :
129 10 : if (!phase_arg->IsNumber()) {
130 0 : THROW_NEW_ERROR_RETURN_FAILURE(
131 : isolate, NewTypeError(MessageTemplate::kTraceEventPhaseError));
132 : }
133 10 : if (!category->IsString()) {
134 0 : THROW_NEW_ERROR_RETURN_FAILURE(
135 : isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError));
136 : }
137 10 : if (!name_arg->IsString()) {
138 0 : THROW_NEW_ERROR_RETURN_FAILURE(
139 : isolate, NewTypeError(MessageTemplate::kTraceEventNameError));
140 : }
141 :
142 : uint32_t flags = TRACE_EVENT_FLAG_COPY;
143 : int32_t id = 0;
144 10 : if (!id_arg->IsNullOrUndefined(isolate)) {
145 10 : if (!id_arg->IsNumber()) {
146 0 : THROW_NEW_ERROR_RETURN_FAILURE(
147 : isolate, NewTypeError(MessageTemplate::kTraceEventIDError));
148 : }
149 : flags |= TRACE_EVENT_FLAG_HAS_ID;
150 10 : id = DoubleToInt32(id_arg->Number());
151 : }
152 :
153 : Handle<String> name_str = Handle<String>::cast(name_arg);
154 10 : if (name_str->length() == 0) {
155 0 : THROW_NEW_ERROR_RETURN_FAILURE(
156 : isolate, NewTypeError(MessageTemplate::kTraceEventNameLengthError));
157 : }
158 10 : MaybeUtf8 name(isolate, name_str);
159 :
160 : // We support passing one additional trace event argument with the
161 : // name "data". Any JSON serializable value may be passed.
162 : static const char* arg_name = "data";
163 : int32_t num_args = 0;
164 : uint8_t arg_type;
165 : uint64_t arg_value;
166 :
167 10 : if (!data_arg->IsUndefined(isolate)) {
168 : // Serializes the data argument as a JSON string, which is then
169 : // copied into an object. This eliminates duplicated code but
170 : // could have perf costs. It is also subject to all the same
171 : // limitations as JSON.stringify() as it relates to circular
172 : // references and value limitations (e.g. BigInt is not supported).
173 : Handle<Object> result;
174 20 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
175 : isolate, result,
176 : JsonStringify(isolate, data_arg, isolate->factory()->undefined_value(),
177 : isolate->factory()->undefined_value()));
178 : std::unique_ptr<JsonTraceValue> traced_value;
179 10 : traced_value.reset(
180 20 : new JsonTraceValue(isolate, Handle<String>::cast(result)));
181 : tracing::SetTraceValue(std::move(traced_value), &arg_type, &arg_value);
182 : num_args++;
183 : }
184 :
185 20 : TRACE_EVENT_API_ADD_TRACE_EVENT(
186 10 : static_cast<char>(DoubleToInt32(phase_arg->Number())),
187 : category_group_enabled, *name, tracing::kGlobalScope, id, tracing::kNoId,
188 : num_args, &arg_name, &arg_type, &arg_value, flags);
189 :
190 10 : return ReadOnlyRoots(isolate).true_value();
191 : }
192 :
193 : } // namespace internal
194 122036 : } // namespace v8
|