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