OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 13 matching lines...) Expand all Loading... |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 #ifdef ENABLE_LOGGING_AND_PROFILING | 28 #ifdef ENABLE_LOGGING_AND_PROFILING |
29 | 29 |
30 #include "v8.h" | 30 #include "v8.h" |
31 #include "global-handles.h" | 31 #include "global-handles.h" |
32 #include "scopeinfo.h" | 32 #include "scopeinfo.h" |
33 #include "top.h" | 33 #include "top.h" |
| 34 #include "unicode.h" |
34 #include "zone-inl.h" | 35 #include "zone-inl.h" |
35 | 36 |
36 #include "profile-generator-inl.h" | 37 #include "profile-generator-inl.h" |
37 | 38 |
38 namespace v8 { | 39 namespace v8 { |
39 namespace internal { | 40 namespace internal { |
40 | 41 |
41 | 42 |
42 TokenEnumerator::TokenEnumerator() | 43 TokenEnumerator::TokenEnumerator() |
43 : token_locations_(4), | 44 : token_locations_(4), |
(...skipping 2081 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2125 } | 2126 } |
2126 int add_child_index = 0, added_entry_index = 1; | 2127 int add_child_index = 0, added_entry_index = 1; |
2127 for (int i = 0; i < added_entries.length(); ++i) { | 2128 for (int i = 0; i < added_entries.length(); ++i) { |
2128 HeapEntry* entry = added_entries[i]; | 2129 HeapEntry* entry = added_entries[i]; |
2129 if (entry->painted_reachable()) | 2130 if (entry->painted_reachable()) |
2130 diff->AddAddedEntry(add_child_index++, added_entry_index++, entry); | 2131 diff->AddAddedEntry(add_child_index++, added_entry_index++, entry); |
2131 } | 2132 } |
2132 return diff; | 2133 return diff; |
2133 } | 2134 } |
2134 | 2135 |
| 2136 |
| 2137 class OutputStreamWriter { |
| 2138 public: |
| 2139 explicit OutputStreamWriter(v8::OutputStream* stream) |
| 2140 : stream_(stream), |
| 2141 chunk_size_(stream->GetChunkSize()), |
| 2142 chunk_(chunk_size_), |
| 2143 chunk_pos_(0), |
| 2144 aborted_(false) { |
| 2145 ASSERT(chunk_size_ > 0); |
| 2146 } |
| 2147 bool aborted() { return aborted_; } |
| 2148 void AddCharacter(char c) { |
| 2149 ASSERT(c != '\0'); |
| 2150 ASSERT(chunk_pos_ < chunk_size_); |
| 2151 chunk_[chunk_pos_++] = c; |
| 2152 MaybeWriteChunk(); |
| 2153 } |
| 2154 void AddString(const char* s) { |
| 2155 AddSubstring(s, StrLength(s)); |
| 2156 } |
| 2157 void AddSubstring(const char* s, int n) { |
| 2158 if (n <= 0) return; |
| 2159 ASSERT(static_cast<size_t>(n) <= strlen(s)); |
| 2160 const char* s_end = s + n; |
| 2161 while (s < s_end) { |
| 2162 int s_chunk_size = Min( |
| 2163 chunk_size_ - chunk_pos_, static_cast<int>(s_end - s)); |
| 2164 ASSERT(s_chunk_size > 0); |
| 2165 memcpy(chunk_.start() + chunk_pos_, s, s_chunk_size); |
| 2166 s += s_chunk_size; |
| 2167 chunk_pos_ += s_chunk_size; |
| 2168 MaybeWriteChunk(); |
| 2169 } |
| 2170 } |
| 2171 void AddNumber(int n) { AddNumberImpl<int>(n, "%d"); } |
| 2172 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); } |
| 2173 void AddNumber(uint64_t n) { AddNumberImpl<uint64_t>(n, "%llu"); } |
| 2174 void Finalize() { |
| 2175 if (aborted_) return; |
| 2176 ASSERT(chunk_pos_ < chunk_size_); |
| 2177 if (chunk_pos_ != 0) { |
| 2178 WriteChunk(); |
| 2179 } |
| 2180 stream_->EndOfStream(); |
| 2181 } |
| 2182 |
| 2183 private: |
| 2184 template<typename T> |
| 2185 void AddNumberImpl(T n, const char* format) { |
| 2186 ScopedVector<char> buffer(32); |
| 2187 int result = OS::SNPrintF(buffer, format, n); |
| 2188 USE(result); |
| 2189 ASSERT(result != -1); |
| 2190 AddString(buffer.start()); |
| 2191 } |
| 2192 void MaybeWriteChunk() { |
| 2193 ASSERT(chunk_pos_ <= chunk_size_); |
| 2194 if (chunk_pos_ == chunk_size_) { |
| 2195 WriteChunk(); |
| 2196 chunk_pos_ = 0; |
| 2197 } |
| 2198 } |
| 2199 void WriteChunk() { |
| 2200 if (aborted_) return; |
| 2201 if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) == |
| 2202 v8::OutputStream::kAbort) aborted_ = true; |
| 2203 } |
| 2204 |
| 2205 v8::OutputStream* stream_; |
| 2206 int chunk_size_; |
| 2207 ScopedVector<char> chunk_; |
| 2208 int chunk_pos_; |
| 2209 bool aborted_; |
| 2210 }; |
| 2211 |
| 2212 void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) { |
| 2213 ASSERT(writer_ == NULL); |
| 2214 writer_ = new OutputStreamWriter(stream); |
| 2215 |
| 2216 // Since nodes graph is cyclic, we need the first pass to enumerate |
| 2217 // them. Strings can be serialized in one pass. |
| 2218 EnumerateNodes(); |
| 2219 SerializeImpl(); |
| 2220 |
| 2221 delete writer_; |
| 2222 writer_ = NULL; |
| 2223 } |
| 2224 |
| 2225 |
| 2226 void HeapSnapshotJSONSerializer::SerializeImpl() { |
| 2227 writer_->AddCharacter('{'); |
| 2228 writer_->AddString("\"snapshot\":{"); |
| 2229 SerializeSnapshot(); |
| 2230 if (writer_->aborted()) return; |
| 2231 writer_->AddString("},\n"); |
| 2232 writer_->AddString("\"nodes\":["); |
| 2233 SerializeNodes(); |
| 2234 if (writer_->aborted()) return; |
| 2235 writer_->AddString("],\n"); |
| 2236 writer_->AddString("\"strings\":["); |
| 2237 SerializeStrings(); |
| 2238 if (writer_->aborted()) return; |
| 2239 writer_->AddCharacter(']'); |
| 2240 writer_->AddCharacter('}'); |
| 2241 writer_->Finalize(); |
| 2242 } |
| 2243 |
| 2244 |
| 2245 class HeapSnapshotJSONSerializerEnumerator { |
| 2246 public: |
| 2247 explicit HeapSnapshotJSONSerializerEnumerator(HeapSnapshotJSONSerializer* s) |
| 2248 : s_(s) { |
| 2249 } |
| 2250 void Apply(HeapEntry** entry) { |
| 2251 s_->GetNodeId(*entry); |
| 2252 } |
| 2253 private: |
| 2254 HeapSnapshotJSONSerializer* s_; |
| 2255 }; |
| 2256 |
| 2257 void HeapSnapshotJSONSerializer::EnumerateNodes() { |
| 2258 GetNodeId(snapshot_->root()); // Make sure root gets the first id. |
| 2259 HeapSnapshotJSONSerializerEnumerator iter(this); |
| 2260 snapshot_->IterateEntries(&iter); |
| 2261 } |
| 2262 |
| 2263 |
| 2264 int HeapSnapshotJSONSerializer::GetNodeId(HeapEntry* entry) { |
| 2265 HashMap::Entry* cache_entry = nodes_.Lookup(entry, ObjectHash(entry), true); |
| 2266 if (cache_entry->value == NULL) { |
| 2267 cache_entry->value = reinterpret_cast<void*>(next_node_id_++); |
| 2268 } |
| 2269 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value)); |
| 2270 } |
| 2271 |
| 2272 |
| 2273 int HeapSnapshotJSONSerializer::GetStringId(const char* s) { |
| 2274 HashMap::Entry* cache_entry = strings_.Lookup( |
| 2275 const_cast<char*>(s), ObjectHash(s), true); |
| 2276 if (cache_entry->value == NULL) { |
| 2277 cache_entry->value = reinterpret_cast<void*>(next_string_id_++); |
| 2278 } |
| 2279 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value)); |
| 2280 } |
| 2281 |
| 2282 |
| 2283 void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) { |
| 2284 writer_->AddCharacter(','); |
| 2285 writer_->AddNumber(edge->type()); |
| 2286 writer_->AddCharacter(','); |
| 2287 if (edge->type() == HeapGraphEdge::kElement) { |
| 2288 writer_->AddNumber(edge->index()); |
| 2289 } else { |
| 2290 writer_->AddNumber(GetStringId(edge->name())); |
| 2291 } |
| 2292 writer_->AddCharacter(','); |
| 2293 writer_->AddNumber(GetNodeId(edge->to())); |
| 2294 } |
| 2295 |
| 2296 |
| 2297 void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { |
| 2298 writer_->AddCharacter('\n'); |
| 2299 writer_->AddCharacter(','); |
| 2300 writer_->AddNumber(entry->type()); |
| 2301 writer_->AddCharacter(','); |
| 2302 writer_->AddNumber(GetStringId(entry->name())); |
| 2303 writer_->AddCharacter(','); |
| 2304 writer_->AddNumber(entry->id()); |
| 2305 writer_->AddCharacter(','); |
| 2306 writer_->AddNumber(entry->self_size()); |
| 2307 Vector<HeapGraphEdge> children = entry->children(); |
| 2308 writer_->AddCharacter(','); |
| 2309 writer_->AddNumber(children.length()); |
| 2310 for (int i = 0; i < children.length(); ++i) { |
| 2311 SerializeEdge(&children[i]); |
| 2312 if (writer_->aborted()) return; |
| 2313 } |
| 2314 } |
| 2315 |
| 2316 |
| 2317 void HeapSnapshotJSONSerializer::SerializeNodes() { |
| 2318 // The first (zero) item of nodes array is a JSON-ified object |
| 2319 // describing node serialization layout. |
| 2320 // We use a set of macros to improve readability. |
| 2321 #define JSON_A(s) "["s"]" |
| 2322 #define JSON_O(s) "{"s"}" |
| 2323 #define JSON_S(s) "\\\""s"\\\"" |
| 2324 writer_->AddString("\"" JSON_O( |
| 2325 JSON_S("fields") ":" JSON_A( |
| 2326 JSON_S("type") |
| 2327 "," JSON_S("name") |
| 2328 "," JSON_S("id") |
| 2329 "," JSON_S("self_size") |
| 2330 "," JSON_S("children_count") |
| 2331 "," JSON_S("children")) |
| 2332 "," JSON_S("types") ":" JSON_A( |
| 2333 JSON_A( |
| 2334 JSON_S("internal") |
| 2335 "," JSON_S("array") |
| 2336 "," JSON_S("string") |
| 2337 "," JSON_S("object") |
| 2338 "," JSON_S("code") |
| 2339 "," JSON_S("closure")) |
| 2340 "," JSON_S("string") |
| 2341 "," JSON_S("number") |
| 2342 "," JSON_S("number") |
| 2343 "," JSON_S("number") |
| 2344 "," JSON_O( |
| 2345 JSON_S("fields") ":" JSON_A( |
| 2346 JSON_S("type") |
| 2347 "," JSON_S("name_or_index") |
| 2348 "," JSON_S("to_node")) |
| 2349 "," JSON_S("types") ":" JSON_A( |
| 2350 JSON_A( |
| 2351 JSON_S("context") |
| 2352 "," JSON_S("element") |
| 2353 "," JSON_S("property") |
| 2354 "," JSON_S("internal")) |
| 2355 "," JSON_S("string_or_number") |
| 2356 "," JSON_S("node"))))) "\""); |
| 2357 #undef JSON_S |
| 2358 #undef JSON_O |
| 2359 #undef JSON_A |
| 2360 |
| 2361 const int node_fields_count = 5; // type,name,id,self_size,children_count. |
| 2362 const int edge_fields_count = 3; // type,name|index,to_node. |
| 2363 List<HashMap::Entry*> sorted_nodes; |
| 2364 SortHashMap(&nodes_, &sorted_nodes); |
| 2365 // Rewrite node ids, so they refer to actual array positions. |
| 2366 if (sorted_nodes.length() > 1) { |
| 2367 // Nodes start from array index 1. |
| 2368 int prev_value = 1; |
| 2369 sorted_nodes[0]->value = reinterpret_cast<void*>(prev_value); |
| 2370 for (int i = 1; i < sorted_nodes.length(); ++i) { |
| 2371 HeapEntry* prev_heap_entry = |
| 2372 reinterpret_cast<HeapEntry*>(sorted_nodes[i-1]->key); |
| 2373 prev_value += node_fields_count + |
| 2374 prev_heap_entry->children().length() * edge_fields_count; |
| 2375 sorted_nodes[i]->value = reinterpret_cast<void*>(prev_value); |
| 2376 } |
| 2377 } |
| 2378 for (int i = 0; i < sorted_nodes.length(); ++i) { |
| 2379 SerializeNode(reinterpret_cast<HeapEntry*>(sorted_nodes[i]->key)); |
| 2380 if (writer_->aborted()) return; |
| 2381 } |
| 2382 } |
| 2383 |
| 2384 |
| 2385 void HeapSnapshotJSONSerializer::SerializeSnapshot() { |
| 2386 writer_->AddString("\"title\":\""); |
| 2387 writer_->AddString(snapshot_->title()); |
| 2388 writer_->AddString("\""); |
| 2389 writer_->AddString(",\"uid\":"); |
| 2390 writer_->AddNumber(snapshot_->uid()); |
| 2391 } |
| 2392 |
| 2393 |
| 2394 static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) { |
| 2395 static const char hex_chars[] = "0123456789ABCDEF"; |
| 2396 w->AddString("\\u"); |
| 2397 w->AddCharacter(hex_chars[(u >> 12) & 0xf]); |
| 2398 w->AddCharacter(hex_chars[(u >> 8) & 0xf]); |
| 2399 w->AddCharacter(hex_chars[(u >> 4) & 0xf]); |
| 2400 w->AddCharacter(hex_chars[u & 0xf]); |
| 2401 } |
| 2402 |
| 2403 void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) { |
| 2404 writer_->AddCharacter('\n'); |
| 2405 writer_->AddCharacter('\"'); |
| 2406 for ( ; *s != '\0'; ++s) { |
| 2407 switch (*s) { |
| 2408 case '\b': |
| 2409 writer_->AddString("\\b"); |
| 2410 continue; |
| 2411 case '\f': |
| 2412 writer_->AddString("\\f"); |
| 2413 continue; |
| 2414 case '\n': |
| 2415 writer_->AddString("\\n"); |
| 2416 continue; |
| 2417 case '\r': |
| 2418 writer_->AddString("\\r"); |
| 2419 continue; |
| 2420 case '\t': |
| 2421 writer_->AddString("\\t"); |
| 2422 continue; |
| 2423 case '\"': |
| 2424 case '\\': |
| 2425 writer_->AddCharacter('\\'); |
| 2426 writer_->AddCharacter(*s); |
| 2427 continue; |
| 2428 default: |
| 2429 if (*s > 31 && *s < 128) { |
| 2430 writer_->AddCharacter(*s); |
| 2431 } else if (*s <= 31) { |
| 2432 // Special character with no dedicated literal. |
| 2433 WriteUChar(writer_, *s); |
| 2434 } else { |
| 2435 // Convert UTF-8 into \u UTF-16 literal. |
| 2436 unsigned length = 1, cursor = 0; |
| 2437 for ( ; length <= 4 && *(s + length) != '\0'; ++length) { } |
| 2438 unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor); |
| 2439 if (c != unibrow::Utf8::kBadChar) { |
| 2440 WriteUChar(writer_, c); |
| 2441 ASSERT(cursor != 0); |
| 2442 s += cursor - 1; |
| 2443 } else { |
| 2444 writer_->AddCharacter('?'); |
| 2445 } |
| 2446 } |
| 2447 } |
| 2448 } |
| 2449 writer_->AddCharacter('\"'); |
| 2450 } |
| 2451 |
| 2452 |
| 2453 void HeapSnapshotJSONSerializer::SerializeStrings() { |
| 2454 List<HashMap::Entry*> sorted_strings; |
| 2455 SortHashMap(&strings_, &sorted_strings); |
| 2456 writer_->AddString("\"<dummy>\""); |
| 2457 for (int i = 0; i < sorted_strings.length(); ++i) { |
| 2458 writer_->AddCharacter(','); |
| 2459 SerializeString( |
| 2460 reinterpret_cast<const unsigned char*>(sorted_strings[i]->key)); |
| 2461 if (writer_->aborted()) return; |
| 2462 } |
| 2463 } |
| 2464 |
| 2465 |
| 2466 template<typename T> |
| 2467 inline static int SortUsingEntryValue(const T* x, const T* y) { |
| 2468 uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value); |
| 2469 uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value); |
| 2470 if (x_uint > y_uint) { |
| 2471 return 1; |
| 2472 } else if (x_uint == y_uint) { |
| 2473 return 0; |
| 2474 } else { |
| 2475 return -1; |
| 2476 } |
| 2477 } |
| 2478 |
| 2479 |
| 2480 void HeapSnapshotJSONSerializer::SortHashMap( |
| 2481 HashMap* map, List<HashMap::Entry*>* sorted_entries) { |
| 2482 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) |
| 2483 sorted_entries->Add(p); |
| 2484 sorted_entries->Sort(SortUsingEntryValue); |
| 2485 } |
| 2486 |
2135 } } // namespace v8::internal | 2487 } } // namespace v8::internal |
2136 | 2488 |
2137 #endif // ENABLE_LOGGING_AND_PROFILING | 2489 #endif // ENABLE_LOGGING_AND_PROFILING |
OLD | NEW |