1 // Copyright 2006-2008 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "v8.h" 29 30 #include "factory.h" 31 #include "string-stream.h" 32 33 namespace v8 { 34 namespace internal { 35 36 static const int kMentionedObjectCacheMaxSize = 256; 37 38 char* HeapStringAllocator::allocate(unsigned bytes) { 39 space_ = NewArray<char>(bytes); 40 return space_; 41 } 42 43 44 NoAllocationStringAllocator::NoAllocationStringAllocator(char* memory, 45 unsigned size) { 46 size_ = size; 47 space_ = memory; 48 } 49 50 51 bool StringStream::Put(char c) { 52 if (full()) return false; 53 ASSERT(length_ < capacity_); 54 // Since the trailing '\0' is not accounted for in length_ fullness is 55 // indicated by a difference of 1 between length_ and capacity_. Thus when 56 // reaching a difference of 2 we need to grow the buffer. 57 if (length_ == capacity_ - 2) { 58 unsigned new_capacity = capacity_; 59 char* new_buffer = allocator_->grow(&new_capacity); 60 if (new_capacity > capacity_) { 61 capacity_ = new_capacity; 62 buffer_ = new_buffer; 63 } else { 64 // Reached the end of the available buffer. 65 ASSERT(capacity_ >= 5); 66 length_ = capacity_ - 1; // Indicate fullness of the stream. 67 buffer_[length_ - 4] = '.'; 68 buffer_[length_ - 3] = '.'; 69 buffer_[length_ - 2] = '.'; 70 buffer_[length_ - 1] = '\n'; 71 buffer_[length_] = '\0'; 72 return false; 73 } 74 } 75 buffer_[length_] = c; 76 buffer_[length_ + 1] = '\0'; 77 length_++; 78 return true; 79 } 80 81 82 // A control character is one that configures a format element. For 83 // instance, in %.5s, .5 are control characters. 84 static bool IsControlChar(char c) { 85 switch (c) { 86 case '0': case '1': case '2': case '3': case '4': case '5': 87 case '6': case '7': case '8': case '9': case '.': case '-': 88 return true; 89 default: 90 return false; 91 } 92 } 93 94 95 void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) { 96 // If we already ran out of space then return immediately. 97 if (full()) return; 98 int offset = 0; 99 int elm = 0; 100 while (offset < format.length()) { 101 if (format[offset] != '%' || elm == elms.length()) { 102 Put(format[offset]); 103 offset++; 104 continue; 105 } 106 // Read this formatting directive into a temporary buffer 107 EmbeddedVector<char, 24> temp; 108 int format_length = 0; 109 // Skip over the whole control character sequence until the 110 // format element type 111 temp[format_length++] = format[offset++]; 112 while (offset < format.length() && IsControlChar(format[offset])) 113 temp[format_length++] = format[offset++]; 114 if (offset >= format.length()) 115 return; 116 char type = format[offset]; 117 temp[format_length++] = type; 118 temp[format_length] = '\0'; 119 offset++; 120 FmtElm current = elms[elm++]; 121 switch (type) { 122 case 's': { 123 ASSERT_EQ(FmtElm::C_STR, current.type_); 124 const char* value = current.data_.u_c_str_; 125 Add(value); 126 break; 127 } 128 case 'w': { 129 ASSERT_EQ(FmtElm::LC_STR, current.type_); 130 Vector<const uc16> value = *current.data_.u_lc_str_; 131 for (int i = 0; i < value.length(); i++) 132 Put(static_cast<char>(value[i])); 133 break; 134 } 135 case 'o': { 136 ASSERT_EQ(FmtElm::OBJ, current.type_); 137 Object* obj = current.data_.u_obj_; 138 PrintObject(obj); 139 break; 140 } 141 case 'k': { 142 ASSERT_EQ(FmtElm::INT, current.type_); 143 int value = current.data_.u_int_; 144 if (0x20 <= value && value <= 0x7F) { 145 Put(value); 146 } else if (value <= 0xff) { 147 Add("\\x%02x", value); 148 } else { 149 Add("\\u%04x", value); 150 } 151 break; 152 } 153 case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': { 154 int value = current.data_.u_int_; 155 EmbeddedVector<char, 24> formatted; 156 int length = OS::SNPrintF(formatted, temp.start(), value); 157 Add(Vector<const char>(formatted.start(), length)); 158 break; 159 } 160 case 'f': case 'g': case 'G': case 'e': case 'E': { 161 double value = current.data_.u_double_; 162 EmbeddedVector<char, 28> formatted; 163 OS::SNPrintF(formatted, temp.start(), value); 164 Add(formatted.start()); 165 break; 166 } 167 case 'p': { 168 void* value = current.data_.u_pointer_; 169 EmbeddedVector<char, 20> formatted; 170 OS::SNPrintF(formatted, temp.start(), value); 171 Add(formatted.start()); 172 break; 173 } 174 default: 175 UNREACHABLE(); 176 break; 177 } 178 } 179 180 // Verify that the buffer is 0-terminated 181 ASSERT(buffer_[length_] == '\0'); 182 } 183 184 185 void StringStream::PrintObject(Object* o) { 186 o->ShortPrint(this); 187 if (o->IsString()) { 188 if (String::cast(o)->length() <= String::kMaxShortPrintLength) { 189 return; 190 } 191 } else if (o->IsNumber() || o->IsOddball()) { 192 return; 193 } 194 if (o->IsHeapObject()) { 195 DebugObjectCache* debug_object_cache = Isolate::Current()-> 196 string_stream_debug_object_cache(); 197 for (int i = 0; i < debug_object_cache->length(); i++) { 198 if ((*debug_object_cache)[i] == o) { 199 Add("#%d#", i); 200 return; 201 } 202 } 203 if (debug_object_cache->length() < kMentionedObjectCacheMaxSize) { 204 Add("#%d#", debug_object_cache->length()); 205 debug_object_cache->Add(HeapObject::cast(o)); 206 } else { 207 Add("@%p", o); 208 } 209 } 210 } 211 212 213 void StringStream::Add(const char* format) { 214 Add(CStrVector(format)); 215 } 216 217 218 void StringStream::Add(Vector<const char> format) { 219 Add(format, Vector<FmtElm>::empty()); 220 } 221 222 223 void StringStream::Add(const char* format, FmtElm arg0) { 224 const char argc = 1; 225 FmtElm argv[argc] = { arg0 }; 226 Add(CStrVector(format), Vector<FmtElm>(argv, argc)); 227 } 228 229 230 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1) { 231 const char argc = 2; 232 FmtElm argv[argc] = { arg0, arg1 }; 233 Add(CStrVector(format), Vector<FmtElm>(argv, argc)); 234 } 235 236 237 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1, 238 FmtElm arg2) { 239 const char argc = 3; 240 FmtElm argv[argc] = { arg0, arg1, arg2 }; 241 Add(CStrVector(format), Vector<FmtElm>(argv, argc)); 242 } 243 244 245 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1, 246 FmtElm arg2, FmtElm arg3) { 247 const char argc = 4; 248 FmtElm argv[argc] = { arg0, arg1, arg2, arg3 }; 249 Add(CStrVector(format), Vector<FmtElm>(argv, argc)); 250 } 251 252 253 SmartPointer<const char> StringStream::ToCString() const { 254 char* str = NewArray<char>(length_ + 1); 255 memcpy(str, buffer_, length_); 256 str[length_] = '\0'; 257 return SmartPointer<const char>(str); 258 } 259 260 261 void StringStream::Log() { 262 LOG(ISOLATE, StringEvent("StackDump", buffer_)); 263 } 264 265 266 void StringStream::OutputToFile(FILE* out) { 267 // Dump the output to stdout, but make sure to break it up into 268 // manageable chunks to avoid losing parts of the output in the OS 269 // printing code. This is a problem on Windows in particular; see 270 // the VPrint() function implementations in platform-win32.cc. 271 unsigned position = 0; 272 for (unsigned next; (next = position + 2048) < length_; position = next) { 273 char save = buffer_[next]; 274 buffer_[next] = '\0'; 275 internal::PrintF(out, "%s", &buffer_[position]); 276 buffer_[next] = save; 277 } 278 internal::PrintF(out, "%s", &buffer_[position]); 279 } 280 281 282 Handle<String> StringStream::ToString() { 283 return FACTORY->NewStringFromUtf8(Vector<const char>(buffer_, length_)); 284 } 285 286 287 void StringStream::ClearMentionedObjectCache() { 288 Isolate* isolate = Isolate::Current(); 289 isolate->set_string_stream_current_security_token(NULL); 290 if (isolate->string_stream_debug_object_cache() == NULL) { 291 isolate->set_string_stream_debug_object_cache( 292 new List<HeapObject*, PreallocatedStorage>(0)); 293 } 294 isolate->string_stream_debug_object_cache()->Clear(); 295 } 296 297 298 #ifdef DEBUG 299 bool StringStream::IsMentionedObjectCacheClear() { 300 return ( 301 Isolate::Current()->string_stream_debug_object_cache()->length() == 0); 302 } 303 #endif 304 305 306 bool StringStream::Put(String* str) { 307 return Put(str, 0, str->length()); 308 } 309 310 311 bool StringStream::Put(String* str, int start, int end) { 312 StringInputBuffer name_buffer(str); 313 name_buffer.Seek(start); 314 for (int i = start; i < end && name_buffer.has_more(); i++) { 315 int c = name_buffer.GetNext(); 316 if (c >= 127 || c < 32) { 317 c = '?'; 318 } 319 if (!Put(c)) { 320 return false; // Output was truncated. 321 } 322 } 323 return true; 324 } 325 326 327 void StringStream::PrintName(Object* name) { 328 if (name->IsString()) { 329 String* str = String::cast(name); 330 if (str->length() > 0) { 331 Put(str); 332 } else { 333 Add("/* anonymous */"); 334 } 335 } else { 336 Add("%o", name); 337 } 338 } 339 340 341 void StringStream::PrintUsingMap(JSObject* js_object) { 342 Map* map = js_object->map(); 343 if (!HEAP->Contains(map) || 344 !map->IsHeapObject() || 345 !map->IsMap()) { 346 Add("<Invalid map>\n"); 347 return; 348 } 349 DescriptorArray* descs = map->instance_descriptors(); 350 for (int i = 0; i < descs->number_of_descriptors(); i++) { 351 switch (descs->GetType(i)) { 352 case FIELD: { 353 Object* key = descs->GetKey(i); 354 if (key->IsString() || key->IsNumber()) { 355 int len = 3; 356 if (key->IsString()) { 357 len = String::cast(key)->length(); 358 } 359 for (; len < 18; len++) 360 Put(' '); 361 if (key->IsString()) { 362 Put(String::cast(key)); 363 } else { 364 key->ShortPrint(); 365 } 366 Add(": "); 367 Object* value = js_object->FastPropertyAt(descs->GetFieldIndex(i)); 368 Add("%o\n", value); 369 } 370 } 371 break; 372 default: 373 break; 374 } 375 } 376 } 377 378 379 void StringStream::PrintFixedArray(FixedArray* array, unsigned int limit) { 380 Heap* heap = HEAP; 381 for (unsigned int i = 0; i < 10 && i < limit; i++) { 382 Object* element = array->get(i); 383 if (element != heap->the_hole_value()) { 384 for (int len = 1; len < 18; len++) 385 Put(' '); 386 Add("%d: %o\n", i, array->get(i)); 387 } 388 } 389 if (limit >= 10) { 390 Add(" ...\n"); 391 } 392 } 393 394 395 void StringStream::PrintByteArray(ByteArray* byte_array) { 396 unsigned int limit = byte_array->length(); 397 for (unsigned int i = 0; i < 10 && i < limit; i++) { 398 byte b = byte_array->get(i); 399 Add(" %d: %3d 0x%02x", i, b, b); 400 if (b >= ' ' && b <= '~') { 401 Add(" '%c'", b); 402 } else if (b == '\n') { 403 Add(" '\n'"); 404 } else if (b == '\r') { 405 Add(" '\r'"); 406 } else if (b >= 1 && b <= 26) { 407 Add(" ^%c", b + 'A' - 1); 408 } 409 Add("\n"); 410 } 411 if (limit >= 10) { 412 Add(" ...\n"); 413 } 414 } 415 416 417 void StringStream::PrintMentionedObjectCache() { 418 DebugObjectCache* debug_object_cache = 419 Isolate::Current()->string_stream_debug_object_cache(); 420 Add("==== Key ============================================\n\n"); 421 for (int i = 0; i < debug_object_cache->length(); i++) { 422 HeapObject* printee = (*debug_object_cache)[i]; 423 Add(" #%d# %p: ", i, printee); 424 printee->ShortPrint(this); 425 Add("\n"); 426 if (printee->IsJSObject()) { 427 if (printee->IsJSValue()) { 428 Add(" value(): %o\n", JSValue::cast(printee)->value()); 429 } 430 PrintUsingMap(JSObject::cast(printee)); 431 if (printee->IsJSArray()) { 432 JSArray* array = JSArray::cast(printee); 433 if (array->HasFastElements()) { 434 unsigned int limit = FixedArray::cast(array->elements())->length(); 435 unsigned int length = 436 static_cast<uint32_t>(JSArray::cast(array)->length()->Number()); 437 if (length < limit) limit = length; 438 PrintFixedArray(FixedArray::cast(array->elements()), limit); 439 } 440 } 441 } else if (printee->IsByteArray()) { 442 PrintByteArray(ByteArray::cast(printee)); 443 } else if (printee->IsFixedArray()) { 444 unsigned int limit = FixedArray::cast(printee)->length(); 445 PrintFixedArray(FixedArray::cast(printee), limit); 446 } 447 } 448 } 449 450 451 void StringStream::PrintSecurityTokenIfChanged(Object* f) { 452 Isolate* isolate = Isolate::Current(); 453 Heap* heap = isolate->heap(); 454 if (!f->IsHeapObject() || !heap->Contains(HeapObject::cast(f))) { 455 return; 456 } 457 Map* map = HeapObject::cast(f)->map(); 458 if (!map->IsHeapObject() || 459 !heap->Contains(map) || 460 !map->IsMap() || 461 !f->IsJSFunction()) { 462 return; 463 } 464 465 JSFunction* fun = JSFunction::cast(f); 466 Object* perhaps_context = fun->unchecked_context(); 467 if (perhaps_context->IsHeapObject() && 468 heap->Contains(HeapObject::cast(perhaps_context)) && 469 perhaps_context->IsContext()) { 470 Context* context = fun->context(); 471 if (!heap->Contains(context)) { 472 Add("(Function context is outside heap)\n"); 473 return; 474 } 475 Object* token = context->global_context()->security_token(); 476 if (token != isolate->string_stream_current_security_token()) { 477 Add("Security context: %o\n", token); 478 isolate->set_string_stream_current_security_token(token); 479 } 480 } else { 481 Add("(Function context is corrupt)\n"); 482 } 483 } 484 485 486 void StringStream::PrintFunction(Object* f, Object* receiver, Code** code) { 487 if (f->IsHeapObject() && 488 HEAP->Contains(HeapObject::cast(f)) && 489 HEAP->Contains(HeapObject::cast(f)->map()) && 490 HeapObject::cast(f)->map()->IsMap()) { 491 if (f->IsJSFunction()) { 492 JSFunction* fun = JSFunction::cast(f); 493 // Common case: on-stack function present and resolved. 494 PrintPrototype(fun, receiver); 495 *code = fun->code(); 496 } else if (f->IsSymbol()) { 497 // Unresolved and megamorphic calls: Instead of the function 498 // we have the function name on the stack. 499 PrintName(f); 500 Add("/* unresolved */ "); 501 } else { 502 // Unless this is the frame of a built-in function, we should always have 503 // the callee function or name on the stack. If we don't, we have a 504 // problem or a change of the stack frame layout. 505 Add("%o", f); 506 Add("/* warning: no JSFunction object or function name found */ "); 507 } 508 /* } else if (is_trampoline()) { 509 Print("trampoline "); 510 */ 511 } else { 512 if (!f->IsHeapObject()) { 513 Add("/* warning: 'function' was not a heap object */ "); 514 return; 515 } 516 if (!HEAP->Contains(HeapObject::cast(f))) { 517 Add("/* warning: 'function' was not on the heap */ "); 518 return; 519 } 520 if (!HEAP->Contains(HeapObject::cast(f)->map())) { 521 Add("/* warning: function's map was not on the heap */ "); 522 return; 523 } 524 if (!HeapObject::cast(f)->map()->IsMap()) { 525 Add("/* warning: function's map was not a valid map */ "); 526 return; 527 } 528 Add("/* warning: Invalid JSFunction object found */ "); 529 } 530 } 531 532 533 void StringStream::PrintPrototype(JSFunction* fun, Object* receiver) { 534 Object* name = fun->shared()->name(); 535 bool print_name = false; 536 Heap* heap = HEAP; 537 for (Object* p = receiver; p != heap->null_value(); p = p->GetPrototype()) { 538 if (p->IsJSObject()) { 539 Object* key = JSObject::cast(p)->SlowReverseLookup(fun); 540 if (key != heap->undefined_value()) { 541 if (!name->IsString() || 542 !key->IsString() || 543 !String::cast(name)->Equals(String::cast(key))) { 544 print_name = true; 545 } 546 if (name->IsString() && String::cast(name)->length() == 0) { 547 print_name = false; 548 } 549 name = key; 550 } 551 } else { 552 print_name = true; 553 } 554 } 555 PrintName(name); 556 // Also known as - if the name in the function doesn't match the name under 557 // which it was looked up. 558 if (print_name) { 559 Add("(aka "); 560 PrintName(fun->shared()->name()); 561 Put(')'); 562 } 563 } 564 565 566 char* HeapStringAllocator::grow(unsigned* bytes) { 567 unsigned new_bytes = *bytes * 2; 568 // Check for overflow. 569 if (new_bytes <= *bytes) { 570 return space_; 571 } 572 char* new_space = NewArray<char>(new_bytes); 573 if (new_space == NULL) { 574 return space_; 575 } 576 memcpy(new_space, space_, *bytes); 577 *bytes = new_bytes; 578 DeleteArray(space_); 579 space_ = new_space; 580 return new_space; 581 } 582 583 584 // Only grow once to the maximum allowable size. 585 char* NoAllocationStringAllocator::grow(unsigned* bytes) { 586 ASSERT(size_ >= *bytes); 587 *bytes = size_; 588 return space_; 589 } 590 591 592 } } // namespace v8::internal 593