source: webkit/trunk/JavaScriptCore/kjs/Shell.cpp@ 37891

Last change on this file since 37891 was 37891, checked in by [email protected], 17 years ago

2008-10-25 Geoffrey Garen <[email protected]>

Reviewed by Sam Weinig, with Gavin Barraclough's help.


Fixed Sampling Tool:

  • Made CodeBlock sampling work with CTI
  • Improved accuracy by unifying most sampling data into a single 32bit word, which can be written / read atomically.
  • Split out three different #ifdefs for modularity: OPCODE_SAMPLING; CODEBLOCK_SAMPLING; OPCODE_STATS.
  • Improved reporting clarity
  • Refactored for code clarity
  • VM/CTI.cpp: (JSC::CTI::emitCTICall): (JSC::CTI::compileOpCall): (JSC::CTI::emitSlowScriptCheck): (JSC::CTI::compileBinaryArithOpSlowCase): (JSC::CTI::privateCompileMainPass): (JSC::CTI::privateCompileSlowCases): (JSC::CTI::privateCompile):
  • VM/CTI.h: Updated CTI codegen to use the unified SamplingTool interface for encoding samples. (This required passing the current vPC to a lot more functions, since the unified interface samples the current vPC.) Added hooks for writing the current CodeBlock* on function entry and after a function call, for the sake of the CodeBlock sampler. Removed obsolete hook for clearing the current sample inside op_end. Also removed the custom enum used to differentiate flavors of op_call, since the OpcodeID enum works just as well. (This was important in an earlier version of the patch, but now it's just cleanup.)
  • VM/CodeBlock.cpp: (JSC::CodeBlock::lineNumberForVPC):
  • VM/CodeBlock.h: Upated for refactored #ifdefs. Changed lineNumberForVPC to be robust against vPCs not recorded for exception handling, since the Sampler may ask for an arbitrary vPC.
  • VM/Machine.cpp: (JSC::Machine::execute): (JSC::Machine::privateExecute): (JSC::Machine::cti_op_call_NotJSFunction): (JSC::Machine::cti_op_construct_NotJSConstruct):
  • VM/Machine.h: (JSC::Machine::setSampler): (JSC::Machine::sampler): (JSC::Machine::jitCodeBuffer): Upated for refactored #ifdefs. Changed Machine to use SamplingTool helper objects to record movement in and out of host code. This makes samples a bit more precise.


  • VM/Opcode.cpp: (JSC::OpcodeStats::~OpcodeStats):
  • VM/Opcode.h: Upated for refactored #ifdefs. Added a little more padding, to accomodate our more verbose opcode names.
  • VM/SamplingTool.cpp: (JSC::ScopeSampleRecord::sample): Only count a sample toward our total if we actually record it. This solves cases where a CodeBlock will claim to have been sampled many times, with reported samples that don't match.

(JSC::SamplingTool::run): Read the current sample into a Sample helper
object, to ensure that the data doesn't change while we're analyzing it,
and to help decode the data. Only access the CodeBlock sampling hash
table if CodeBlock sampling has been enabled, so non-CodeBlock sampling
runs can operate with even less overhead.

(JSC::SamplingTool::dump): I reorganized this code a lot to print the
most important info at the top, print as a table, annotate and document
the stuff I didn't understand when I started, etc.

  • VM/SamplingTool.h: New helper classes, described above.
  • kjs/Parser.h:
  • kjs/Shell.cpp: (runWithScripts):
  • kjs/nodes.cpp: (JSC::ScopeNode::ScopeNode): Updated for new sampling APIs.
  • wtf/Platform.h: Moved sampling #defines here, since our custom is to put ENABLE #defines into Platform.h. Made explicit the fact that CODEBLOCK_SAMPLING depends on OPCODE_SAMPLING.
  • Property svn:eol-style set to native
File size: 16.0 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2006 Bjoern Graf ([email protected])
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24
25#include "CodeGenerator.h"
26#include "InitializeThreading.h"
27#include "JSArray.h"
28#include "JSLock.h"
29#include "PrototypeFunction.h"
30#include "SamplingTool.h"
31#include "completion.h"
32#include "interpreter.h"
33#include <math.h>
34#include <stdio.h>
35#include <string.h>
36
37#if !PLATFORM(WIN_OS)
38#include <unistd.h>
39#endif
40
41#if HAVE(READLINE)
42#include <readline/history.h>
43#include <readline/readline.h>
44#endif
45
46#if HAVE(SYS_TIME_H)
47#include <sys/time.h>
48#endif
49
50#if PLATFORM(UNIX)
51#include <signal.h>
52#endif
53
54#if COMPILER(MSVC)
55#include <crtdbg.h>
56#include <windows.h>
57#endif
58
59#if PLATFORM(QT)
60#include <QCoreApplication>
61#include <QDateTime>
62#endif
63
64using namespace JSC;
65using namespace WTF;
66
67static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
68
69static JSValue* functionPrint(ExecState*, JSObject*, JSValue*, const ArgList&);
70static JSValue* functionDebug(ExecState*, JSObject*, JSValue*, const ArgList&);
71static JSValue* functionGC(ExecState*, JSObject*, JSValue*, const ArgList&);
72static JSValue* functionVersion(ExecState*, JSObject*, JSValue*, const ArgList&);
73static JSValue* functionRun(ExecState*, JSObject*, JSValue*, const ArgList&);
74static JSValue* functionLoad(ExecState*, JSObject*, JSValue*, const ArgList&);
75static JSValue* functionReadline(ExecState*, JSObject*, JSValue*, const ArgList&);
76static JSValue* functionQuit(ExecState*, JSObject*, JSValue*, const ArgList&);
77
78struct Options {
79 Options()
80 : interactive(false)
81 , prettyPrint(false)
82 , dump(false)
83 {
84 }
85
86 bool interactive;
87 bool prettyPrint;
88 bool dump;
89 Vector<UString> fileNames;
90 Vector<UString> arguments;
91};
92
93static const char interactivePrompt[] = "> ";
94static const UString interpreterName("Interpreter");
95
96class StopWatch {
97public:
98 void start();
99 void stop();
100 long getElapsedMS(); // call stop() first
101
102private:
103#if PLATFORM(QT)
104 uint m_startTime;
105 uint m_stopTime;
106#elif PLATFORM(WIN_OS)
107 DWORD m_startTime;
108 DWORD m_stopTime;
109#else
110 // Windows does not have timeval, disabling this class for now (bug 7399)
111 timeval m_startTime;
112 timeval m_stopTime;
113#endif
114};
115
116void StopWatch::start()
117{
118#if PLATFORM(QT)
119 QDateTime t = QDateTime::currentDateTime();
120 m_startTime = t.toTime_t() * 1000 + t.time().msec();
121#elif PLATFORM(WIN_OS)
122 m_startTime = timeGetTime();
123#else
124 gettimeofday(&m_startTime, 0);
125#endif
126}
127
128void StopWatch::stop()
129{
130#if PLATFORM(QT)
131 QDateTime t = QDateTime::currentDateTime();
132 m_stopTime = t.toTime_t() * 1000 + t.time().msec();
133#elif PLATFORM(WIN_OS)
134 m_stopTime = timeGetTime();
135#else
136 gettimeofday(&m_stopTime, 0);
137#endif
138}
139
140long StopWatch::getElapsedMS()
141{
142#if PLATFORM(WIN_OS) || PLATFORM(QT)
143 return m_stopTime - m_startTime;
144#else
145 timeval elapsedTime;
146 timersub(&m_stopTime, &m_startTime, &elapsedTime);
147
148 return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
149#endif
150}
151
152class GlobalObject : public JSGlobalObject {
153public:
154 GlobalObject(const Vector<UString>& arguments);
155 virtual UString className() const { return "global"; }
156};
157COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
158ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
159
160GlobalObject::GlobalObject(const Vector<UString>& arguments)
161 : JSGlobalObject()
162{
163 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
164 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
165 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
166 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
167 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
168 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
169 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
170 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
171
172 JSObject* array = constructEmptyArray(globalExec());
173 for (size_t i = 0; i < arguments.size(); ++i)
174 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
175 putDirect(Identifier(globalExec(), "arguments"), array);
176
177 Interpreter::setShouldPrintExceptions(true);
178}
179
180JSValue* functionPrint(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
181{
182 for (unsigned i = 0; i < args.size(); ++i) {
183 if (i != 0)
184 putchar(' ');
185
186 printf("%s", args.at(exec, i)->toString(exec).UTF8String().c_str());
187 }
188
189 putchar('\n');
190 fflush(stdout);
191 return jsUndefined();
192}
193
194JSValue* functionDebug(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
195{
196 fprintf(stderr, "--> %s\n", args.at(exec, 0)->toString(exec).UTF8String().c_str());
197 return jsUndefined();
198}
199
200JSValue* functionGC(ExecState* exec, JSObject*, JSValue*, const ArgList&)
201{
202 JSLock lock(false);
203 exec->heap()->collect();
204 return jsUndefined();
205}
206
207JSValue* functionVersion(ExecState*, JSObject*, JSValue*, const ArgList&)
208{
209 // We need this function for compatibility with the Mozilla JS tests but for now
210 // we don't actually do any version-specific handling
211 return jsUndefined();
212}
213
214JSValue* functionRun(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
215{
216 StopWatch stopWatch;
217 UString fileName = args.at(exec, 0)->toString(exec);
218 Vector<char> script;
219 if (!fillBufferWithContentsOfFile(fileName, script))
220 return throwError(exec, GeneralError, "Could not open file.");
221
222 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
223
224 stopWatch.start();
225 Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
226 stopWatch.stop();
227
228 return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS());
229}
230
231JSValue* functionLoad(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
232{
233 UString fileName = args.at(exec, 0)->toString(exec);
234 Vector<char> script;
235 if (!fillBufferWithContentsOfFile(fileName, script))
236 return throwError(exec, GeneralError, "Could not open file.");
237
238 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
239 Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
240
241 return jsUndefined();
242}
243
244JSValue* functionReadline(ExecState* exec, JSObject*, JSValue*, const ArgList&)
245{
246 Vector<char, 256> line;
247 int c;
248 while ((c = getchar()) != EOF) {
249 // FIXME: Should we also break on \r?
250 if (c == '\n')
251 break;
252 line.append(c);
253 }
254 line.append('\0');
255 return jsString(exec, line.data());
256}
257
258JSValue* functionQuit(ExecState*, JSObject*, JSValue*, const ArgList&)
259{
260 exit(0);
261#if !COMPILER(MSVC)
262 // MSVC knows that exit(0) never returns, so it flags this return statement as unreachable.
263 return jsUndefined();
264#endif
265}
266
267// Use SEH for Release builds only to get rid of the crash report dialog
268// (luckily the same tests fail in Release and Debug builds so far). Need to
269// be in a separate main function because the jscmain function requires object
270// unwinding.
271
272#if COMPILER(MSVC) && !defined(_DEBUG)
273#define TRY __try {
274#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
275#else
276#define TRY
277#define EXCEPT(x)
278#endif
279
280int jscmain(int argc, char** argv, JSGlobalData*);
281
282int main(int argc, char** argv)
283{
284#if defined(_DEBUG) && PLATFORM(WIN_OS)
285 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
286 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
287 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
288 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
289 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
290 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
291#endif
292
293#if PLATFORM(QT)
294 QCoreApplication app(argc, argv);
295#endif
296
297 int res = 0;
298 TRY
299 res = jscmain(argc, argv, JSGlobalData::create().releaseRef());
300 EXCEPT(res = 3)
301 return res;
302}
303
304static bool prettyPrintScript(ExecState* exec, const UString& fileName, const Vector<char>& script)
305{
306 int errLine = 0;
307 UString errMsg;
308 RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), makeSource(script.data(), fileName), &errLine, &errMsg);
309 if (!programNode) {
310 fprintf(stderr, "%s:%d: %s.\n", fileName.UTF8String().c_str(), errLine, errMsg.UTF8String().c_str());
311 return false;
312 }
313
314 printf("%s\n", programNode->toString().UTF8String().c_str());
315 return true;
316}
317
318static bool runWithScripts(GlobalObject* globalObject, const Vector<UString>& fileNames, bool prettyPrint, bool dump)
319{
320 Vector<char> script;
321
322 if (dump)
323 CodeGenerator::setDumpsGeneratedCode(true);
324
325#if ENABLE(OPCODE_SAMPLING)
326 Machine* machine = globalObject->globalData()->machine;
327 machine->setSampler(new SamplingTool(machine));
328#endif
329
330 bool success = true;
331 for (size_t i = 0; i < fileNames.size(); i++) {
332 UString fileName = fileNames[i];
333
334 if (!fillBufferWithContentsOfFile(fileName, script))
335 return false; // fail early so we can catch missing files
336
337 if (prettyPrint)
338 prettyPrintScript(globalObject->globalExec(), fileName, script);
339 else {
340#if ENABLE(OPCODE_SAMPLING)
341 machine->sampler()->start();
342#endif
343 Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
344 success = success && completion.complType() != Throw;
345 if (dump) {
346 if (completion.complType() == Throw)
347 printf("Exception: %s\n", completion.value()->toString(globalObject->globalExec()).ascii());
348 else
349 printf("End: %s\n", completion.value()->toString(globalObject->globalExec()).ascii());
350 }
351
352 globalObject->globalExec()->clearException();
353
354#if ENABLE(OPCODE_SAMPLING)
355 machine->sampler()->stop();
356#endif
357 }
358 }
359
360#if ENABLE(OPCODE_SAMPLING)
361 machine->sampler()->dump(globalObject->globalExec());
362 delete machine->sampler();
363#endif
364 return success;
365}
366
367static void runInteractive(GlobalObject* globalObject)
368{
369 while (true) {
370#if HAVE(READLINE)
371 char* line = readline(interactivePrompt);
372 if (!line)
373 break;
374 if (line[0])
375 add_history(line);
376 Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
377 free(line);
378#else
379 puts(interactivePrompt);
380 Vector<char, 256> line;
381 int c;
382 while ((c = getchar()) != EOF) {
383 // FIXME: Should we also break on \r?
384 if (c == '\n')
385 break;
386 line.append(c);
387 }
388 line.append('\0');
389 Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
390#endif
391 if (completion.complType() == Throw)
392 printf("Exception: %s\n", completion.value()->toString(globalObject->globalExec()).ascii());
393 else
394 printf("%s\n", completion.value()->toString(globalObject->globalExec()).UTF8String().c_str());
395
396 globalObject->globalExec()->clearException();
397 }
398 printf("\n");
399}
400
401static void printUsageStatement()
402{
403 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
404 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
405 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
406 fprintf(stderr, " -h|--help Prints this help message\n");
407 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
408 fprintf(stderr, " -p Prints formatted source code\n");
409 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
410 exit(-1);
411}
412
413static void parseArguments(int argc, char** argv, Options& options)
414{
415 int i = 1;
416 for (; i < argc; ++i) {
417 const char* arg = argv[i];
418 if (strcmp(arg, "-f") == 0) {
419 if (++i == argc)
420 printUsageStatement();
421 options.fileNames.append(argv[i]);
422 continue;
423 }
424 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
425 printUsageStatement();
426 }
427 if (strcmp(arg, "-i") == 0) {
428 options.interactive = true;
429 continue;
430 }
431 if (strcmp(arg, "-p") == 0) {
432 options.prettyPrint = true;
433 continue;
434 }
435 if (strcmp(arg, "-d") == 0) {
436 options.dump = true;
437 continue;
438 }
439 if (strcmp(arg, "-s") == 0) {
440#if PLATFORM(UNIX)
441 signal(SIGILL, _exit);
442 signal(SIGFPE, _exit);
443 signal(SIGBUS, _exit);
444 signal(SIGSEGV, _exit);
445#endif
446 continue;
447 }
448 if (strcmp(arg, "--") == 0) {
449 ++i;
450 break;
451 }
452 options.fileNames.append(argv[i]);
453 }
454
455 if (options.fileNames.isEmpty())
456 options.interactive = true;
457
458 for (; i < argc; ++i)
459 options.arguments.append(argv[i]);
460}
461
462int jscmain(int argc, char** argv, JSGlobalData* globalData)
463{
464 JSC::initializeThreading();
465
466 JSLock lock(false);
467
468 Options options;
469 parseArguments(argc, argv, options);
470
471 GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
472 bool success = runWithScripts(globalObject, options.fileNames, options.prettyPrint, options.dump);
473 if (options.interactive && success)
474 runInteractive(globalObject);
475
476 return success ? 0 : 3;
477}
478
479static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
480{
481 FILE* f = fopen(fileName.UTF8String().c_str(), "r");
482 if (!f) {
483 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
484 return false;
485 }
486
487 size_t buffer_size = 0;
488 size_t buffer_capacity = 1024;
489
490 buffer.resize(buffer_capacity);
491
492 while (!feof(f) && !ferror(f)) {
493 buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f);
494 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
495 buffer_capacity *= 2;
496 buffer.resize(buffer_capacity);
497 }
498 }
499 fclose(f);
500 buffer[buffer_size] = '\0';
501
502 return true;
503}
Note: See TracBrowser for help on using the repository browser.