source: webkit/trunk/JavaScriptCore/jsc.cpp@ 43100

Last change on this file since 43100 was 43047, checked in by [email protected], 16 years ago

2009-04-30 Gavin Barraclough <[email protected]>

Reviewed by Maciej Stachowiak.

Add SamplingFlags mechanism.

This mechanism allows fine-grained JSC and JavaScript program aware
performance measurement. The mechanism provides a set of 32 flags,
numbered #1..#32. Flag #16 is initially set, and all other flags
are cleared. Flags may be set and cleared from within

Enable by setting ENABLE_SAMPLING_FLAGS to 1 in wtf/Platform.h.
Disabled by default, no performance impact. Flags may be modified
by calling SamplingFlags::setFlag() and SamplingFlags::clearFlag()
from within JSC implementation, or by calling setSamplingFlag() and
clearSamplingFlag() from JavaScript.

The flags are sampled with a frequency of 10000Hz, and the highest
set flag in recorded, allowing multiple events to be measured (with
the highest flag number representing the highest priority).

Disabled by default; no performance impact.

  • JavaScriptCore.exp:
  • bytecode/SamplingTool.cpp: (JSC::SamplingFlags::sample): (JSC::SamplingFlags::start): (JSC::SamplingFlags::stop): (JSC::SamplingThread::threadStartFunc): (JSC::SamplingThread::start): (JSC::SamplingThread::stop): (JSC::ScopeSampleRecord::sample): (JSC::SamplingTool::doRun): (JSC::SamplingTool::sample): (JSC::SamplingTool::start): (JSC::SamplingTool::stop):
  • bytecode/SamplingTool.h: (JSC::SamplingFlags::setFlag): (JSC::SamplingFlags::clearFlag): (JSC::SamplingTool::SamplingTool):
  • jsc.cpp: (GlobalObject::GlobalObject): (functionSetSamplingFlag): (functionClearSamplingFlag): (runWithScripts):
  • wtf/Platform.h:
  • Property svn:eol-style set to native
File size: 17.8 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 "BytecodeGenerator.h"
26#include "Completion.h"
27#include "InitializeThreading.h"
28#include "JSArray.h"
29#include "JSLock.h"
30#include "PrototypeFunction.h"
31#include "SamplingTool.h"
32#include <math.h>
33#include <stdio.h>
34#include <stdlib.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) && !PLATFORM(WIN_CE)
55#include <crtdbg.h>
56#include <windows.h>
57#include <mmsystem.h>
58#endif
59
60#if PLATFORM(QT)
61#include <QCoreApplication>
62#include <QDateTime>
63#endif
64
65using namespace JSC;
66using namespace WTF;
67
68static void cleanupGlobalData(JSGlobalData*);
69static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
70
71static JSValuePtr functionPrint(ExecState*, JSObject*, JSValuePtr, const ArgList&);
72static JSValuePtr functionDebug(ExecState*, JSObject*, JSValuePtr, const ArgList&);
73static JSValuePtr functionGC(ExecState*, JSObject*, JSValuePtr, const ArgList&);
74static JSValuePtr functionVersion(ExecState*, JSObject*, JSValuePtr, const ArgList&);
75static JSValuePtr functionRun(ExecState*, JSObject*, JSValuePtr, const ArgList&);
76static JSValuePtr functionLoad(ExecState*, JSObject*, JSValuePtr, const ArgList&);
77static JSValuePtr functionReadline(ExecState*, JSObject*, JSValuePtr, const ArgList&);
78static NO_RETURN JSValuePtr functionQuit(ExecState*, JSObject*, JSValuePtr, const ArgList&);
79
80#if ENABLE(SAMPLING_FLAGS)
81static JSValuePtr functionSetSamplingFlag(ExecState*, JSObject*, JSValuePtr, const ArgList&);
82static JSValuePtr functionClearSamplingFlag(ExecState*, JSObject*, JSValuePtr, const ArgList&);
83#endif
84
85struct Script {
86 bool isFile;
87 char *argument;
88
89 Script(bool isFile, char *argument)
90 : isFile(isFile)
91 , argument(argument)
92 {
93 }
94};
95
96struct Options {
97 Options()
98 : interactive(false)
99 , dump(false)
100 {
101 }
102
103 bool interactive;
104 bool dump;
105 Vector<Script> scripts;
106 Vector<UString> arguments;
107};
108
109static const char interactivePrompt[] = "> ";
110static const UString interpreterName("Interpreter");
111
112class StopWatch {
113public:
114 void start();
115 void stop();
116 long getElapsedMS(); // call stop() first
117
118private:
119#if PLATFORM(QT)
120 uint m_startTime;
121 uint m_stopTime;
122#elif PLATFORM(WIN_OS)
123 DWORD m_startTime;
124 DWORD m_stopTime;
125#else
126 // Windows does not have timeval, disabling this class for now (bug 7399)
127 timeval m_startTime;
128 timeval m_stopTime;
129#endif
130};
131
132void StopWatch::start()
133{
134#if PLATFORM(QT)
135 QDateTime t = QDateTime::currentDateTime();
136 m_startTime = t.toTime_t() * 1000 + t.time().msec();
137#elif PLATFORM(WIN_OS)
138 m_startTime = timeGetTime();
139#else
140 gettimeofday(&m_startTime, 0);
141#endif
142}
143
144void StopWatch::stop()
145{
146#if PLATFORM(QT)
147 QDateTime t = QDateTime::currentDateTime();
148 m_stopTime = t.toTime_t() * 1000 + t.time().msec();
149#elif PLATFORM(WIN_OS)
150 m_stopTime = timeGetTime();
151#else
152 gettimeofday(&m_stopTime, 0);
153#endif
154}
155
156long StopWatch::getElapsedMS()
157{
158#if PLATFORM(WIN_OS) || PLATFORM(QT)
159 return m_stopTime - m_startTime;
160#else
161 timeval elapsedTime;
162 timersub(&m_stopTime, &m_startTime, &elapsedTime);
163
164 return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
165#endif
166}
167
168class GlobalObject : public JSGlobalObject {
169public:
170 GlobalObject(const Vector<UString>& arguments);
171 virtual UString className() const { return "global"; }
172};
173COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
174ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
175
176GlobalObject::GlobalObject(const Vector<UString>& arguments)
177 : JSGlobalObject()
178{
179 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
180 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
181 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
182 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
183 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
184 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
185 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
186 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
187
188#if ENABLE(SAMPLING_FLAGS)
189 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlag"), functionSetSamplingFlag));
190 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlag"), functionClearSamplingFlag));
191#endif
192
193 JSObject* array = constructEmptyArray(globalExec());
194 for (size_t i = 0; i < arguments.size(); ++i)
195 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
196 putDirect(Identifier(globalExec(), "arguments"), array);
197}
198
199JSValuePtr functionPrint(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args)
200{
201 for (unsigned i = 0; i < args.size(); ++i) {
202 if (i != 0)
203 putchar(' ');
204
205 printf("%s", args.at(i).toString(exec).UTF8String().c_str());
206 }
207
208 putchar('\n');
209 fflush(stdout);
210 return jsUndefined();
211}
212
213JSValuePtr functionDebug(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args)
214{
215 fprintf(stderr, "--> %s\n", args.at(0).toString(exec).UTF8String().c_str());
216 return jsUndefined();
217}
218
219JSValuePtr functionGC(ExecState* exec, JSObject*, JSValuePtr, const ArgList&)
220{
221 JSLock lock(false);
222 exec->heap()->collect();
223 return jsUndefined();
224}
225
226JSValuePtr functionVersion(ExecState*, JSObject*, JSValuePtr, const ArgList&)
227{
228 // We need this function for compatibility with the Mozilla JS tests but for now
229 // we don't actually do any version-specific handling
230 return jsUndefined();
231}
232
233JSValuePtr functionRun(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args)
234{
235 StopWatch stopWatch;
236 UString fileName = args.at(0).toString(exec);
237 Vector<char> script;
238 if (!fillBufferWithContentsOfFile(fileName, script))
239 return throwError(exec, GeneralError, "Could not open file.");
240
241 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
242
243 stopWatch.start();
244 evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
245 stopWatch.stop();
246
247 return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS());
248}
249
250JSValuePtr functionLoad(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args)
251{
252 UString fileName = args.at(0).toString(exec);
253 Vector<char> script;
254 if (!fillBufferWithContentsOfFile(fileName, script))
255 return throwError(exec, GeneralError, "Could not open file.");
256
257 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
258 Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
259 if (result.complType() == Throw)
260 exec->setException(result.value());
261 return result.value();
262}
263
264#if ENABLE(SAMPLING_FLAGS)
265JSValuePtr functionSetSamplingFlag(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args)
266{
267 unsigned flag = static_cast<unsigned>(args.at(0).toNumber(exec));
268
269 // Sanitize the input into the range 1..32.
270 if (flag > 32)
271 flag &= 31;
272 if (!flag)
273 flag = 32;
274
275 SamplingFlags::setFlag(flag);
276
277 return jsNull();
278}
279
280JSValuePtr functionClearSamplingFlag(ExecState* exec, JSObject*, JSValuePtr, const ArgList& args)
281{
282 unsigned flag = static_cast<unsigned>(args.at(0).toNumber(exec));
283
284 // Sanitize the input into the range 1..32.
285 if (flag > 32)
286 flag &= 31;
287 if (!flag)
288 flag = 32;
289
290 SamplingFlags::clearFlag(flag);
291
292 return jsNull();
293}
294#endif
295
296JSValuePtr functionReadline(ExecState* exec, JSObject*, JSValuePtr, const ArgList&)
297{
298 Vector<char, 256> line;
299 int c;
300 while ((c = getchar()) != EOF) {
301 // FIXME: Should we also break on \r?
302 if (c == '\n')
303 break;
304 line.append(c);
305 }
306 line.append('\0');
307 return jsString(exec, line.data());
308}
309
310JSValuePtr functionQuit(ExecState* exec, JSObject*, JSValuePtr, const ArgList&)
311{
312 cleanupGlobalData(&exec->globalData());
313 exit(EXIT_SUCCESS);
314}
315
316// Use SEH for Release builds only to get rid of the crash report dialog
317// (luckily the same tests fail in Release and Debug builds so far). Need to
318// be in a separate main function because the jscmain function requires object
319// unwinding.
320
321#if COMPILER(MSVC) && !defined(_DEBUG)
322#define TRY __try {
323#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
324#else
325#define TRY
326#define EXCEPT(x)
327#endif
328
329int jscmain(int argc, char** argv, JSGlobalData*);
330
331int main(int argc, char** argv)
332{
333#if defined(_DEBUG) && PLATFORM(WIN_OS)
334 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
335 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
336 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
337 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
338 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
339 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
340#endif
341
342#if COMPILER(MSVC) && !PLATFORM(WIN_CE)
343 timeBeginPeriod(1);
344#endif
345
346#if PLATFORM(QT)
347 QCoreApplication app(argc, argv);
348#endif
349
350 // Initialize JSC before getting JSGlobalData.
351 JSC::initializeThreading();
352
353 // We can't use destructors in the following code because it uses Windows
354 // Structured Exception Handling
355 int res = 0;
356 JSGlobalData* globalData = JSGlobalData::create().releaseRef();
357 TRY
358 res = jscmain(argc, argv, globalData);
359 EXCEPT(res = 3)
360
361 cleanupGlobalData(globalData);
362 return res;
363}
364
365static void cleanupGlobalData(JSGlobalData* globalData)
366{
367 JSLock lock(false);
368 globalData->heap.destroy();
369 globalData->deref();
370}
371
372static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
373{
374 UString script;
375 UString fileName;
376 Vector<char> scriptBuffer;
377
378 if (dump)
379 BytecodeGenerator::setDumpsGeneratedCode(true);
380
381#if ENABLE(OPCODE_SAMPLING)
382 Interpreter* interpreter = globalObject->globalData()->interpreter;
383 interpreter->setSampler(new SamplingTool(interpreter));
384 interpreter->sampler()->setup();
385#endif
386#if ENABLE(SAMPLING_FLAGS)
387 SamplingFlags::start();
388#endif
389
390 bool success = true;
391 for (size_t i = 0; i < scripts.size(); i++) {
392 if (scripts[i].isFile) {
393 fileName = scripts[i].argument;
394 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
395 return false; // fail early so we can catch missing files
396 script = scriptBuffer.data();
397 } else {
398 script = scripts[i].argument;
399 fileName = "[Command Line]";
400 }
401
402#if ENABLE(SAMPLING_THREAD)
403 SamplingThread::start();
404#endif
405
406 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
407 success = success && completion.complType() != Throw;
408 if (dump) {
409 if (completion.complType() == Throw)
410 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
411 else
412 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
413 }
414
415#if ENABLE(SAMPLING_THREAD)
416 SamplingThread::stop();
417#endif
418
419 globalObject->globalExec()->clearException();
420 }
421
422#if ENABLE(SAMPLING_FLAGS)
423 SamplingFlags::stop();
424#endif
425#if ENABLE(OPCODE_SAMPLING)
426 interpreter->sampler()->dump(globalObject->globalExec());
427 delete interpreter->sampler();
428#endif
429 return success;
430}
431
432static
433#if !HAVE(READLINE)
434NO_RETURN
435#endif
436void runInteractive(GlobalObject* globalObject)
437{
438 while (true) {
439#if HAVE(READLINE)
440 char* line = readline(interactivePrompt);
441 if (!line)
442 break;
443 if (line[0])
444 add_history(line);
445 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
446 free(line);
447#else
448 puts(interactivePrompt);
449 Vector<char, 256> line;
450 int c;
451 while ((c = getchar()) != EOF) {
452 // FIXME: Should we also break on \r?
453 if (c == '\n')
454 break;
455 line.append(c);
456 }
457 line.append('\0');
458 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
459#endif
460 if (completion.complType() == Throw)
461 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
462 else
463 printf("%s\n", completion.value().toString(globalObject->globalExec()).UTF8String().c_str());
464
465 globalObject->globalExec()->clearException();
466 }
467 printf("\n");
468}
469
470static NO_RETURN void printUsageStatement(bool help = false)
471{
472 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
473 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
474 fprintf(stderr, " -e Evaluate argument as script code\n");
475 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
476 fprintf(stderr, " -h|--help Prints this help message\n");
477 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
478 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
479 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
480}
481
482static void parseArguments(int argc, char** argv, Options& options)
483{
484 int i = 1;
485 for (; i < argc; ++i) {
486 const char* arg = argv[i];
487 if (strcmp(arg, "-f") == 0) {
488 if (++i == argc)
489 printUsageStatement();
490 options.scripts.append(Script(true, argv[i]));
491 continue;
492 }
493 if (strcmp(arg, "-e") == 0) {
494 if (++i == argc)
495 printUsageStatement();
496 options.scripts.append(Script(false, argv[i]));
497 continue;
498 }
499 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
500 printUsageStatement(true);
501 }
502 if (strcmp(arg, "-i") == 0) {
503 options.interactive = true;
504 continue;
505 }
506 if (strcmp(arg, "-d") == 0) {
507 options.dump = true;
508 continue;
509 }
510 if (strcmp(arg, "-s") == 0) {
511#if PLATFORM(UNIX)
512 signal(SIGILL, _exit);
513 signal(SIGFPE, _exit);
514 signal(SIGBUS, _exit);
515 signal(SIGSEGV, _exit);
516#endif
517 continue;
518 }
519 if (strcmp(arg, "--") == 0) {
520 ++i;
521 break;
522 }
523 options.scripts.append(Script(true, argv[i]));
524 }
525
526 if (options.scripts.isEmpty())
527 options.interactive = true;
528
529 for (; i < argc; ++i)
530 options.arguments.append(argv[i]);
531}
532
533int jscmain(int argc, char** argv, JSGlobalData* globalData)
534{
535 JSLock lock(false);
536
537 Options options;
538 parseArguments(argc, argv, options);
539
540 GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
541 bool success = runWithScripts(globalObject, options.scripts, options.dump);
542 if (options.interactive && success)
543 runInteractive(globalObject);
544
545 return success ? 0 : 3;
546}
547
548static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
549{
550 FILE* f = fopen(fileName.UTF8String().c_str(), "r");
551 if (!f) {
552 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
553 return false;
554 }
555
556 size_t buffer_size = 0;
557 size_t buffer_capacity = 1024;
558
559 buffer.resize(buffer_capacity);
560
561 while (!feof(f) && !ferror(f)) {
562 buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f);
563 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
564 buffer_capacity *= 2;
565 buffer.resize(buffer_capacity);
566 }
567 }
568 fclose(f);
569 buffer[buffer_size] = '\0';
570
571 return true;
572}
Note: See TracBrowser for help on using the repository browser.