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/prototype.h"
13
14 namespace v8 {
15 namespace internal {
16
17 static const int kMentionedObjectCacheMaxSize = 256;
18
allocate(unsigned bytes)19 char* HeapStringAllocator::allocate(unsigned bytes) {
20 space_ = NewArray<char>(bytes);
21 return space_;
22 }
23
24
allocate(unsigned bytes)25 char* FixedStringAllocator::allocate(unsigned bytes) {
26 CHECK_LE(bytes, length_);
27 return buffer_;
28 }
29
30
grow(unsigned * old)31 char* FixedStringAllocator::grow(unsigned* old) {
32 *old = length_;
33 return buffer_;
34 }
35
36
Put(char c)37 bool StringStream::Put(char c) {
38 if (full()) return false;
39 DCHECK(length_ < capacity_);
40 // Since the trailing '\0' is not accounted for in length_ fullness is
41 // indicated by a difference of 1 between length_ and capacity_. Thus when
42 // reaching a difference of 2 we need to grow the buffer.
43 if (length_ == capacity_ - 2) {
44 unsigned new_capacity = capacity_;
45 char* new_buffer = allocator_->grow(&new_capacity);
46 if (new_capacity > capacity_) {
47 capacity_ = new_capacity;
48 buffer_ = new_buffer;
49 } else {
50 // Reached the end of the available buffer.
51 DCHECK(capacity_ >= 5);
52 length_ = capacity_ - 1; // Indicate fullness of the stream.
53 buffer_[length_ - 4] = '.';
54 buffer_[length_ - 3] = '.';
55 buffer_[length_ - 2] = '.';
56 buffer_[length_ - 1] = '\n';
57 buffer_[length_] = '\0';
58 return false;
59 }
60 }
61 buffer_[length_] = c;
62 buffer_[length_ + 1] = '\0';
63 length_++;
64 return true;
65 }
66
67
68 // A control character is one that configures a format element. For
69 // instance, in %.5s, .5 are control characters.
IsControlChar(char c)70 static bool IsControlChar(char c) {
71 switch (c) {
72 case '0': case '1': case '2': case '3': case '4': case '5':
73 case '6': case '7': case '8': case '9': case '.': case '-':
74 return true;
75 default:
76 return false;
77 }
78 }
79
80
Add(Vector<const char> format,Vector<FmtElm> elms)81 void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
82 // If we already ran out of space then return immediately.
83 if (full()) return;
84 int offset = 0;
85 int elm = 0;
86 while (offset < format.length()) {
87 if (format[offset] != '%' || elm == elms.length()) {
88 Put(format[offset]);
89 offset++;
90 continue;
91 }
92 // Read this formatting directive into a temporary buffer
93 EmbeddedVector<char, 24> temp;
94 int format_length = 0;
95 // Skip over the whole control character sequence until the
96 // format element type
97 temp[format_length++] = format[offset++];
98 while (offset < format.length() && IsControlChar(format[offset]))
99 temp[format_length++] = format[offset++];
100 if (offset >= format.length())
101 return;
102 char type = format[offset];
103 temp[format_length++] = type;
104 temp[format_length] = '\0';
105 offset++;
106 FmtElm current = elms[elm++];
107 switch (type) {
108 case 's': {
109 DCHECK_EQ(FmtElm::C_STR, current.type_);
110 const char* value = current.data_.u_c_str_;
111 Add(value);
112 break;
113 }
114 case 'w': {
115 DCHECK_EQ(FmtElm::LC_STR, current.type_);
116 Vector<const uc16> value = *current.data_.u_lc_str_;
117 for (int i = 0; i < value.length(); i++)
118 Put(static_cast<char>(value[i]));
119 break;
120 }
121 case 'o': {
122 DCHECK_EQ(FmtElm::OBJ, current.type_);
123 Object* obj = current.data_.u_obj_;
124 PrintObject(obj);
125 break;
126 }
127 case 'k': {
128 DCHECK_EQ(FmtElm::INT, current.type_);
129 int value = current.data_.u_int_;
130 if (0x20 <= value && value <= 0x7F) {
131 Put(value);
132 } else if (value <= 0xff) {
133 Add("\\x%02x", value);
134 } else {
135 Add("\\u%04x", value);
136 }
137 break;
138 }
139 case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
140 int value = current.data_.u_int_;
141 EmbeddedVector<char, 24> formatted;
142 int length = SNPrintF(formatted, temp.start(), value);
143 Add(Vector<const char>(formatted.start(), length));
144 break;
145 }
146 case 'f': case 'g': case 'G': case 'e': case 'E': {
147 double value = current.data_.u_double_;
148 int inf = std::isinf(value);
149 if (inf == -1) {
150 Add("-inf");
151 } else if (inf == 1) {
152 Add("inf");
153 } else if (std::isnan(value)) {
154 Add("nan");
155 } else {
156 EmbeddedVector<char, 28> formatted;
157 SNPrintF(formatted, temp.start(), value);
158 Add(formatted.start());
159 }
160 break;
161 }
162 case 'p': {
163 void* value = current.data_.u_pointer_;
164 EmbeddedVector<char, 20> formatted;
165 SNPrintF(formatted, temp.start(), value);
166 Add(formatted.start());
167 break;
168 }
169 default:
170 UNREACHABLE();
171 break;
172 }
173 }
174
175 // Verify that the buffer is 0-terminated
176 DCHECK(buffer_[length_] == '\0');
177 }
178
179
PrintObject(Object * o)180 void StringStream::PrintObject(Object* o) {
181 o->ShortPrint(this);
182 if (o->IsString()) {
183 if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
184 return;
185 }
186 } else if (o->IsNumber() || o->IsOddball()) {
187 return;
188 }
189 if (o->IsHeapObject() && object_print_mode_ == kPrintObjectVerbose) {
190 HeapObject* ho = HeapObject::cast(o);
191 DebugObjectCache* debug_object_cache = ho->GetIsolate()->
192 string_stream_debug_object_cache();
193 for (int i = 0; i < debug_object_cache->length(); i++) {
194 if ((*debug_object_cache)[i] == o) {
195 Add("#%d#", i);
196 return;
197 }
198 }
199 if (debug_object_cache->length() < kMentionedObjectCacheMaxSize) {
200 Add("#%d#", debug_object_cache->length());
201 debug_object_cache->Add(HeapObject::cast(o));
202 } else {
203 Add("@%p", o);
204 }
205 }
206 }
207
208
ToCString() const209 std::unique_ptr<char[]> StringStream::ToCString() const {
210 char* str = NewArray<char>(length_ + 1);
211 MemCopy(str, buffer_, length_);
212 str[length_] = '\0';
213 return std::unique_ptr<char[]>(str);
214 }
215
216
Log(Isolate * isolate)217 void StringStream::Log(Isolate* isolate) {
218 LOG(isolate, StringEvent("StackDump", buffer_));
219 }
220
221
OutputToFile(FILE * out)222 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 for (unsigned next; (next = position + 2048) < length_; position = next) {
229 char save = buffer_[next];
230 buffer_[next] = '\0';
231 internal::PrintF(out, "%s", &buffer_[position]);
232 buffer_[next] = save;
233 }
234 internal::PrintF(out, "%s", &buffer_[position]);
235 }
236
237
ToString(Isolate * isolate)238 Handle<String> StringStream::ToString(Isolate* isolate) {
239 return isolate->factory()->NewStringFromUtf8(
240 Vector<const char>(buffer_, length_)).ToHandleChecked();
241 }
242
243
ClearMentionedObjectCache(Isolate * isolate)244 void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
245 isolate->set_string_stream_current_security_token(NULL);
246 if (isolate->string_stream_debug_object_cache() == NULL) {
247 isolate->set_string_stream_debug_object_cache(new DebugObjectCache(0));
248 }
249 isolate->string_stream_debug_object_cache()->Clear();
250 }
251
252
253 #ifdef DEBUG
IsMentionedObjectCacheClear(Isolate * isolate)254 bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
255 return object_print_mode_ == kPrintObjectConcise ||
256 isolate->string_stream_debug_object_cache()->length() == 0;
257 }
258 #endif
259
260
Put(String * str)261 bool StringStream::Put(String* str) {
262 return Put(str, 0, str->length());
263 }
264
265
Put(String * str,int start,int end)266 bool StringStream::Put(String* str, int start, int end) {
267 StringCharacterStream stream(str, start);
268 for (int i = start; i < end && stream.HasMore(); i++) {
269 uint16_t c = stream.GetNext();
270 if (c >= 127 || c < 32) {
271 c = '?';
272 }
273 if (!Put(static_cast<char>(c))) {
274 return false; // Output was truncated.
275 }
276 }
277 return true;
278 }
279
280
PrintName(Object * name)281 void StringStream::PrintName(Object* name) {
282 if (name->IsString()) {
283 String* str = String::cast(name);
284 if (str->length() > 0) {
285 Put(str);
286 } else {
287 Add("/* anonymous */");
288 }
289 } else {
290 Add("%o", name);
291 }
292 }
293
294
PrintUsingMap(JSObject * js_object)295 void StringStream::PrintUsingMap(JSObject* js_object) {
296 Map* map = js_object->map();
297 if (!js_object->GetHeap()->Contains(map) ||
298 !map->IsHeapObject() ||
299 !map->IsMap()) {
300 Add("<Invalid map>\n");
301 return;
302 }
303 int real_size = map->NumberOfOwnDescriptors();
304 DescriptorArray* descs = map->instance_descriptors();
305 for (int i = 0; i < real_size; i++) {
306 PropertyDetails details = descs->GetDetails(i);
307 if (details.location() == kField) {
308 DCHECK_EQ(kData, details.kind());
309 Object* key = descs->GetKey(i);
310 if (key->IsString() || key->IsNumber()) {
311 int len = 3;
312 if (key->IsString()) {
313 len = String::cast(key)->length();
314 }
315 for (; len < 18; len++)
316 Put(' ');
317 if (key->IsString()) {
318 Put(String::cast(key));
319 } else {
320 key->ShortPrint();
321 }
322 Add(": ");
323 FieldIndex index = FieldIndex::ForDescriptor(map, i);
324 if (js_object->IsUnboxedDoubleField(index)) {
325 double value = js_object->RawFastDoublePropertyAt(index);
326 Add("<unboxed double> %.16g\n", FmtElm(value));
327 } else {
328 Object* value = js_object->RawFastPropertyAt(index);
329 Add("%o\n", value);
330 }
331 }
332 }
333 }
334 }
335
336
PrintFixedArray(FixedArray * array,unsigned int limit)337 void StringStream::PrintFixedArray(FixedArray* array, unsigned int limit) {
338 Isolate* isolate = array->GetIsolate();
339 for (unsigned int i = 0; i < 10 && i < limit; i++) {
340 Object* element = array->get(i);
341 if (element->IsTheHole(isolate)) continue;
342 for (int len = 1; len < 18; len++) {
343 Put(' ');
344 }
345 Add("%d: %o\n", i, array->get(i));
346 }
347 if (limit >= 10) {
348 Add(" ...\n");
349 }
350 }
351
352
PrintByteArray(ByteArray * byte_array)353 void StringStream::PrintByteArray(ByteArray* byte_array) {
354 unsigned int limit = byte_array->length();
355 for (unsigned int i = 0; i < 10 && i < limit; i++) {
356 byte b = byte_array->get(i);
357 Add(" %d: %3d 0x%02x", i, b, b);
358 if (b >= ' ' && b <= '~') {
359 Add(" '%c'", b);
360 } else if (b == '\n') {
361 Add(" '\n'");
362 } else if (b == '\r') {
363 Add(" '\r'");
364 } else if (b >= 1 && b <= 26) {
365 Add(" ^%c", b + 'A' - 1);
366 }
367 Add("\n");
368 }
369 if (limit >= 10) {
370 Add(" ...\n");
371 }
372 }
373
374
PrintMentionedObjectCache(Isolate * isolate)375 void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
376 if (object_print_mode_ == kPrintObjectConcise) return;
377 DebugObjectCache* debug_object_cache =
378 isolate->string_stream_debug_object_cache();
379 Add("==== Key ============================================\n\n");
380 for (int i = 0; i < debug_object_cache->length(); i++) {
381 HeapObject* printee = (*debug_object_cache)[i];
382 Add(" #%d# %p: ", i, printee);
383 printee->ShortPrint(this);
384 Add("\n");
385 if (printee->IsJSObject()) {
386 if (printee->IsJSValue()) {
387 Add(" value(): %o\n", JSValue::cast(printee)->value());
388 }
389 PrintUsingMap(JSObject::cast(printee));
390 if (printee->IsJSArray()) {
391 JSArray* array = JSArray::cast(printee);
392 if (array->HasFastObjectElements()) {
393 unsigned int limit = FixedArray::cast(array->elements())->length();
394 unsigned int length =
395 static_cast<uint32_t>(JSArray::cast(array)->length()->Number());
396 if (length < limit) limit = length;
397 PrintFixedArray(FixedArray::cast(array->elements()), limit);
398 }
399 }
400 } else if (printee->IsByteArray()) {
401 PrintByteArray(ByteArray::cast(printee));
402 } else if (printee->IsFixedArray()) {
403 unsigned int limit = FixedArray::cast(printee)->length();
404 PrintFixedArray(FixedArray::cast(printee), limit);
405 }
406 }
407 }
408
409
PrintSecurityTokenIfChanged(Object * f)410 void StringStream::PrintSecurityTokenIfChanged(Object* f) {
411 if (!f->IsHeapObject()) return;
412 HeapObject* obj = HeapObject::cast(f);
413 Isolate* isolate = obj->GetIsolate();
414 Heap* heap = isolate->heap();
415 if (!heap->Contains(obj)) return;
416 Map* map = obj->map();
417 if (!map->IsHeapObject() ||
418 !heap->Contains(map) ||
419 !map->IsMap() ||
420 !f->IsJSFunction()) {
421 return;
422 }
423
424 JSFunction* fun = JSFunction::cast(f);
425 Object* perhaps_context = fun->context();
426 if (perhaps_context->IsHeapObject() &&
427 heap->Contains(HeapObject::cast(perhaps_context)) &&
428 perhaps_context->IsContext()) {
429 Context* context = fun->context();
430 if (!heap->Contains(context)) {
431 Add("(Function context is outside heap)\n");
432 return;
433 }
434 Object* token = context->native_context()->security_token();
435 if (token != isolate->string_stream_current_security_token()) {
436 Add("Security context: %o\n", token);
437 isolate->set_string_stream_current_security_token(token);
438 }
439 } else {
440 Add("(Function context is corrupt)\n");
441 }
442 }
443
444
PrintFunction(Object * f,Object * receiver,Code ** code)445 void StringStream::PrintFunction(Object* f, Object* receiver, Code** code) {
446 if (!f->IsHeapObject()) {
447 Add("/* warning: 'function' was not a heap object */ ");
448 return;
449 }
450 Heap* heap = HeapObject::cast(f)->GetHeap();
451 if (!heap->Contains(HeapObject::cast(f))) {
452 Add("/* warning: 'function' was not on the heap */ ");
453 return;
454 }
455 if (!heap->Contains(HeapObject::cast(f)->map())) {
456 Add("/* warning: function's map was not on the heap */ ");
457 return;
458 }
459 if (!HeapObject::cast(f)->map()->IsMap()) {
460 Add("/* warning: function's map was not a valid map */ ");
461 return;
462 }
463 if (f->IsJSFunction()) {
464 JSFunction* fun = JSFunction::cast(f);
465 // Common case: on-stack function present and resolved.
466 PrintPrototype(fun, receiver);
467 *code = fun->code();
468 } else if (f->IsInternalizedString()) {
469 // Unresolved and megamorphic calls: Instead of the function
470 // we have the function name on the stack.
471 PrintName(f);
472 Add("/* unresolved */ ");
473 } else {
474 // Unless this is the frame of a built-in function, we should always have
475 // the callee function or name on the stack. If we don't, we have a
476 // problem or a change of the stack frame layout.
477 Add("%o", f);
478 Add("/* warning: no JSFunction object or function name found */ ");
479 }
480 }
481
482
PrintPrototype(JSFunction * fun,Object * receiver)483 void StringStream::PrintPrototype(JSFunction* fun, Object* receiver) {
484 Object* name = fun->shared()->name();
485 bool print_name = false;
486 Isolate* isolate = fun->GetIsolate();
487 if (receiver->IsNullOrUndefined(isolate) || receiver->IsTheHole(isolate) ||
488 receiver->IsJSProxy()) {
489 print_name = true;
490 } else if (isolate->context() != nullptr) {
491 if (!receiver->IsJSObject()) {
492 receiver = receiver->GetPrototypeChainRootMap(isolate)->prototype();
493 }
494
495 for (PrototypeIterator iter(isolate, JSObject::cast(receiver),
496 kStartAtReceiver);
497 !iter.IsAtEnd(); iter.Advance()) {
498 if (iter.GetCurrent()->IsJSProxy()) break;
499 Object* key = iter.GetCurrent<JSObject>()->SlowReverseLookup(fun);
500 if (!key->IsUndefined(isolate)) {
501 if (!name->IsString() ||
502 !key->IsString() ||
503 !String::cast(name)->Equals(String::cast(key))) {
504 print_name = true;
505 }
506 if (name->IsString() && String::cast(name)->length() == 0) {
507 print_name = false;
508 }
509 name = key;
510 break;
511 }
512 }
513 }
514 PrintName(name);
515 // Also known as - if the name in the function doesn't match the name under
516 // which it was looked up.
517 if (print_name) {
518 Add("(aka ");
519 PrintName(fun->shared()->name());
520 Put(')');
521 }
522 }
523
524
grow(unsigned * bytes)525 char* HeapStringAllocator::grow(unsigned* bytes) {
526 unsigned new_bytes = *bytes * 2;
527 // Check for overflow.
528 if (new_bytes <= *bytes) {
529 return space_;
530 }
531 char* new_space = NewArray<char>(new_bytes);
532 if (new_space == NULL) {
533 return space_;
534 }
535 MemCopy(new_space, space_, *bytes);
536 *bytes = new_bytes;
537 DeleteArray(space_);
538 space_ = new_space;
539 return new_space;
540 }
541
542
543 } // namespace internal
544 } // namespace v8
545