V8 Project
string-stream.cc
Go to the documentation of this file.
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 "src/handles-inl.h"
8 #include "src/prototype.h"
9 
10 namespace v8 {
11 namespace internal {
12 
13 static const int kMentionedObjectCacheMaxSize = 256;
14 
15 char* HeapStringAllocator::allocate(unsigned bytes) {
16  space_ = NewArray<char>(bytes);
17  return space_;
18 }
19 
20 
21 bool StringStream::Put(char c) {
22  if (full()) return false;
23  DCHECK(length_ < capacity_);
24  // Since the trailing '\0' is not accounted for in length_ fullness is
25  // indicated by a difference of 1 between length_ and capacity_. Thus when
26  // reaching a difference of 2 we need to grow the buffer.
27  if (length_ == capacity_ - 2) {
28  unsigned new_capacity = capacity_;
29  char* new_buffer = allocator_->grow(&new_capacity);
30  if (new_capacity > capacity_) {
31  capacity_ = new_capacity;
32  buffer_ = new_buffer;
33  } else {
34  // Reached the end of the available buffer.
35  DCHECK(capacity_ >= 5);
36  length_ = capacity_ - 1; // Indicate fullness of the stream.
37  buffer_[length_ - 4] = '.';
38  buffer_[length_ - 3] = '.';
39  buffer_[length_ - 2] = '.';
40  buffer_[length_ - 1] = '\n';
41  buffer_[length_] = '\0';
42  return false;
43  }
44  }
45  buffer_[length_] = c;
46  buffer_[length_ + 1] = '\0';
47  length_++;
48  return true;
49 }
50 
51 
52 // A control character is one that configures a format element. For
53 // instance, in %.5s, .5 are control characters.
54 static bool IsControlChar(char c) {
55  switch (c) {
56  case '0': case '1': case '2': case '3': case '4': case '5':
57  case '6': case '7': case '8': case '9': case '.': case '-':
58  return true;
59  default:
60  return false;
61  }
62 }
63 
64 
65 void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
66  // If we already ran out of space then return immediately.
67  if (full()) return;
68  int offset = 0;
69  int elm = 0;
70  while (offset < format.length()) {
71  if (format[offset] != '%' || elm == elms.length()) {
72  Put(format[offset]);
73  offset++;
74  continue;
75  }
76  // Read this formatting directive into a temporary buffer
77  EmbeddedVector<char, 24> temp;
78  int format_length = 0;
79  // Skip over the whole control character sequence until the
80  // format element type
81  temp[format_length++] = format[offset++];
82  while (offset < format.length() && IsControlChar(format[offset]))
83  temp[format_length++] = format[offset++];
84  if (offset >= format.length())
85  return;
86  char type = format[offset];
87  temp[format_length++] = type;
88  temp[format_length] = '\0';
89  offset++;
90  FmtElm current = elms[elm++];
91  switch (type) {
92  case 's': {
93  DCHECK_EQ(FmtElm::C_STR, current.type_);
94  const char* value = current.data_.u_c_str_;
95  Add(value);
96  break;
97  }
98  case 'w': {
99  DCHECK_EQ(FmtElm::LC_STR, current.type_);
100  Vector<const uc16> value = *current.data_.u_lc_str_;
101  for (int i = 0; i < value.length(); i++)
102  Put(static_cast<char>(value[i]));
103  break;
104  }
105  case 'o': {
106  DCHECK_EQ(FmtElm::OBJ, current.type_);
107  Object* obj = current.data_.u_obj_;
108  PrintObject(obj);
109  break;
110  }
111  case 'k': {
112  DCHECK_EQ(FmtElm::INT, current.type_);
113  int value = current.data_.u_int_;
114  if (0x20 <= value && value <= 0x7F) {
115  Put(value);
116  } else if (value <= 0xff) {
117  Add("\\x%02x", value);
118  } else {
119  Add("\\u%04x", value);
120  }
121  break;
122  }
123  case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
124  int value = current.data_.u_int_;
125  EmbeddedVector<char, 24> formatted;
126  int length = SNPrintF(formatted, temp.start(), value);
127  Add(Vector<const char>(formatted.start(), length));
128  break;
129  }
130  case 'f': case 'g': case 'G': case 'e': case 'E': {
131  double value = current.data_.u_double_;
132  int inf = std::isinf(value);
133  if (inf == -1) {
134  Add("-inf");
135  } else if (inf == 1) {
136  Add("inf");
137  } else if (std::isnan(value)) {
138  Add("nan");
139  } else {
140  EmbeddedVector<char, 28> formatted;
141  SNPrintF(formatted, temp.start(), value);
142  Add(formatted.start());
143  }
144  break;
145  }
146  case 'p': {
147  void* value = current.data_.u_pointer_;
148  EmbeddedVector<char, 20> formatted;
149  SNPrintF(formatted, temp.start(), value);
150  Add(formatted.start());
151  break;
152  }
153  default:
154  UNREACHABLE();
155  break;
156  }
157  }
158 
159  // Verify that the buffer is 0-terminated
160  DCHECK(buffer_[length_] == '\0');
161 }
162 
163 
164 void StringStream::PrintObject(Object* o) {
165  o->ShortPrint(this);
166  if (o->IsString()) {
167  if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
168  return;
169  }
170  } else if (o->IsNumber() || o->IsOddball()) {
171  return;
172  }
173  if (o->IsHeapObject()) {
174  HeapObject* ho = HeapObject::cast(o);
175  DebugObjectCache* debug_object_cache = ho->GetIsolate()->
176  string_stream_debug_object_cache();
177  for (int i = 0; i < debug_object_cache->length(); i++) {
178  if ((*debug_object_cache)[i] == o) {
179  Add("#%d#", i);
180  return;
181  }
182  }
183  if (debug_object_cache->length() < kMentionedObjectCacheMaxSize) {
184  Add("#%d#", debug_object_cache->length());
185  debug_object_cache->Add(HeapObject::cast(o));
186  } else {
187  Add("@%p", o);
188  }
189  }
190 }
191 
192 
193 void StringStream::Add(const char* format) {
194  Add(CStrVector(format));
195 }
196 
197 
198 void StringStream::Add(Vector<const char> format) {
199  Add(format, Vector<FmtElm>::empty());
200 }
201 
202 
203 void StringStream::Add(const char* format, FmtElm arg0) {
204  const char argc = 1;
205  FmtElm argv[argc] = { arg0 };
206  Add(CStrVector(format), Vector<FmtElm>(argv, argc));
207 }
208 
209 
210 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1) {
211  const char argc = 2;
212  FmtElm argv[argc] = { arg0, arg1 };
213  Add(CStrVector(format), Vector<FmtElm>(argv, argc));
214 }
215 
216 
217 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
218  FmtElm arg2) {
219  const char argc = 3;
220  FmtElm argv[argc] = { arg0, arg1, arg2 };
221  Add(CStrVector(format), Vector<FmtElm>(argv, argc));
222 }
223 
224 
225 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
226  FmtElm arg2, FmtElm arg3) {
227  const char argc = 4;
228  FmtElm argv[argc] = { arg0, arg1, arg2, arg3 };
229  Add(CStrVector(format), Vector<FmtElm>(argv, argc));
230 }
231 
232 
233 void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
234  FmtElm arg2, FmtElm arg3, FmtElm arg4) {
235  const char argc = 5;
236  FmtElm argv[argc] = { arg0, arg1, arg2, arg3, arg4 };
237  Add(CStrVector(format), Vector<FmtElm>(argv, argc));
238 }
239 
240 
241 SmartArrayPointer<const char> StringStream::ToCString() const {
242  char* str = NewArray<char>(length_ + 1);
243  MemCopy(str, buffer_, length_);
244  str[length_] = '\0';
245  return SmartArrayPointer<const char>(str);
246 }
247 
248 
249 void StringStream::Log(Isolate* isolate) {
250  LOG(isolate, StringEvent("StackDump", buffer_));
251 }
252 
253 
254 void StringStream::OutputToFile(FILE* out) {
255  // Dump the output to stdout, but make sure to break it up into
256  // manageable chunks to avoid losing parts of the output in the OS
257  // printing code. This is a problem on Windows in particular; see
258  // the VPrint() function implementations in platform-win32.cc.
259  unsigned position = 0;
260  for (unsigned next; (next = position + 2048) < length_; position = next) {
261  char save = buffer_[next];
262  buffer_[next] = '\0';
263  internal::PrintF(out, "%s", &buffer_[position]);
264  buffer_[next] = save;
265  }
266  internal::PrintF(out, "%s", &buffer_[position]);
267 }
268 
269 
270 Handle<String> StringStream::ToString(Isolate* isolate) {
271  return isolate->factory()->NewStringFromUtf8(
272  Vector<const char>(buffer_, length_)).ToHandleChecked();
273 }
274 
275 
276 void StringStream::ClearMentionedObjectCache(Isolate* isolate) {
277  isolate->set_string_stream_current_security_token(NULL);
278  if (isolate->string_stream_debug_object_cache() == NULL) {
279  isolate->set_string_stream_debug_object_cache(new DebugObjectCache(0));
280  }
281  isolate->string_stream_debug_object_cache()->Clear();
282 }
283 
284 
285 #ifdef DEBUG
286 bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
287  return isolate->string_stream_debug_object_cache()->length() == 0;
288 }
289 #endif
290 
291 
292 bool StringStream::Put(String* str) {
293  return Put(str, 0, str->length());
294 }
295 
296 
297 bool StringStream::Put(String* str, int start, int end) {
298  ConsStringIteratorOp op;
299  StringCharacterStream stream(str, &op, start);
300  for (int i = start; i < end && stream.HasMore(); i++) {
301  uint16_t c = stream.GetNext();
302  if (c >= 127 || c < 32) {
303  c = '?';
304  }
305  if (!Put(static_cast<char>(c))) {
306  return false; // Output was truncated.
307  }
308  }
309  return true;
310 }
311 
312 
313 void StringStream::PrintName(Object* name) {
314  if (name->IsString()) {
315  String* str = String::cast(name);
316  if (str->length() > 0) {
317  Put(str);
318  } else {
319  Add("/* anonymous */");
320  }
321  } else {
322  Add("%o", name);
323  }
324 }
325 
326 
327 void StringStream::PrintUsingMap(JSObject* js_object) {
328  Map* map = js_object->map();
329  if (!js_object->GetHeap()->Contains(map) ||
330  !map->IsHeapObject() ||
331  !map->IsMap()) {
332  Add("<Invalid map>\n");
333  return;
334  }
335  int real_size = map->NumberOfOwnDescriptors();
336  DescriptorArray* descs = map->instance_descriptors();
337  for (int i = 0; i < real_size; i++) {
338  PropertyDetails details = descs->GetDetails(i);
339  if (details.type() == FIELD) {
340  Object* key = descs->GetKey(i);
341  if (key->IsString() || key->IsNumber()) {
342  int len = 3;
343  if (key->IsString()) {
344  len = String::cast(key)->length();
345  }
346  for (; len < 18; len++)
347  Put(' ');
348  if (key->IsString()) {
349  Put(String::cast(key));
350  } else {
351  key->ShortPrint();
352  }
353  Add(": ");
354  FieldIndex index = FieldIndex::ForDescriptor(map, i);
355  Object* value = js_object->RawFastPropertyAt(index);
356  Add("%o\n", value);
357  }
358  }
359  }
360 }
361 
362 
363 void StringStream::PrintFixedArray(FixedArray* array, unsigned int limit) {
364  Heap* heap = array->GetHeap();
365  for (unsigned int i = 0; i < 10 && i < limit; i++) {
366  Object* element = array->get(i);
367  if (element != heap->the_hole_value()) {
368  for (int len = 1; len < 18; len++)
369  Put(' ');
370  Add("%d: %o\n", i, array->get(i));
371  }
372  }
373  if (limit >= 10) {
374  Add(" ...\n");
375  }
376 }
377 
378 
379 void StringStream::PrintByteArray(ByteArray* byte_array) {
380  unsigned int limit = byte_array->length();
381  for (unsigned int i = 0; i < 10 && i < limit; i++) {
382  byte b = byte_array->get(i);
383  Add(" %d: %3d 0x%02x", i, b, b);
384  if (b >= ' ' && b <= '~') {
385  Add(" '%c'", b);
386  } else if (b == '\n') {
387  Add(" '\n'");
388  } else if (b == '\r') {
389  Add(" '\r'");
390  } else if (b >= 1 && b <= 26) {
391  Add(" ^%c", b + 'A' - 1);
392  }
393  Add("\n");
394  }
395  if (limit >= 10) {
396  Add(" ...\n");
397  }
398 }
399 
400 
401 void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
402  DebugObjectCache* debug_object_cache =
403  isolate->string_stream_debug_object_cache();
404  Add("==== Key ============================================\n\n");
405  for (int i = 0; i < debug_object_cache->length(); i++) {
406  HeapObject* printee = (*debug_object_cache)[i];
407  Add(" #%d# %p: ", i, printee);
408  printee->ShortPrint(this);
409  Add("\n");
410  if (printee->IsJSObject()) {
411  if (printee->IsJSValue()) {
412  Add(" value(): %o\n", JSValue::cast(printee)->value());
413  }
414  PrintUsingMap(JSObject::cast(printee));
415  if (printee->IsJSArray()) {
416  JSArray* array = JSArray::cast(printee);
417  if (array->HasFastObjectElements()) {
418  unsigned int limit = FixedArray::cast(array->elements())->length();
419  unsigned int length =
420  static_cast<uint32_t>(JSArray::cast(array)->length()->Number());
421  if (length < limit) limit = length;
422  PrintFixedArray(FixedArray::cast(array->elements()), limit);
423  }
424  }
425  } else if (printee->IsByteArray()) {
426  PrintByteArray(ByteArray::cast(printee));
427  } else if (printee->IsFixedArray()) {
428  unsigned int limit = FixedArray::cast(printee)->length();
429  PrintFixedArray(FixedArray::cast(printee), limit);
430  }
431  }
432 }
433 
434 
435 void StringStream::PrintSecurityTokenIfChanged(Object* f) {
436  if (!f->IsHeapObject()) return;
437  HeapObject* obj = HeapObject::cast(f);
438  Isolate* isolate = obj->GetIsolate();
439  Heap* heap = isolate->heap();
440  if (!heap->Contains(obj)) return;
441  Map* map = obj->map();
442  if (!map->IsHeapObject() ||
443  !heap->Contains(map) ||
444  !map->IsMap() ||
445  !f->IsJSFunction()) {
446  return;
447  }
448 
449  JSFunction* fun = JSFunction::cast(f);
450  Object* perhaps_context = fun->context();
451  if (perhaps_context->IsHeapObject() &&
452  heap->Contains(HeapObject::cast(perhaps_context)) &&
453  perhaps_context->IsContext()) {
454  Context* context = fun->context();
455  if (!heap->Contains(context)) {
456  Add("(Function context is outside heap)\n");
457  return;
458  }
459  Object* token = context->native_context()->security_token();
460  if (token != isolate->string_stream_current_security_token()) {
461  Add("Security context: %o\n", token);
462  isolate->set_string_stream_current_security_token(token);
463  }
464  } else {
465  Add("(Function context is corrupt)\n");
466  }
467 }
468 
469 
470 void StringStream::PrintFunction(Object* f, Object* receiver, Code** code) {
471  if (!f->IsHeapObject()) {
472  Add("/* warning: 'function' was not a heap object */ ");
473  return;
474  }
475  Heap* heap = HeapObject::cast(f)->GetHeap();
476  if (!heap->Contains(HeapObject::cast(f))) {
477  Add("/* warning: 'function' was not on the heap */ ");
478  return;
479  }
480  if (!heap->Contains(HeapObject::cast(f)->map())) {
481  Add("/* warning: function's map was not on the heap */ ");
482  return;
483  }
484  if (!HeapObject::cast(f)->map()->IsMap()) {
485  Add("/* warning: function's map was not a valid map */ ");
486  return;
487  }
488  if (f->IsJSFunction()) {
489  JSFunction* fun = JSFunction::cast(f);
490  // Common case: on-stack function present and resolved.
491  PrintPrototype(fun, receiver);
492  *code = fun->code();
493  } else if (f->IsInternalizedString()) {
494  // Unresolved and megamorphic calls: Instead of the function
495  // we have the function name on the stack.
496  PrintName(f);
497  Add("/* unresolved */ ");
498  } else {
499  // Unless this is the frame of a built-in function, we should always have
500  // the callee function or name on the stack. If we don't, we have a
501  // problem or a change of the stack frame layout.
502  Add("%o", f);
503  Add("/* warning: no JSFunction object or function name found */ ");
504  }
505 }
506 
507 
508 void StringStream::PrintPrototype(JSFunction* fun, Object* receiver) {
509  Object* name = fun->shared()->name();
510  bool print_name = false;
511  Isolate* isolate = fun->GetIsolate();
512  for (PrototypeIterator iter(isolate, receiver,
514  !iter.IsAtEnd(); iter.Advance()) {
515  if (iter.GetCurrent()->IsJSObject()) {
516  Object* key = JSObject::cast(iter.GetCurrent())->SlowReverseLookup(fun);
517  if (key != isolate->heap()->undefined_value()) {
518  if (!name->IsString() ||
519  !key->IsString() ||
520  !String::cast(name)->Equals(String::cast(key))) {
521  print_name = true;
522  }
523  if (name->IsString() && String::cast(name)->length() == 0) {
524  print_name = false;
525  }
526  name = key;
527  }
528  } else {
529  print_name = true;
530  }
531  }
532  PrintName(name);
533  // Also known as - if the name in the function doesn't match the name under
534  // which it was looked up.
535  if (print_name) {
536  Add("(aka ");
537  PrintName(fun->shared()->name());
538  Put(')');
539  }
540 }
541 
542 
543 char* HeapStringAllocator::grow(unsigned* bytes) {
544  unsigned new_bytes = *bytes * 2;
545  // Check for overflow.
546  if (new_bytes <= *bytes) {
547  return space_;
548  }
549  char* new_space = NewArray<char>(new_bytes);
550  if (new_space == NULL) {
551  return space_;
552  }
553  MemCopy(new_space, space_, *bytes);
554  *bytes = new_bytes;
555  DeleteArray(space_);
556  space_ = new_space;
557  return new_space;
558 }
559 
560 
561 } } // namespace v8::internal
static const int kMaxShortPrintLength
Definition: objects.h:8827
static Vector< T > empty()
Definition: vector.h:95
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf map
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes enable context specialization in TurboFan execution budget before interrupt is triggered max percentage of megamorphic generic ICs to allow optimization enable use of SAHF instruction if enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable use of MLS instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long enable alignment of csp to bytes on platforms which prefer the register to always be expose gc extension under the specified name show built in functions in stack traces use random jit cookie to mask large constants minimum length for automatic enable preparsing CPU profiler sampling interval in microseconds trace out of bounds accesses to external arrays default size of stack region v8 is allowed to maximum length of function source code printed in a stack trace min size of a semi the new space consists of two semi spaces print one trace line following each garbage collection do not print trace line after scavenger collection print cumulative GC statistics in name
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes enable context specialization in TurboFan execution budget before interrupt is triggered max percentage of megamorphic generic ICs to allow optimization enable use of SAHF instruction if enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable use of MLS instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long enable alignment of csp to bytes on platforms which prefer the register to always be NULL
#define LOG(isolate, Call)
Definition: log.h:69
#define UNREACHABLE()
Definition: logging.h:30
#define DCHECK(condition)
Definition: logging.h:205
#define DCHECK_EQ(v1, v2)
Definition: logging.h:206
unsigned short uint16_t
Definition: unicode.cc:23
Vector< const char > CStrVector(const char *data)
Definition: vector.h:158
void DeleteArray(T *array)
Definition: allocation.h:68
static const int kMentionedObjectCacheMaxSize
int SNPrintF(Vector< char > str, const char *format,...)
Definition: utils.cc:105
kSerializedDataOffset Object
Definition: objects-inl.h:5322
static bool IsControlChar(char c)
void PrintF(const char *format,...)
Definition: utils.cc:80
List< HeapObject * > DebugObjectCache
Definition: isolate.h:354
void MemCopy(void *dest, const void *src, size_t size)
Definition: utils.h:350
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20
int isinf(double x)