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