Line data Source code
1 : // Copyright 2014 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/string-stream.h"
6 :
7 : #include <memory>
8 :
9 : #include "src/handles-inl.h"
10 : #include "src/log.h"
11 : #include "src/objects-inl.h"
12 : #include "src/objects/js-array-inl.h"
13 : #include "src/prototype.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : static const int kMentionedObjectCacheMaxSize = 256;
19 :
20 83370 : char* HeapStringAllocator::allocate(unsigned bytes) {
21 83370 : space_ = NewArray<char>(bytes);
22 83370 : return space_;
23 : }
24 :
25 :
26 15 : char* FixedStringAllocator::allocate(unsigned bytes) {
27 15 : CHECK_LE(bytes, length_);
28 15 : return buffer_;
29 : }
30 :
31 :
32 0 : char* FixedStringAllocator::grow(unsigned* old) {
33 0 : *old = length_;
34 0 : return buffer_;
35 : }
36 :
37 :
38 5083157 : bool StringStream::Put(char c) {
39 5083157 : if (full()) return false;
40 : DCHECK(length_ < capacity_);
41 : // Since the trailing '\0' is not accounted for in length_ fullness is
42 : // indicated by a difference of 1 between length_ and capacity_. Thus when
43 : // reaching a difference of 2 we need to grow the buffer.
44 5083157 : if (length_ == capacity_ - 2) {
45 198369 : unsigned new_capacity = capacity_;
46 198369 : char* new_buffer = allocator_->grow(&new_capacity);
47 198369 : if (new_capacity > capacity_) {
48 198369 : capacity_ = new_capacity;
49 198369 : buffer_ = new_buffer;
50 : } else {
51 : // Reached the end of the available buffer.
52 : DCHECK_GE(capacity_, 5);
53 0 : length_ = capacity_ - 1; // Indicate fullness of the stream.
54 0 : buffer_[length_ - 4] = '.';
55 0 : buffer_[length_ - 3] = '.';
56 0 : buffer_[length_ - 2] = '.';
57 0 : buffer_[length_ - 1] = '\n';
58 0 : buffer_[length_] = '\0';
59 0 : return false;
60 : }
61 : }
62 5083157 : buffer_[length_] = c;
63 5083157 : buffer_[length_ + 1] = '\0';
64 5083157 : length_++;
65 5083157 : return true;
66 : }
67 :
68 :
69 : // A control character is one that configures a format element. For
70 : // instance, in %.5s, .5 are control characters.
71 : static bool IsControlChar(char c) {
72 : switch (c) {
73 : case '0': case '1': case '2': case '3': case '4': case '5':
74 : case '6': case '7': case '8': case '9': case '.': case '-':
75 : return true;
76 : default:
77 : return false;
78 : }
79 : }
80 :
81 :
82 871920 : void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
83 : // If we already ran out of space then return immediately.
84 871920 : if (full()) return;
85 : int offset = 0;
86 : int elm = 0;
87 6121197 : while (offset < format.length()) {
88 11202501 : if (format[offset] != '%' || elm == elms.length()) {
89 4545338 : Put(format[offset]);
90 4545338 : offset++;
91 4545338 : continue;
92 : }
93 : // Read this formatting directive into a temporary buffer
94 : EmbeddedVector<char, 24> temp;
95 : int format_length = 0;
96 : // Skip over the whole control character sequence until the
97 : // format element type
98 703939 : temp[format_length++] = format[offset++];
99 3607583 : while (offset < format.length() && IsControlChar(format[offset]))
100 2243649 : temp[format_length++] = format[offset++];
101 703939 : if (offset >= format.length())
102 0 : return;
103 1407878 : char type = format[offset];
104 1407878 : temp[format_length++] = type;
105 1407878 : temp[format_length] = '\0';
106 703939 : offset++;
107 1407878 : FmtElm current = elms[elm++];
108 703939 : switch (type) {
109 : case 's': {
110 : DCHECK_EQ(FmtElm::C_STR, current.type_);
111 207339 : const char* value = current.data_.u_c_str_;
112 207339 : Add(value);
113 207339 : break;
114 : }
115 : case 'w': {
116 : DCHECK_EQ(FmtElm::LC_STR, current.type_);
117 0 : Vector<const uc16> value = *current.data_.u_lc_str_;
118 0 : for (int i = 0; i < value.length(); i++)
119 0 : Put(static_cast<char>(value[i]));
120 : break;
121 : }
122 : case 'o': {
123 : DCHECK_EQ(FmtElm::OBJ, current.type_);
124 549 : Object obj(current.data_.u_obj_);
125 549 : PrintObject(obj);
126 : break;
127 : }
128 : case 'k': {
129 : DCHECK_EQ(FmtElm::INT, current.type_);
130 0 : int value = current.data_.u_int_;
131 0 : if (0x20 <= value && value <= 0x7F) {
132 0 : Put(value);
133 0 : } else if (value <= 0xFF) {
134 0 : Add("\\x%02x", value);
135 : } else {
136 0 : Add("\\u%04x", value);
137 : }
138 : break;
139 : }
140 : case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
141 465424 : int value = current.data_.u_int_;
142 : EmbeddedVector<char, 24> formatted;
143 465424 : int length = SNPrintF(formatted, temp.start(), value);
144 930848 : Add(Vector<const char>(formatted.start(), length));
145 : break;
146 : }
147 : case 'f': case 'g': case 'G': case 'e': case 'E': {
148 0 : double value = current.data_.u_double_;
149 0 : int inf = std::isinf(value);
150 0 : if (inf == -1) {
151 0 : Add("-inf");
152 0 : } else if (inf == 1) {
153 0 : Add("inf");
154 0 : } else if (std::isnan(value)) {
155 0 : Add("nan");
156 : } else {
157 : EmbeddedVector<char, 28> formatted;
158 0 : SNPrintF(formatted, temp.start(), value);
159 0 : Add(formatted.start());
160 : }
161 : break;
162 : }
163 : case 'p': {
164 30627 : void* value = current.data_.u_pointer_;
165 : EmbeddedVector<char, 20> formatted;
166 30627 : SNPrintF(formatted, temp.start(), value);
167 30627 : Add(formatted.start());
168 : break;
169 : }
170 : default:
171 0 : UNREACHABLE();
172 : break;
173 : }
174 : }
175 :
176 : // Verify that the buffer is 0-terminated
177 : DCHECK_EQ(buffer_[length_], '\0');
178 : }
179 :
180 549 : void StringStream::PrintObject(Object o) {
181 549 : o->ShortPrint(this);
182 549 : if (o->IsString()) {
183 49 : if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
184 : return;
185 : }
186 872 : } else if (o->IsNumber() || o->IsOddball()) {
187 : return;
188 : }
189 337 : if (o->IsHeapObject() && object_print_mode_ == kPrintObjectVerbose) {
190 : // TODO(delphick): Consider whether we can get the isolate without using
191 : // TLS.
192 3893 : DebugObjectCache* debug_object_cache =
193 337 : Isolate::Current()->string_stream_debug_object_cache();
194 7786 : for (size_t i = 0; i < debug_object_cache->size(); i++) {
195 3647 : if ((*debug_object_cache)[i] == o) {
196 91 : Add("#%d#", static_cast<int>(i));
197 91 : return;
198 : }
199 : }
200 246 : if (debug_object_cache->size() < kMentionedObjectCacheMaxSize) {
201 246 : Add("#%d#", static_cast<int>(debug_object_cache->size()));
202 492 : debug_object_cache->push_back(HeapObject::cast(o));
203 : } else {
204 0 : Add("@%p", o);
205 : }
206 : }
207 : }
208 :
209 83353 : std::unique_ptr<char[]> StringStream::ToCString() const {
210 83353 : char* str = NewArray<char>(length_ + 1);
211 83353 : MemCopy(str, buffer_, length_);
212 83353 : str[length_] = '\0';
213 83353 : return std::unique_ptr<char[]>(str);
214 : }
215 :
216 :
217 17 : void StringStream::Log(Isolate* isolate) {
218 17 : LOG(isolate, StringEvent("StackDump", buffer_));
219 17 : }
220 :
221 :
222 17 : void StringStream::OutputToFile(FILE* out) {
223 : // Dump the output to stdout, but make sure to break it up into
224 : // manageable chunks to avoid losing parts of the output in the OS
225 : // printing code. This is a problem on Windows in particular; see
226 : // the VPrint() function implementations in platform-win32.cc.
227 : unsigned position = 0;
228 63 : for (unsigned next; (next = position + 2048) < length_; position = next) {
229 29 : char save = buffer_[next];
230 29 : buffer_[next] = '\0';
231 29 : internal::PrintF(out, "%s", &buffer_[position]);
232 29 : buffer_[next] = save;
233 : }
234 17 : internal::PrintF(out, "%s", &buffer_[position]);
235 17 : }
236 :
237 :
238 0 : Handle<String> StringStream::ToString(Isolate* isolate) {
239 : return isolate->factory()->NewStringFromUtf8(
240 0 : Vector<const char>(buffer_, length_)).ToHandleChecked();
241 : }
242 :
243 :
244 69 : void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
245 : isolate->set_string_stream_current_security_token(Object());
246 23 : if (isolate->string_stream_debug_object_cache() == nullptr) {
247 15 : isolate->set_string_stream_debug_object_cache(new DebugObjectCache());
248 : }
249 : isolate->string_stream_debug_object_cache()->clear();
250 23 : }
251 :
252 :
253 : #ifdef DEBUG
254 : bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
255 : return object_print_mode_ == kPrintObjectConcise ||
256 : isolate->string_stream_debug_object_cache()->size() == 0;
257 : }
258 : #endif
259 :
260 51355 : bool StringStream::Put(String str) { return Put(str, 0, str->length()); }
261 :
262 25857 : bool StringStream::Put(String str, int start, int end) {
263 25857 : StringCharacterStream stream(str, start);
264 191988 : for (int i = start; i < end && stream.HasMore(); i++) {
265 166131 : uint16_t c = stream.GetNext();
266 166131 : if (c >= 127 || c < 32) {
267 : c = '?';
268 : }
269 166131 : if (!Put(static_cast<char>(c))) {
270 : return false; // Output was truncated.
271 : }
272 : }
273 : return true;
274 : }
275 :
276 139 : void StringStream::PrintName(Object name) {
277 139 : if (name->IsString()) {
278 : String str = String::cast(name);
279 139 : if (str->length() > 0) {
280 : Put(str);
281 : } else {
282 25 : Add("/* anonymous */");
283 : }
284 : } else {
285 0 : Add("%o", name);
286 : }
287 139 : }
288 :
289 205 : void StringStream::PrintUsingMap(JSObject js_object) {
290 205 : Map map = js_object->map();
291 : int real_size = map->NumberOfOwnDescriptors();
292 205 : DescriptorArray descs = map->instance_descriptors();
293 639 : for (int i = 0; i < real_size; i++) {
294 434 : PropertyDetails details = descs->GetDetails(i);
295 434 : if (details.location() == kField) {
296 : DCHECK_EQ(kData, details.kind());
297 245 : Object key = descs->GetKey(i);
298 245 : if (key->IsString() || key->IsNumber()) {
299 : int len = 3;
300 245 : if (key->IsString()) {
301 : len = String::cast(key)->length();
302 : }
303 2499 : for (; len < 18; len++)
304 2499 : Put(' ');
305 245 : if (key->IsString()) {
306 : Put(String::cast(key));
307 : } else {
308 0 : key->ShortPrint();
309 : }
310 245 : Add(": ");
311 245 : FieldIndex index = FieldIndex::ForDescriptor(map, i);
312 245 : if (js_object->IsUnboxedDoubleField(index)) {
313 : double value = js_object->RawFastDoublePropertyAt(index);
314 0 : Add("<unboxed double> %.16g\n", FmtElm(value));
315 : } else {
316 245 : Object value = js_object->RawFastPropertyAt(index);
317 245 : Add("%o\n", value);
318 : }
319 : }
320 : }
321 : }
322 205 : }
323 :
324 42 : void StringStream::PrintFixedArray(FixedArray array, unsigned int limit) {
325 42 : ReadOnlyRoots roots = array->GetReadOnlyRoots();
326 112 : for (unsigned int i = 0; i < 10 && i < limit; i++) {
327 140 : Object element = array->get(i);
328 70 : if (element->IsTheHole(roots)) continue;
329 1190 : for (int len = 1; len < 18; len++) {
330 1190 : Put(' ');
331 : }
332 70 : Add("%d: %o\n", i, array->get(i));
333 : }
334 42 : if (limit >= 10) {
335 0 : Add(" ...\n");
336 : }
337 42 : }
338 :
339 0 : void StringStream::PrintByteArray(ByteArray byte_array) {
340 0 : unsigned int limit = byte_array->length();
341 0 : for (unsigned int i = 0; i < 10 && i < limit; i++) {
342 0 : byte b = byte_array->get(i);
343 0 : Add(" %d: %3d 0x%02x", i, b, b);
344 0 : if (b >= ' ' && b <= '~') {
345 0 : Add(" '%c'", b);
346 0 : } else if (b == '\n') {
347 0 : Add(" '\n'");
348 0 : } else if (b == '\r') {
349 0 : Add(" '\r'");
350 0 : } else if (b >= 1 && b <= 26) {
351 0 : Add(" ^%c", b + 'A' - 1);
352 : }
353 0 : Add("\n");
354 : }
355 0 : if (limit >= 10) {
356 0 : Add(" ...\n");
357 : }
358 0 : }
359 :
360 16 : void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
361 16 : if (object_print_mode_ == kPrintObjectConcise) return;
362 227 : DebugObjectCache* debug_object_cache =
363 : isolate->string_stream_debug_object_cache();
364 8 : Add("==== Key ============================================\n\n");
365 454 : for (size_t i = 0; i < debug_object_cache->size(); i++) {
366 219 : HeapObject printee = (*debug_object_cache)[i];
367 : Add(" #%d# %p: ", static_cast<int>(i),
368 219 : reinterpret_cast<void*>(printee->ptr()));
369 219 : printee->ShortPrint(this);
370 219 : Add("\n");
371 219 : if (printee->IsJSObject()) {
372 205 : if (printee->IsJSValue()) {
373 0 : Add(" value(): %o\n", JSValue::cast(printee)->value());
374 : }
375 205 : PrintUsingMap(JSObject::cast(printee));
376 205 : if (printee->IsJSArray()) {
377 105 : JSArray array = JSArray::cast(printee);
378 105 : if (array->HasObjectElements()) {
379 56 : unsigned int limit = FixedArray::cast(array->elements())->length();
380 : unsigned int length =
381 28 : static_cast<uint32_t>(JSArray::cast(array)->length()->Number());
382 28 : if (length < limit) limit = length;
383 56 : PrintFixedArray(FixedArray::cast(array->elements()), limit);
384 : }
385 : }
386 14 : } else if (printee->IsByteArray()) {
387 0 : PrintByteArray(ByteArray::cast(printee));
388 14 : } else if (printee->IsFixedArray()) {
389 14 : unsigned int limit = FixedArray::cast(printee)->length();
390 14 : PrintFixedArray(FixedArray::cast(printee), limit);
391 : }
392 : }
393 : }
394 :
395 66 : void StringStream::PrintSecurityTokenIfChanged(JSFunction fun) {
396 66 : Object token = fun->native_context()->security_token();
397 : Isolate* isolate = fun->GetIsolate();
398 66 : if (token != isolate->string_stream_current_security_token()) {
399 17 : Add("Security context: %o\n", token);
400 : isolate->set_string_stream_current_security_token(token);
401 : }
402 66 : }
403 :
404 66 : void StringStream::PrintFunction(JSFunction fun, Object receiver, Code* code) {
405 66 : PrintPrototype(fun, receiver);
406 66 : *code = fun->code();
407 66 : }
408 :
409 66 : void StringStream::PrintPrototype(JSFunction fun, Object receiver) {
410 66 : Object name = fun->shared()->Name();
411 : bool print_name = false;
412 : Isolate* isolate = fun->GetIsolate();
413 198 : if (receiver->IsNullOrUndefined(isolate) || receiver->IsTheHole(isolate) ||
414 : receiver->IsJSProxy()) {
415 : print_name = true;
416 66 : } else if (!isolate->context().is_null()) {
417 66 : if (!receiver->IsJSObject()) {
418 0 : receiver = receiver->GetPrototypeChainRootMap(isolate)->prototype();
419 : }
420 :
421 360 : for (PrototypeIterator iter(isolate, JSObject::cast(receiver),
422 : kStartAtReceiver);
423 228 : !iter.IsAtEnd(); iter.Advance()) {
424 474 : if (iter.GetCurrent()->IsJSProxy()) break;
425 237 : Object key = iter.GetCurrent<JSObject>()->SlowReverseLookup(fun);
426 237 : if (!key->IsUndefined(isolate)) {
427 18 : if (!name->IsString() ||
428 18 : !key->IsString() ||
429 27 : !String::cast(name)->Equals(String::cast(key))) {
430 : print_name = true;
431 : }
432 18 : if (name->IsString() && String::cast(name)->length() == 0) {
433 : print_name = false;
434 : }
435 9 : name = key;
436 9 : break;
437 : }
438 : }
439 : }
440 66 : PrintName(name);
441 : // Also known as - if the name in the function doesn't match the name under
442 : // which it was looked up.
443 66 : if (print_name) {
444 0 : Add("(aka ");
445 0 : PrintName(fun->shared()->Name());
446 0 : Put(')');
447 : }
448 66 : }
449 :
450 91539 : char* HeapStringAllocator::grow(unsigned* bytes) {
451 91539 : unsigned new_bytes = *bytes * 2;
452 : // Check for overflow.
453 91539 : if (new_bytes <= *bytes) {
454 0 : return space_;
455 : }
456 91539 : char* new_space = NewArray<char>(new_bytes);
457 91539 : if (new_space == nullptr) {
458 0 : return space_;
459 : }
460 91539 : MemCopy(new_space, space_, *bytes);
461 91539 : *bytes = new_bytes;
462 91539 : DeleteArray(space_);
463 91539 : space_ = new_space;
464 91539 : return new_space;
465 : }
466 :
467 :
468 : } // namespace internal
469 178779 : } // namespace v8
|