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