├── ChangeLog.txt
├── License
├── README.md
├── bin
├── stdio.js
└── tickprocessor-driver.js
├── lib
├── codemap.js
├── composer.js
├── consarray.js
├── csvparser.js
├── logreader.js
├── profile.js
├── profile_view.js
├── splaytree.js
└── tickprocessor.js
├── package.json
└── test
├── generate.js
└── v8-3.22.24.19.log
/ChangeLog.txt:
--------------------------------------------------------------------------------
1 | 0.1.0
2 | - LogProcessor now tries to giess v8.log format and it should handle
3 | profiles from both node 0.10.x and 0.11.x
4 | - added timeline gnuplot script generation
5 |
6 | 0.0.6
7 | - --ignore-unknown flag
8 |
9 | 0.0.2
10 | - it's now possible to use large files (previous version failed on 5Gb
11 | example). readableStream is used now instead of 'read all & split'
12 | - fixe for argument parser initialisation. log & snapshot file name
13 | and other options work now
14 | - separate source files `require`d instead of joining all sources into
15 | single bundle
16 | - scripts updated from V8 3.14.5
17 |
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Andrey Sidorov (sidorares@yandex.ru) and contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | v8.log processor based on scripts in v8 distribution.
2 | Allows you to profile V8-based programs without installing V8 from source.
3 |
4 | Note that starting from version v5.2.0 node distribution includes v8.log processor - see [#node/4021](https://github.com/nodejs/node/pull/4021). To use it, type `node --prof-process`
5 |
6 | ### Install
7 | $ npm install -g tick
8 |
9 | ### Usage
10 |
11 | See [V8 profiler page](https://github.com/v8/v8/wiki/V8%20Profiler) for basic introduction to v8.log
12 |
13 | $ node --prof yourprogram
14 | $ node-tick-processor
15 |
16 | ### See also
17 |
18 | `takeSnapshot` & `startProfiling` & `stopProfiling` V8 profiler API exposed to node.js: [v8-profiler](https://github.com/dannycoates/v8-profiler)
19 |
20 |
21 | ### V8 Performance optimisation resources
22 |
23 | - [v8-perf](https://thlorenz.github.io/v8-perf/)
24 | - [Trevor Norris notes](https://gist.github.com/trevnorris/6fea5ab2632dff8b5b25#file-perf-training-syllabus-md)
25 | - [V8 optimisations killers](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers)
26 | - [I-want-to-optimize-my-JS-application-on-V8 checklist](http://mrale.ph/blog/2011/12/18/v8-optimization-checklist.html) ( + [another listlist](http://mrale.ph/v8/resources.html) ) by @mraleph
27 | - https://github.com/thlorenz/iojs-inspect-entire-stack
28 | - [v8 bailout reasons explained](https://github.com/vhf/v8-bailout-reasons)
29 |
--------------------------------------------------------------------------------
/bin/stdio.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var tickProcessorModule = require('../lib/tickprocessor');
4 | var composer = require('../lib/composer');
5 | var ArgumentsProcessor = tickProcessorModule.ArgumentsProcessor;
6 | var TickProcessor = tickProcessorModule.TickProcessor;
7 | var SnapshotLogProcessor = tickProcessorModule.SnapshotLogProcessor;
8 | var PlotScriptComposer = composer.PlotScriptComposer;
9 | var processFileLines = tickProcessorModule.processFileLines;
10 |
11 | // Copyright 2013 the V8 project authors. All rights reserved.
12 | // Redistribution and use in source and binary forms, with or without
13 | // modification, are permitted provided that the following conditions are
14 | // met:
15 | //
16 | // * Redistributions of source code must retain the above copyright
17 | // notice, this list of conditions and the following disclaimer.
18 | // * Redistributions in binary form must reproduce the above
19 | // copyright notice, this list of conditions and the following
20 | // disclaimer in the documentation and/or other materials provided
21 | // with the distribution.
22 | // * Neither the name of Google Inc. nor the names of its
23 | // contributors may be used to endorse or promote products derived
24 | // from this software without specific prior written permission.
25 | //
26 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 |
38 | var args = process.argv.slice(2);
39 |
40 | var processor = new ArgumentsProcessor(args);
41 | var distortion_per_entry = 0;
42 | var range_start_override = undefined;
43 | var range_end_override = undefined;
44 |
45 | if (!processor.parse()) processor.printUsageAndExit();
46 | var result = processor.result();
47 | var distortion = parseInt(result.distortion);
48 | if (isNaN(distortion)) processor.printUsageAndExit();
49 | // Convert picoseconds to milliseconds.
50 | distortion_per_entry = distortion / 1000000;
51 | var rangelimits = result.range.split(",");
52 | var range_start = parseInt(rangelimits[0]);
53 | var range_end = parseInt(rangelimits[1]);
54 | if (!isNaN(range_start)) range_start_override = range_start;
55 | if (!isNaN(range_end)) range_end_override = range_end;
56 |
57 | var kResX = 1600;
58 | var kResY = 700;
59 | function log_error(text) {
60 | console.error(text);
61 | quit(1);
62 | }
63 | var psc = new PlotScriptComposer(kResX, kResY, log_error);
64 | var collector = psc.collectData(distortion_per_entry);
65 |
66 | processFileLines(result.logFileName, function(readline) {
67 | collector.processLine(readline);
68 | }, function() {
69 | collector.onDone();
70 |
71 | psc.findPlotRange(range_start_override, range_end_override);
72 | console.log("set terminal pngcairo size " + kResX + "," + kResY +
73 | " enhanced font 'Helvetica,10'");
74 | psc.assembleOutput(console.log);
75 | });
76 |
--------------------------------------------------------------------------------
/bin/tickprocessor-driver.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var tickProcessorModule = require('../lib/tickprocessor');
4 | var ArgumentsProcessor = tickProcessorModule.ArgumentsProcessor;
5 | var TickProcessor = tickProcessorModule.TickProcessor;
6 | var SnapshotLogProcessor = tickProcessorModule.SnapshotLogProcessor;
7 |
8 | // Copyright 2012 the V8 project authors. All rights reserved.
9 | // Redistribution and use in source and binary forms, with or without
10 | // modification, are permitted provided that the following conditions are
11 | // met:
12 | //
13 | // * Redistributions of source code must retain the above copyright
14 | // notice, this list of conditions and the following disclaimer.
15 | // * Redistributions in binary form must reproduce the above
16 | // copyright notice, this list of conditions and the following
17 | // disclaimer in the documentation and/or other materials provided
18 | // with the distribution.
19 | // * Neither the name of Google Inc. nor the names of its
20 | // contributors may be used to endorse or promote products derived
21 | // from this software without specific prior written permission.
22 | //
23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 |
35 |
36 | // Tick Processor's code flow.
37 |
38 | function processArguments(args) {
39 | var processor = new ArgumentsProcessor(args);
40 | if (processor.parse()) {
41 | return processor.result();
42 | } else {
43 | processor.printUsageAndExit();
44 | }
45 | }
46 |
47 | var entriesProviders = {
48 | 'unix': tickProcessorModule.UnixCppEntriesProvider,
49 | 'windows': tickProcessorModule.WindowsCppEntriesProvider,
50 | 'mac': tickProcessorModule.MacCppEntriesProvider
51 | };
52 |
53 | var params = processArguments(process.argv.slice(2));
54 | var snapshotLogProcessor;
55 | if (params.snapshotLogFileName) {
56 | snapshotLogProcessor = new SnapshotLogProcessor(params.ignoreUnknown);
57 | snapshotLogProcessor.processLogFile(params.snapshotLogFileName, processTicks);
58 | }
59 |
60 | function processTicks() {
61 | var tickProcessor = new TickProcessor(
62 | new (entriesProviders[params.platform])(params.nm, params.targetRootFS),
63 | params.separateIc,
64 | params.callGraphSize,
65 | params.ignoreUnknown,
66 | params.stateFilter,
67 | snapshotLogProcessor,
68 | params.distortion,
69 | params.range,
70 | params.sourceMap);
71 | tickProcessor.processLogFile(params.logFileName, tickProcessor.printStatistics.bind(tickProcessor));
72 | }
73 |
74 | processTicks();
75 |
--------------------------------------------------------------------------------
/lib/codemap.js:
--------------------------------------------------------------------------------
1 | var SplayTree = require('./splaytree');
2 | // Copyright 2009 the V8 project authors. All rights reserved.
3 | // Redistribution and use in source and binary forms, with or without
4 | // modification, are permitted provided that the following conditions are
5 | // met:
6 | //
7 | // * Redistributions of source code must retain the above copyright
8 | // notice, this list of conditions and the following disclaimer.
9 | // * Redistributions in binary form must reproduce the above
10 | // copyright notice, this list of conditions and the following
11 | // disclaimer in the documentation and/or other materials provided
12 | // with the distribution.
13 | // * Neither the name of Google Inc. nor the names of its
14 | // contributors may be used to endorse or promote products derived
15 | // from this software without specific prior written permission.
16 | //
17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 |
30 | /**
31 | * Constructs a mapper that maps addresses into code entries.
32 | *
33 | * @constructor
34 | */
35 | function CodeMap() {
36 | /**
37 | * Dynamic code entries. Used for JIT compiled code.
38 | */
39 | this.dynamics_ = new SplayTree();
40 |
41 | /**
42 | * Name generator for entries having duplicate names.
43 | */
44 | this.dynamicsNameGen_ = new CodeMap.NameGenerator();
45 |
46 | /**
47 | * Static code entries. Used for statically compiled code.
48 | */
49 | this.statics_ = new SplayTree();
50 |
51 | /**
52 | * Libraries entries. Used for the whole static code libraries.
53 | */
54 | this.libraries_ = new SplayTree();
55 |
56 | /**
57 | * Map of memory pages occupied with static code.
58 | */
59 | this.pages_ = [];
60 | };
61 |
62 |
63 | /**
64 | * The number of alignment bits in a page address.
65 | */
66 | CodeMap.PAGE_ALIGNMENT = 12;
67 |
68 |
69 | /**
70 | * Page size in bytes.
71 | */
72 | CodeMap.PAGE_SIZE =
73 | 1 << CodeMap.PAGE_ALIGNMENT;
74 |
75 |
76 | /**
77 | * Adds a dynamic (i.e. moveable and discardable) code entry.
78 | *
79 | * @param {number} start The starting address.
80 | * @param {CodeMap.CodeEntry} codeEntry Code entry object.
81 | */
82 | CodeMap.prototype.addCode = function(start, codeEntry) {
83 | this.deleteAllCoveredNodes_(this.dynamics_, start, start + codeEntry.size);
84 | this.dynamics_.insert(start, codeEntry);
85 | };
86 |
87 |
88 | /**
89 | * Moves a dynamic code entry. Throws an exception if there is no dynamic
90 | * code entry with the specified starting address.
91 | *
92 | * @param {number} from The starting address of the entry being moved.
93 | * @param {number} to The destination address.
94 | */
95 | CodeMap.prototype.moveCode = function(from, to) {
96 | var removedNode = this.dynamics_.remove(from);
97 | this.deleteAllCoveredNodes_(this.dynamics_, to, to + removedNode.value.size);
98 | this.dynamics_.insert(to, removedNode.value);
99 | };
100 |
101 |
102 | /**
103 | * Discards a dynamic code entry. Throws an exception if there is no dynamic
104 | * code entry with the specified starting address.
105 | *
106 | * @param {number} start The starting address of the entry being deleted.
107 | */
108 | CodeMap.prototype.deleteCode = function(start) {
109 | var removedNode = this.dynamics_.remove(start);
110 | };
111 |
112 |
113 | /**
114 | * Adds a library entry.
115 | *
116 | * @param {number} start The starting address.
117 | * @param {CodeMap.CodeEntry} codeEntry Code entry object.
118 | */
119 | CodeMap.prototype.addLibrary = function(
120 | start, codeEntry) {
121 | this.markPages_(start, start + codeEntry.size);
122 | this.libraries_.insert(start, codeEntry);
123 | };
124 |
125 |
126 | /**
127 | * Adds a static code entry.
128 | *
129 | * @param {number} start The starting address.
130 | * @param {CodeMap.CodeEntry} codeEntry Code entry object.
131 | */
132 | CodeMap.prototype.addStaticCode = function(
133 | start, codeEntry) {
134 | this.statics_.insert(start, codeEntry);
135 | };
136 |
137 |
138 | /**
139 | * @private
140 | */
141 | CodeMap.prototype.markPages_ = function(start, end) {
142 | for (var addr = start; addr <= end;
143 | addr += CodeMap.PAGE_SIZE) {
144 | this.pages_[addr >>> CodeMap.PAGE_ALIGNMENT] = 1;
145 | }
146 | };
147 |
148 |
149 | /**
150 | * @private
151 | */
152 | CodeMap.prototype.deleteAllCoveredNodes_ = function(tree, start, end) {
153 | var to_delete = [];
154 | var addr = end - 1;
155 | while (addr >= start) {
156 | var node = tree.findGreatestLessThan(addr);
157 | if (!node) break;
158 | var start2 = node.key, end2 = start2 + node.value.size;
159 | if (start2 < end && start < end2) to_delete.push(start2);
160 | addr = start2 - 1;
161 | }
162 | for (var i = 0, l = to_delete.length; i < l; ++i) tree.remove(to_delete[i]);
163 | };
164 |
165 |
166 | /**
167 | * @private
168 | */
169 | CodeMap.prototype.isAddressBelongsTo_ = function(addr, node) {
170 | return addr >= node.key && addr < (node.key + node.value.size);
171 | };
172 |
173 |
174 | /**
175 | * @private
176 | */
177 | CodeMap.prototype.findInTree_ = function(tree, addr) {
178 | var node = tree.findGreatestLessThan(addr);
179 | return node && this.isAddressBelongsTo_(addr, node) ? node.value : null;
180 | };
181 |
182 |
183 | /**
184 | * Finds a code entry that contains the specified address. Both static and
185 | * dynamic code entries are considered.
186 | *
187 | * @param {number} addr Address.
188 | */
189 | CodeMap.prototype.findEntry = function(addr) {
190 | var pageAddr = addr >>> CodeMap.PAGE_ALIGNMENT;
191 | if (pageAddr in this.pages_) {
192 | // Static code entries can contain "holes" of unnamed code.
193 | // In this case, the whole library is assigned to this address.
194 | return this.findInTree_(this.statics_, addr) ||
195 | this.findInTree_(this.libraries_, addr);
196 | }
197 | var min = this.dynamics_.findMin();
198 | var max = this.dynamics_.findMax();
199 | if (max != null && addr < (max.key + max.value.size) && addr >= min.key) {
200 | var dynaEntry = this.findInTree_(this.dynamics_, addr);
201 | if (dynaEntry == null) return null;
202 | // Dedupe entry name.
203 | if (!dynaEntry.nameUpdated_) {
204 | dynaEntry.name = this.dynamicsNameGen_.getName(dynaEntry.name);
205 | dynaEntry.nameUpdated_ = true;
206 | }
207 | return dynaEntry;
208 | }
209 | return null;
210 | };
211 |
212 |
213 | /**
214 | * Returns a dynamic code entry using its starting address.
215 | *
216 | * @param {number} addr Address.
217 | */
218 | CodeMap.prototype.findDynamicEntryByStartAddress =
219 | function(addr) {
220 | var node = this.dynamics_.find(addr);
221 | return node ? node.value : null;
222 | };
223 |
224 |
225 | /**
226 | * Returns an array of all dynamic code entries.
227 | */
228 | CodeMap.prototype.getAllDynamicEntries = function() {
229 | return this.dynamics_.exportValues();
230 | };
231 |
232 |
233 | /**
234 | * Returns an array of pairs of all dynamic code entries and their addresses.
235 | */
236 | CodeMap.prototype.getAllDynamicEntriesWithAddresses = function() {
237 | return this.dynamics_.exportKeysAndValues();
238 | };
239 |
240 |
241 | /**
242 | * Returns an array of all static code entries.
243 | */
244 | CodeMap.prototype.getAllStaticEntries = function() {
245 | return this.statics_.exportValues();
246 | };
247 |
248 |
249 | /**
250 | * Returns an array of all libraries entries.
251 | */
252 | CodeMap.prototype.getAllLibrariesEntries = function() {
253 | return this.libraries_.exportValues();
254 | };
255 |
256 |
257 | /**
258 | * Creates a code entry object.
259 | *
260 | * @param {number} size Code entry size in bytes.
261 | * @param {string} opt_name Code entry name.
262 | * @constructor
263 | */
264 | CodeMap.CodeEntry = function(size, opt_name) {
265 | this.size = size;
266 | this.name = opt_name || '';
267 | this.nameUpdated_ = false;
268 | };
269 |
270 |
271 | CodeMap.CodeEntry.prototype.getName = function() {
272 | return this.name;
273 | };
274 |
275 |
276 | CodeMap.CodeEntry.prototype.toString = function() {
277 | return this.name + ': ' + this.size.toString(16);
278 | };
279 |
280 |
281 | CodeMap.NameGenerator = function() {
282 | this.knownNames_ = {};
283 | };
284 |
285 |
286 | CodeMap.NameGenerator.prototype.getName = function(name) {
287 | if (!(name in this.knownNames_)) {
288 | this.knownNames_[name] = 0;
289 | return name;
290 | }
291 | var count = ++this.knownNames_[name];
292 | return name + ' {' + count + '}';
293 | };
294 |
295 | module.exports.CodeMap = CodeMap;
296 | module.exports.CodeEntry = CodeMap.CodeEntry;
297 |
--------------------------------------------------------------------------------
/lib/composer.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 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 | var codeMapModule = require('./codemap');
29 | var CodeMap = codeMapModule.CodeMap;
30 | var logReaderModule = require('./logreader');
31 | var LogReader = logReaderModule.LogReader;
32 |
33 | Array.prototype.top = function() {
34 | if (this.length == 0) return undefined;
35 | return this[this.length - 1];
36 | }
37 |
38 |
39 | function PlotScriptComposer(kResX, kResY, error_output) {
40 | // Constants.
41 | var kV8BinarySuffixes = ["/d8", "/libv8.so"];
42 | var kStackFrames = 8; // Stack frames to display in the plot.
43 |
44 | var kTimerEventWidth = 0.33; // Width of each timeline.
45 | var kExecutionFrameWidth = 0.2; // Width of the top stack frame line.
46 | var kStackFrameWidth = 0.1; // Width of the lower stack frame lines.
47 | var kGapWidth = 0.05; // Gap between stack frame lines.
48 |
49 | var kY1Offset = 11; // Offset for stack frame vs. event lines.
50 | var kDeoptRow = 7; // Row displaying deopts.
51 | var kGetTimeHeight = 0.5; // Height of marker displaying timed part.
52 | var kMaxDeoptLength = 4; // Draw size of the largest deopt.
53 | var kPauseLabelPadding = 5; // Padding for pause time labels.
54 | var kNumPauseLabels = 7; // Number of biggest pauses to label.
55 | var kCodeKindLabelPadding = 100; // Padding for code kind labels.
56 |
57 | var kTickHalfDuration = 0.5; // Duration of half a tick in ms.
58 | var kMinRangeLength = 0.0005; // Minimum length for an event in ms.
59 |
60 | var kNumThreads = 2; // Number of threads.
61 | var kExecutionThreadId = 0; // ID of main thread.
62 |
63 | // Init values.
64 | var num_timer_event = kY1Offset + 0.5;
65 |
66 | // Data structures.
67 | function TimerEvent(label, color, pause, thread_id) {
68 | assert(thread_id >= 0 && thread_id < kNumThreads, "invalid thread id");
69 | this.label = label;
70 | this.color = color;
71 | this.pause = pause;
72 | this.ranges = [];
73 | this.thread_id = thread_id;
74 | this.index = ++num_timer_event;
75 | }
76 |
77 | function CodeKind(color, kinds) {
78 | this.color = color;
79 | this.in_execution = [];
80 | this.stack_frames = [];
81 | for (var i = 0; i < kStackFrames; i++) this.stack_frames.push([]);
82 | this.kinds = kinds;
83 | }
84 |
85 | function Range(start, end) {
86 | this.start = start; // In milliseconds.
87 | this.end = end; // In milliseconds.
88 | }
89 |
90 | function Deopt(time, size) {
91 | this.time = time; // In milliseconds.
92 | this.size = size; // In bytes.
93 | }
94 |
95 | Range.prototype.duration = function() { return this.end - this.start; }
96 |
97 | function Tick(tick) {
98 | this.tick = tick;
99 | }
100 |
101 | var TimerEvents = {
102 | 'V8.Execute':
103 | new TimerEvent("execution", "#000000", false, 0),
104 | 'V8.External':
105 | new TimerEvent("external", "#3399FF", false, 0),
106 | 'V8.CompileFullCode':
107 | new TimerEvent("compile unopt", "#CC0000", true, 0),
108 | 'V8.RecompileSynchronous':
109 | new TimerEvent("recompile sync", "#CC0044", true, 0),
110 | 'V8.RecompileConcurrent':
111 | new TimerEvent("recompile async", "#CC4499", false, 1),
112 | 'V8.CompileEval':
113 | new TimerEvent("compile eval", "#CC4400", true, 0),
114 | 'V8.IcMiss':
115 | new TimerEvent("ic miss", "#CC9900", false, 0),
116 | 'V8.Parse':
117 | new TimerEvent("parse", "#00CC00", true, 0),
118 | 'V8.PreParse':
119 | new TimerEvent("preparse", "#44CC00", true, 0),
120 | 'V8.ParseLazy':
121 | new TimerEvent("lazy parse", "#00CC44", true, 0),
122 | 'V8.GCScavenger':
123 | new TimerEvent("gc scavenge", "#0044CC", true, 0),
124 | 'V8.GCCompactor':
125 | new TimerEvent("gc compaction", "#4444CC", true, 0),
126 | 'V8.GCContext':
127 | new TimerEvent("gc context", "#4400CC", true, 0),
128 | };
129 |
130 | var CodeKinds = {
131 | 'external ': new CodeKind("#3399FF", [-2]),
132 | 'runtime ': new CodeKind("#000000", [-1]),
133 | 'full code': new CodeKind("#DD0000", [0]),
134 | 'opt code ': new CodeKind("#00EE00", [1]),
135 | 'code stub': new CodeKind("#FF00FF", [2]),
136 | 'built-in ': new CodeKind("#AA00AA", [3]),
137 | 'inl.cache': new CodeKind("#4444AA",
138 | [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]),
139 | 'reg.exp. ': new CodeKind("#0000FF", [15]),
140 | };
141 |
142 | var code_map = new CodeMap();
143 | var execution_pauses = [];
144 | var deopts = [];
145 | var gettime = [];
146 | var event_stack = [];
147 | var last_time_stamp = [];
148 | for (var i = 0; i < kNumThreads; i++) {
149 | event_stack[i] = [];
150 | last_time_stamp[i] = -1;
151 | }
152 |
153 | var range_start = undefined;
154 | var range_end = undefined;
155 | var obj_index = 0;
156 | var pause_tolerance = 0.005; // Milliseconds.
157 | var distortion = 0;
158 |
159 | // Utility functions.
160 | function assert(something, message) {
161 | if (!something) {
162 | var error = new Error(message);
163 | error_output(error.stack);
164 | }
165 | }
166 |
167 | function FindCodeKind(kind) {
168 | for (name in CodeKinds) {
169 | if (CodeKinds[name].kinds.indexOf(kind) >= 0) {
170 | return CodeKinds[name];
171 | }
172 | }
173 | }
174 |
175 | function TicksToRanges(ticks) {
176 | var ranges = [];
177 | for (var i = 0; i < ticks.length; i++) {
178 | var tick = ticks[i].tick;
179 | ranges.push(
180 | new Range(tick - kTickHalfDuration, tick + kTickHalfDuration));
181 | }
182 | return ranges;
183 | }
184 |
185 | function MergeRanges(ranges) {
186 | ranges.sort(function(a, b) { return a.start - b.start; });
187 | var result = [];
188 | var j = 0;
189 | for (var i = 0; i < ranges.length; i = j) {
190 | var merge_start = ranges[i].start;
191 | if (merge_start > range_end) break; // Out of plot range.
192 | var merge_end = ranges[i].end;
193 | for (j = i + 1; j < ranges.length; j++) {
194 | var next_range = ranges[j];
195 | // Don't merge ranges if there is no overlap (incl. merge tolerance).
196 | if (next_range.start > merge_end + pause_tolerance) break;
197 | // Merge ranges.
198 | if (next_range.end > merge_end) { // Extend range end.
199 | merge_end = next_range.end;
200 | }
201 | }
202 | if (merge_end < range_start) continue; // Out of plot range.
203 | if (merge_end < merge_start) continue; // Not an actual range.
204 | result.push(new Range(merge_start, merge_end));
205 | }
206 | return result;
207 | }
208 |
209 | function RestrictRangesTo(ranges, start, end) {
210 | var result = [];
211 | for (var i = 0; i < ranges.length; i++) {
212 | if (ranges[i].start <= end && ranges[i].end >= start) {
213 | result.push(new Range(Math.max(ranges[i].start, start),
214 | Math.min(ranges[i].end, end)));
215 | }
216 | }
217 | return result;
218 | }
219 |
220 | // Public methods.
221 | this.collectData = function(distortion_per_entry) {
222 |
223 | var last_timestamp = 0;
224 |
225 | // Parse functions.
226 | var parseTimeStamp = function(timestamp) {
227 | int_timestamp = parseInt(timestamp);
228 | assert(int_timestamp >= last_timestamp, "Inconsistent timestamps.");
229 | last_timestamp = int_timestamp;
230 | distortion += distortion_per_entry;
231 | return int_timestamp / 1000 - distortion;
232 | }
233 |
234 | var processTimerEventStart = function(name, start) {
235 | // Find out the thread id.
236 | var new_event = TimerEvents[name];
237 | if (new_event === undefined) return;
238 | var thread_id = new_event.thread_id;
239 |
240 | start = Math.max(last_time_stamp[thread_id] + kMinRangeLength, start);
241 |
242 | // Last event on this thread is done with the start of this event.
243 | var last_event = event_stack[thread_id].top();
244 | if (last_event !== undefined) {
245 | var new_range = new Range(last_time_stamp[thread_id], start);
246 | last_event.ranges.push(new_range);
247 | }
248 | event_stack[thread_id].push(new_event);
249 | last_time_stamp[thread_id] = start;
250 | };
251 |
252 | var processTimerEventEnd = function(name, end) {
253 | // Find out about the thread_id.
254 | var finished_event = TimerEvents[name];
255 | var thread_id = finished_event.thread_id;
256 | assert(finished_event === event_stack[thread_id].pop(),
257 | "inconsistent event stack");
258 |
259 | end = Math.max(last_time_stamp[thread_id] + kMinRangeLength, end);
260 |
261 | var new_range = new Range(last_time_stamp[thread_id], end);
262 | finished_event.ranges.push(new_range);
263 | last_time_stamp[thread_id] = end;
264 | };
265 |
266 | var processCodeCreateEvent = function(type, kind, address, size, name) {
267 | var code_entry = new CodeMap.CodeEntry(size, name);
268 | code_entry.kind = kind;
269 | code_map.addCode(address, code_entry);
270 | };
271 |
272 | var processCodeMoveEvent = function(from, to) {
273 | code_map.moveCode(from, to);
274 | };
275 |
276 | var processCodeDeleteEvent = function(address) {
277 | code_map.deleteCode(address);
278 | };
279 |
280 | var processCodeDeoptEvent = function(time, size) {
281 | deopts.push(new Deopt(time, size));
282 | }
283 |
284 | var processCurrentTimeEvent = function(time) {
285 | gettime.push(time);
286 | }
287 |
288 | var processSharedLibrary = function(name, start, end) {
289 | var code_entry = new CodeMap.CodeEntry(end - start, name);
290 | code_entry.kind = -3; // External code kind.
291 | for (var i = 0; i < kV8BinarySuffixes.length; i++) {
292 | var suffix = kV8BinarySuffixes[i];
293 | if (name.indexOf(suffix, name.length - suffix.length) >= 0) {
294 | code_entry.kind = -1; // V8 runtime code kind.
295 | break;
296 | }
297 | }
298 | code_map.addLibrary(start, code_entry);
299 | };
300 |
301 | var processTickEvent = function(
302 | pc, timer, unused_x, unused_y, vmstate, stack) {
303 | var tick = new Tick(timer);
304 |
305 | var entry = code_map.findEntry(pc);
306 | if (entry) FindCodeKind(entry.kind).in_execution.push(tick);
307 |
308 | for (var i = 0; i < kStackFrames; i++) {
309 | if (!stack[i]) break;
310 | var entry = code_map.findEntry(stack[i]);
311 | if (entry) FindCodeKind(entry.kind).stack_frames[i].push(tick);
312 | }
313 | };
314 | // Collect data from log.
315 | var logreader = new LogReader(
316 | { 'timer-event-start': { parsers: [null, parseTimeStamp],
317 | processor: processTimerEventStart },
318 | 'timer-event-end': { parsers: [null, parseTimeStamp],
319 | processor: processTimerEventEnd },
320 | 'shared-library': { parsers: [null, parseInt, parseInt],
321 | processor: processSharedLibrary },
322 | 'code-creation': { parsers: [null, parseInt, parseInt, parseInt, null],
323 | processor: processCodeCreateEvent },
324 | 'code-move': { parsers: [parseInt, parseInt],
325 | processor: processCodeMoveEvent },
326 | 'code-delete': { parsers: [parseInt],
327 | processor: processCodeDeleteEvent },
328 | 'code-deopt': { parsers: [parseTimeStamp, parseInt],
329 | processor: processCodeDeoptEvent },
330 | 'current-time': { parsers: [parseTimeStamp],
331 | processor: processCurrentTimeEvent },
332 | 'tick': { parsers: [parseInt, parseTimeStamp,
333 | null, null, parseInt, 'var-args'],
334 | processor: processTickEvent }
335 | });
336 |
337 | return {
338 | processLine: function(line) {
339 | logreader.processLogLine(line);
340 | },
341 | onDone: function() {
342 | for (name in TimerEvents) {
343 | var event = TimerEvents[name];
344 | if (!event.pause) continue;
345 | var ranges = event.ranges;
346 | for (var j = 0; j < ranges.length; j++) execution_pauses.push(ranges[j]);
347 | }
348 | execution_pauses = MergeRanges(execution_pauses);
349 | }
350 | };
351 | };
352 |
353 |
354 | this.findPlotRange = function(
355 | range_start_override, range_end_override, result_callback) {
356 | var start_found = (range_start_override || range_start_override == 0);
357 | var end_found = (range_end_override || range_end_override == 0);
358 | range_start = start_found ? range_start_override : Infinity;
359 | range_end = end_found ? range_end_override : -Infinity;
360 |
361 | if (!start_found || !end_found) {
362 | for (name in TimerEvents) {
363 | var ranges = TimerEvents[name].ranges;
364 | for (var i = 0; i < ranges.length; i++) {
365 | if (ranges[i].start < range_start && !start_found) {
366 | range_start = ranges[i].start;
367 | }
368 | if (ranges[i].end > range_end && !end_found) {
369 | range_end = ranges[i].end;
370 | }
371 | }
372 | }
373 |
374 | for (codekind in CodeKinds) {
375 | var ticks = CodeKinds[codekind].in_execution;
376 | for (var i = 0; i < ticks.length; i++) {
377 | if (ticks[i].tick < range_start && !start_found) {
378 | range_start = ticks[i].tick;
379 | }
380 | if (ticks[i].tick > range_end && !end_found) {
381 | range_end = ticks[i].tick;
382 | }
383 | }
384 | }
385 | }
386 | // Set pause tolerance to something appropriate for the plot resolution
387 | // to make it easier for gnuplot.
388 | pause_tolerance = (range_end - range_start) / kResX / 10;
389 |
390 | if (typeof result_callback === 'function') {
391 | result_callback(range_start, range_end);
392 | }
393 | };
394 |
395 |
396 | this.assembleOutput = function(output) {
397 | output("set yrange [0:" + (num_timer_event + 1) + "]");
398 | output("set xlabel \"execution time in ms\"");
399 | output("set xrange [" + range_start + ":" + range_end + "]");
400 | output("set style fill pattern 2 bo 1");
401 | output("set style rect fs solid 1 noborder");
402 | output("set style line 1 lt 1 lw 1 lc rgb \"#000000\"");
403 | output("set border 15 lw 0.2"); // Draw thin border box.
404 | output("set style line 2 lt 1 lw 1 lc rgb \"#9944CC\"");
405 | output("set xtics out nomirror");
406 | output("unset key");
407 |
408 | function DrawBarBase(color, start, end, top, bottom, transparency) {
409 | obj_index++;
410 | command = "set object " + obj_index + " rect";
411 | command += " from " + start + ", " + top;
412 | command += " to " + end + ", " + bottom;
413 | command += " fc rgb \"" + color + "\"";
414 | if (transparency) {
415 | command += " fs transparent solid " + transparency;
416 | }
417 | output(command);
418 | }
419 |
420 | function DrawBar(row, color, start, end, width) {
421 | DrawBarBase(color, start, end, row + width, row - width);
422 | }
423 |
424 | function DrawHalfBar(row, color, start, end, width) {
425 | DrawBarBase(color, start, end, row, row - width);
426 | }
427 |
428 | var percentages = {};
429 | var total = 0;
430 | for (var name in TimerEvents) {
431 | var event = TimerEvents[name];
432 | var ranges = RestrictRangesTo(event.ranges, range_start, range_end);
433 | var sum =
434 | ranges.map(function(range) { return range.duration(); })
435 | .reduce(function(a, b) { return a + b; }, 0);
436 | percentages[name] = (sum / (range_end - range_start) * 100).toFixed(1);
437 | }
438 |
439 | // Plot deopts.
440 | deopts.sort(function(a, b) { return b.size - a.size; });
441 | var max_deopt_size = deopts.length > 0 ? deopts[0].size : Infinity;
442 |
443 | for (var i = 0; i < deopts.length; i++) {
444 | var deopt = deopts[i];
445 | DrawHalfBar(kDeoptRow, "#9944CC", deopt.time,
446 | deopt.time + 10 * pause_tolerance,
447 | deopt.size / max_deopt_size * kMaxDeoptLength);
448 | }
449 |
450 | // Plot current time polls.
451 | if (gettime.length > 1) {
452 | var start = gettime[0];
453 | var end = gettime.pop();
454 | DrawBarBase("#0000BB", start, end, kGetTimeHeight, 0, 0.2);
455 | }
456 |
457 | // Name Y-axis.
458 | var ytics = [];
459 | for (name in TimerEvents) {
460 | var index = TimerEvents[name].index;
461 | var label = TimerEvents[name].label;
462 | ytics.push('"' + label + ' (' + percentages[name] + '%%)" ' + index);
463 | }
464 | ytics.push('"code kind color coding" ' + kY1Offset);
465 | ytics.push('"code kind in execution" ' + (kY1Offset - 1));
466 | ytics.push('"top ' + kStackFrames + ' js stack frames"' + ' ' +
467 | (kY1Offset - 2));
468 | ytics.push('"pause times" 0');
469 | ytics.push('"max deopt size: ' + (max_deopt_size / 1024).toFixed(1) +
470 | ' kB" ' + kDeoptRow);
471 | output("set ytics out nomirror (" + ytics.join(', ') + ")");
472 |
473 | // Plot timeline.
474 | for (var name in TimerEvents) {
475 | var event = TimerEvents[name];
476 | var ranges = MergeRanges(event.ranges);
477 | for (var i = 0; i < ranges.length; i++) {
478 | DrawBar(event.index, event.color,
479 | ranges[i].start, ranges[i].end,
480 | kTimerEventWidth);
481 | }
482 | }
483 |
484 | // Plot code kind gathered from ticks.
485 | for (var name in CodeKinds) {
486 | var code_kind = CodeKinds[name];
487 | var offset = kY1Offset - 1;
488 | // Top most frame.
489 | var row = MergeRanges(TicksToRanges(code_kind.in_execution));
490 | for (var j = 0; j < row.length; j++) {
491 | DrawBar(offset, code_kind.color,
492 | row[j].start, row[j].end, kExecutionFrameWidth);
493 | }
494 | offset = offset - 2 * kExecutionFrameWidth - kGapWidth;
495 | // Javascript frames.
496 | for (var i = 0; i < kStackFrames; i++) {
497 | offset = offset - 2 * kStackFrameWidth - kGapWidth;
498 | row = MergeRanges(TicksToRanges(code_kind.stack_frames[i]));
499 | for (var j = 0; j < row.length; j++) {
500 | DrawBar(offset, code_kind.color,
501 | row[j].start, row[j].end, kStackFrameWidth);
502 | }
503 | }
504 | }
505 |
506 | // Add labels as legend for code kind colors.
507 | var padding = kCodeKindLabelPadding * (range_end - range_start) / kResX;
508 | var label_x = range_start;
509 | var label_y = kY1Offset;
510 | for (var name in CodeKinds) {
511 | label_x += padding;
512 | output("set label \"" + name + "\" at " + label_x + "," + label_y +
513 | " textcolor rgb \"" + CodeKinds[name].color + "\"" +
514 | " font \"Helvetica,9'\"");
515 | obj_index++;
516 | }
517 |
518 | if (execution_pauses.length == 0) {
519 | // Force plot and return without plotting execution pause impulses.
520 | output("plot 1/0");
521 | return;
522 | }
523 |
524 | // Label the longest pauses.
525 | execution_pauses =
526 | RestrictRangesTo(execution_pauses, range_start, range_end);
527 | execution_pauses.sort(
528 | function(a, b) { return b.duration() - a.duration(); });
529 |
530 | var max_pause_time = execution_pauses.length > 0
531 | ? execution_pauses[0].duration() : 0;
532 | padding = kPauseLabelPadding * (range_end - range_start) / kResX;
533 | var y_scale = kY1Offset / max_pause_time / 2;
534 | for (var i = 0; i < execution_pauses.length && i < kNumPauseLabels; i++) {
535 | var pause = execution_pauses[i];
536 | var label_content = (pause.duration() | 0) + " ms";
537 | var label_x = pause.end + padding;
538 | var label_y = Math.max(1, (pause.duration() * y_scale));
539 | output("set label \"" + label_content + "\" at " +
540 | label_x + "," + label_y + " font \"Helvetica,7'\"");
541 | obj_index++;
542 | }
543 |
544 | // Scale second Y-axis appropriately.
545 | var y2range = max_pause_time * num_timer_event / kY1Offset * 2;
546 | output("set y2range [0:" + y2range + "]");
547 | // Plot graph with impulses as data set.
548 | output("plot '-' using 1:2 axes x1y2 with impulses ls 1");
549 | for (var i = 0; i < execution_pauses.length; i++) {
550 | var pause = execution_pauses[i];
551 | output(pause.end + " " + pause.duration());
552 | obj_index++;
553 | }
554 | output("e");
555 | return obj_index;
556 | };
557 | }
558 |
559 | module.exports.PlotScriptComposer = PlotScriptComposer;
560 |
--------------------------------------------------------------------------------
/lib/consarray.js:
--------------------------------------------------------------------------------
1 | // Copyright 2009 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 |
29 | /**
30 | * Constructs a ConsArray object. It is used mainly for tree traversal.
31 | * In this use case we have lots of arrays that we need to iterate
32 | * sequentally. The internal Array implementation is horribly slow
33 | * when concatenating on large (10K items) arrays due to memory copying.
34 | * That's why we avoid copying memory and insead build a linked list
35 | * of arrays to iterate through.
36 | *
37 | * @constructor
38 | */
39 | function ConsArray() {
40 | this.tail_ = new ConsArray.Cell(null, null);
41 | this.currCell_ = this.tail_;
42 | this.currCellPos_ = 0;
43 | };
44 |
45 |
46 | /**
47 | * Concatenates another array for iterating. Empty arrays are ignored.
48 | * This operation can be safely performed during ongoing ConsArray
49 | * iteration.
50 | *
51 | * @param {Array} arr Array to concatenate.
52 | */
53 | ConsArray.prototype.concat = function(arr) {
54 | if (arr.length > 0) {
55 | this.tail_.data = arr;
56 | this.tail_ = this.tail_.next = new ConsArray.Cell(null, null);
57 | }
58 | };
59 |
60 |
61 | /**
62 | * Whether the end of iteration is reached.
63 | */
64 | ConsArray.prototype.atEnd = function() {
65 | return this.currCell_ === null ||
66 | this.currCell_.data === null ||
67 | this.currCellPos_ >= this.currCell_.data.length;
68 | };
69 |
70 |
71 | /**
72 | * Returns the current item, moves to the next one.
73 | */
74 | ConsArray.prototype.next = function() {
75 | var result = this.currCell_.data[this.currCellPos_++];
76 | if (this.currCellPos_ >= this.currCell_.data.length) {
77 | this.currCell_ = this.currCell_.next;
78 | this.currCellPos_ = 0;
79 | }
80 | return result;
81 | };
82 |
83 |
84 | /**
85 | * A cell object used for constructing a list in ConsArray.
86 | *
87 | * @constructor
88 | */
89 | ConsArray.Cell = function(data, next) {
90 | this.data = data;
91 | this.next = next;
92 | };
93 | module.exports = ConsArray;
94 |
--------------------------------------------------------------------------------
/lib/csvparser.js:
--------------------------------------------------------------------------------
1 | // Copyright 2009 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 |
29 | /**
30 | * Creates a CSV lines parser.
31 | */
32 | function CsvParser() {
33 | };
34 |
35 |
36 | /**
37 | * A regex for matching a CSV field.
38 | * @private
39 | */
40 | CsvParser.CSV_FIELD_RE_ = /^"((?:[^"]|"")*)"|([^,]*)/;
41 |
42 |
43 | /**
44 | * A regex for matching a double quote.
45 | * @private
46 | */
47 | CsvParser.DOUBLE_QUOTE_RE_ = /""/g;
48 |
49 |
50 | /**
51 | * Parses a line of CSV-encoded values. Returns an array of fields.
52 | *
53 | * @param {string} line Input line.
54 | */
55 | CsvParser.prototype.parseLine = function(line) {
56 | var fieldRe = CsvParser.CSV_FIELD_RE_;
57 | var doubleQuoteRe = CsvParser.DOUBLE_QUOTE_RE_;
58 | var pos = 0;
59 | var endPos = line.length;
60 | var fields = [];
61 | if (endPos > 0) {
62 | do {
63 | var fieldMatch = fieldRe.exec(line.substr(pos));
64 | if (typeof fieldMatch[1] === "string") {
65 | var field = fieldMatch[1];
66 | pos += field.length + 3; // Skip comma and quotes.
67 | fields.push(field.replace(doubleQuoteRe, '"'));
68 | } else {
69 | // The second field pattern will match anything, thus
70 | // in the worst case the match will be an empty string.
71 | var field = fieldMatch[2];
72 | pos += field.length + 1; // Skip comma.
73 | fields.push(field);
74 | }
75 | } while (pos <= endPos);
76 | }
77 | return fields;
78 | };
79 | module.exports = CsvParser;
80 |
--------------------------------------------------------------------------------
/lib/logreader.js:
--------------------------------------------------------------------------------
1 | var CsvParser = require('./csvparser');
2 | // Copyright 2011 the V8 project authors. All rights reserved.
3 | // Redistribution and use in source and binary forms, with or without
4 | // modification, are permitted provided that the following conditions are
5 | // met:
6 | //
7 | // * Redistributions of source code must retain the above copyright
8 | // notice, this list of conditions and the following disclaimer.
9 | // * Redistributions in binary form must reproduce the above
10 | // copyright notice, this list of conditions and the following
11 | // disclaimer in the documentation and/or other materials provided
12 | // with the distribution.
13 | // * Neither the name of Google Inc. nor the names of its
14 | // contributors may be used to endorse or promote products derived
15 | // from this software without specific prior written permission.
16 | //
17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 | /**
30 | * @fileoverview Log Reader is used to process log file produced by V8.
31 | */
32 |
33 |
34 | /**
35 | * Base class for processing log files.
36 | *
37 | * @param {Array.