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