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