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