Bug 607719 followup: deep tests that do not match the current config will run in...
[tamarin-stm.git] / shell / avmshell.cpp
blobaf7f3545511b0a1836547c3ba576f78e8f5be8a9
1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2004-2006
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Adobe AS3 Team
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "avmshell.h"
41 #ifdef VMCFG_NANOJIT
42 #include "../nanojit/nanojit.h"
43 #endif
44 #include <float.h>
46 #include "avmplus-gc-interlock.h"
47 #include "extensions-gc-interlock.h"
48 #include "avmshell-gc-interlock.h"
49 #include "extensions-as3-gc.h"
50 #include "extensions-cpp-gc.h"
51 #include "avmshell-as3-gc.h"
52 #include "avmshell-cpp-gc.h"
54 namespace avmshell
56 #ifdef AVMPLUS_WIN32
57 extern bool show_error; // in avmshellWin.cpp
58 #endif
60 ShellSettings::ShellSettings()
61 : ShellCoreSettings()
62 , programFilename(NULL)
63 , filenames(NULL)
64 , numfiles(-1)
65 , do_selftest(false)
66 , do_repl(false)
67 , do_log(false)
68 , do_projector(false)
69 , numthreads(1)
70 , numworkers(1)
71 , repeats(1)
72 , stackSize(0)
76 ShellCoreImpl::ShellCoreImpl(MMgc::GC* gc, ShellSettings& settings, bool mainthread)
77 : ShellCore(gc)
78 , settings(settings)
79 , mainthread(mainthread)
83 /* virtual */
84 void ShellCoreImpl::setStackLimit()
86 uintptr_t minstack;
87 if (settings.stackSize != 0)
89 // Here we really depend on being called fairly high up on
90 // the thread's stack, because we don't know where the highest
91 // stack address is.
92 minstack = uintptr_t(&minstack) - settings.stackSize + avmshell::kStackMargin;
94 else
96 #ifdef VMCFG_WORKERTHREADS
97 if (mainthread)
98 minstack = Platform::GetInstance()->getMainThreadStackLimit();
99 else {
100 // Here we really depend on being called fairly high up on
101 // the thread's stack, because we don't know where the highest
102 // stack address is.
103 size_t stackheight;
104 pthread_attr_t attr;
105 pthread_attr_init(&attr);
106 pthread_attr_getstacksize(&attr, &stackheight);
107 pthread_attr_destroy(&attr);
108 minstack = uintptr_t(&stackheight) - stackheight + avmshell::kStackMargin;
110 #else
111 minstack = Platform::GetInstance()->getMainThreadStackLimit();
112 #endif
115 // call the non-virtual setter on AvmCore
116 AvmCore::setStackLimit(minstack);
119 /* static */
120 int Shell::run(int argc, char *argv[])
122 MMgc::GCHeap::EnterLockInit();
123 MMgc::GCHeapConfig conf;
124 //conf.verbose = AvmCore::DEFAULT_VERBOSE_ON;
125 MMgc::GCHeap::Init(conf);
127 // Note that output from the command line parser (usage messages, error messages,
128 // and printed version number / feature list) will not go to the log file. We
129 // could fix this if it's a hardship.
132 MMGC_ENTER_RETURN(OUT_OF_MEMORY);
133 ShellSettings settings;
134 parseCommandLine(argc, argv, settings);
137 // code coverage/cheap test
138 MMGC_ENTER_SUSPEND;
141 if (settings.do_log)
142 initializeLogging(settings.numfiles > 0 ? settings.filenames[0] : "AVMLOG");
144 #ifdef VMCFG_WORKERTHREADS
145 if (settings.numworkers == 1 && settings.numthreads == 1 && settings.repeats == 1)
146 singleWorker(settings);
147 else
148 multiWorker(settings);
149 #else
150 singleWorker(settings);
151 #endif
154 #ifdef AVMPLUS_WITH_JNI
155 // This surely does not belong here?
156 if (Java::startup_options) delete Java::startup_options;
157 #endif /* AVMPLUS_WITH_JNI */
159 MMgc::GCHeap::Destroy();
160 MMgc::GCHeap::EnterLockDestroy();
161 return 0;
164 // In the single worker case everything is run on the main thread. This
165 // is where we handler the repl, selftest, and projectors.
167 /* static */
168 void Shell::singleWorker(ShellSettings& settings)
170 MMgc::GCConfig gcconfig;
171 gcconfig.collectionThreshold = settings.gcthreshold;
172 gcconfig.exactTracing = settings.exactgc;
173 MMgc::GC *gc = mmfx_new( MMgc::GC(MMgc::GCHeap::GetGCHeap(), settings.gcMode(), &gcconfig) );
175 MMGC_GCENTER(gc);
176 ShellCore* shell = new ShellCoreImpl(gc, settings, true);
177 Shell::singleWorkerHelper(shell, settings);
178 delete shell;
180 mmfx_delete( gc );
183 /* static */
184 void Shell::singleWorkerHelper(ShellCore* shell, ShellSettings& settings)
186 if (!shell->setup(settings))
187 Platform::GetInstance()->exit(1);
189 #ifdef VMCFG_SELFTEST
190 if (settings.do_selftest) {
191 shell->executeSelftest(settings);
192 return;
194 #endif
196 #ifdef AVMSHELL_PROJECTOR_SUPPORT
197 if (settings.do_projector) {
198 AvmAssert(settings.programFilename != NULL);
199 int exitCode = shell->executeProjector(settings.programFilename);
200 if (exitCode != 0)
201 Platform::GetInstance()->exit(exitCode);
203 #endif
205 #ifdef VMCFG_AOT
206 int exitCode = shell->evaluateFile(settings, NULL);
207 if (exitCode != 0)
208 Platform::GetInstance()->exit(exitCode);
209 return;
210 #endif
212 // execute each abc file
213 for (int i=0 ; i < settings.numfiles ; i++ ) {
214 int exitCode = shell->evaluateFile(settings, settings.filenames[i]);
215 if (exitCode != 0)
216 Platform::GetInstance()->exit(exitCode);
219 #ifdef VMCFG_EVAL
220 if (settings.do_repl)
221 repl(shell);
222 #endif
225 #ifdef VMCFG_WORKERTHREADS
227 //#define LOGGING(x) x
228 #define LOGGING(x)
230 // When we have more than one worker then we spawn as many threads as
231 // called for and create a number of ShellCores corresponding to
232 // the number of workers. Those cores are scheduled on the threads.
233 // The main thread acts as a supervisor, handing out work and scheduling
234 // cores on the threads.
236 // At this time pthreads is required. Windows Vista provides condition
237 // variables and it is a straightforward exercise to map from pthread types
238 // and calls to the Vista types and calls. For older Win32 the situation
239 // is grimmer. If you need to run this code on older Win32, please download
240 // and install the available open-source pthreads libraries for Win32.
242 struct MultiworkerState;
244 struct CoreNode
246 CoreNode(ShellCore* core, int id)
247 : core(core)
248 , id(id)
249 , next(NULL)
253 ~CoreNode()
255 // Destruction order matters.
256 MMgc::GC* gc = core->GetGC();
258 MMGC_GCENTER(gc);
259 #ifdef _DEBUG
260 core->codeContextThread = VMPI_currentThread();
261 #endif
262 delete core;
266 delete gc;
269 ShellCore * const core;
270 const int id;
271 CoreNode * next; // For the LRU list of available cores
274 struct ThreadNode
276 ThreadNode(MultiworkerState& state, int id)
277 : state(state)
278 , id(id)
279 , pendingWork(false)
280 , corenode(NULL)
281 , filename(NULL)
282 , next(NULL)
284 // 'thread' is initialized after construction
285 pthread_cond_init(&c, NULL);
286 pthread_mutex_init(&m, NULL);
289 ~ThreadNode()
291 pthread_cond_destroy(&c);
292 pthread_mutex_destroy(&m);
295 // Called from master, which should not be holding m.
296 void startWork(CoreNode* corenode, const char* filename)
298 pthread_mutex_lock(&m);
299 this->corenode = corenode;
300 this->filename = filename;
301 this->pendingWork = true;
302 pthread_cond_signal(&c);
303 pthread_mutex_unlock(&m);
306 MultiworkerState& state;
307 pthread_t thread;
308 const int id;
309 bool pendingWork;
310 pthread_cond_t c;
311 pthread_mutex_t m; // Protects corenode, filename, next, c
312 CoreNode* corenode; // The core running (or about to run, or just finished running) on this thread
313 const char* filename; // The work given to that core
314 ThreadNode * next; // For the LRU list of available threads
317 struct MultiworkerState
319 MultiworkerState(ShellSettings& settings)
320 : settings(settings)
321 , numthreads(settings.numthreads)
322 , numcores(settings.numworkers)
323 , free_threads(NULL)
324 , free_threads_last(NULL)
325 , free_cores(NULL)
326 , free_cores_last(NULL)
327 , num_free_threads(0)
329 pthread_mutex_init(&m, NULL);
330 pthread_cond_init(&c, NULL);
333 ~MultiworkerState()
335 pthread_mutex_destroy(&m);
336 pthread_cond_destroy(&c);
339 // Called from the slave threads, which should not be holding m.
340 void freeThread(ThreadNode* t)
342 pthread_mutex_lock(&m);
344 if (free_threads_last != NULL)
345 free_threads_last->next = t;
346 else
347 free_threads = t;
348 free_threads_last = t;
350 if (t->corenode != NULL) {
351 if (free_cores_last != NULL)
352 free_cores_last->next = t->corenode;
353 else
354 free_cores = t->corenode;
355 free_cores_last = t->corenode;
358 num_free_threads++;
360 pthread_cond_signal(&c);
361 pthread_mutex_unlock(&m);
364 // Called from the master thread, which must already hold m.
365 bool getThreadAndCore(ThreadNode** t, CoreNode** c)
367 if (free_threads == NULL || free_cores == NULL)
368 return false;
370 *t = free_threads;
371 free_threads = free_threads->next;
372 if (free_threads == NULL)
373 free_threads_last = NULL;
374 (*t)->next = NULL;
376 *c = free_cores;
377 free_cores = free_cores->next;
378 if (free_cores == NULL)
379 free_cores_last = NULL;
380 (*c)->next = NULL;
382 num_free_threads--;
384 return true;
387 ShellSettings& settings;
388 int numthreads;
389 int numcores;
391 // Queues of available threads and cores. Protected by m, availability signaled on c.
392 pthread_mutex_t m;
393 pthread_cond_t c;
394 ThreadNode* free_threads;
395 ThreadNode* free_threads_last;
396 CoreNode* free_cores;
397 CoreNode* free_cores_last;
398 int num_free_threads;
401 static void masterThread(MultiworkerState& state);
402 static void* slaveThread(void *arg);
404 /* static */
405 void Shell::multiWorker(ShellSettings& settings)
407 AvmAssert(!settings.do_repl && !settings.do_projector && !settings.do_selftest);
409 MultiworkerState state(settings);
410 const int numthreads(state.numthreads);
411 const int numcores(state.numcores);
412 ThreadNode** const threads(new ThreadNode*[numthreads]);
413 CoreNode** const cores(new CoreNode*[numcores]);
415 MMgc::GCConfig gcconfig;
416 gcconfig.collectionThreshold = settings.gcthreshold;
417 gcconfig.exactTracing = settings.exactgc;
419 // Going multi-threaded.
421 // Create and start threads. They add themselves to the free list.
422 for ( int i=0 ; i < numthreads ; i++ ) {
423 threads[i] = new ThreadNode(state, i);
424 pthread_create(&threads[i]->thread, NULL, slaveThread, threads[i]);
427 // Create collectors and cores.
428 // Extra credit: perform setup in parallel on the threads.
429 for ( int i=0 ; i < numcores ; i++ ) {
430 MMgc::GC* gc = new MMgc::GC(MMgc::GCHeap::GetGCHeap(), settings.gcMode(), &gcconfig);
431 MMGC_GCENTER(gc);
432 cores[i] = new CoreNode(new ShellCoreImpl(gc, settings, false), i);
433 if (!cores[i]->core->setup(settings))
434 Platform::GetInstance()->exit(1);
437 // Add the cores to the free list.
438 for ( int i=numcores-1 ; i >= 0 ; i-- ) {
439 cores[i]->next = state.free_cores;
440 state.free_cores = cores[i];
442 state.free_cores_last = cores[numcores-1];
444 // No locks are held by the master at this point
445 masterThread(state);
446 // No locks are held by the master at this point
448 // Some threads may still be computing, so just wait for them
449 pthread_mutex_lock(&state.m);
450 while (state.num_free_threads < numthreads)
451 pthread_cond_wait(&state.c, &state.m);
452 pthread_mutex_unlock(&state.m);
454 // Shutdown: feed NULL to all threads to make them exit.
455 for ( int i=0 ; i < numthreads ; i++ )
456 threads[i]->startWork(NULL,NULL);
458 // Wait for all threads to exit.
459 for ( int i=0 ; i < numthreads ; i++ ) {
460 pthread_join(threads[i]->thread, NULL);
461 LOGGING( AvmLog("T%d: joined the main thread\n", i); )
464 // Single threaded again.
466 for ( int i=0 ; i < numthreads ; i++ )
467 delete threads[i];
469 for ( int i=0 ; i < numcores ; i++ )
470 delete cores[i];
472 delete [] threads;
473 delete [] cores;
476 static void masterThread(MultiworkerState& state)
478 const int numfiles(state.settings.numfiles);
479 const int repeats(state.settings.repeats);
480 char** const filenames(state.settings.filenames);
482 pthread_mutex_lock(&state.m);
483 int r=0;
484 for (;;) {
485 int nextfile = 0;
486 for (;;) {
487 ThreadNode* threadnode;
488 CoreNode* corenode;
489 while (state.getThreadAndCore(&threadnode, &corenode)) {
490 LOGGING( AvmLog("Scheduling %s on T%d with C%d\n", filenames[nextfile], threadnode->id, corenode->id); )
491 threadnode->startWork(corenode, filenames[nextfile]);
492 nextfile++;
493 if (nextfile == numfiles) {
494 r++;
495 if (r == repeats)
496 goto finish;
497 nextfile = 0;
501 LOGGING( AvmLog("Waiting for available threads.\n"); )
502 pthread_cond_wait(&state.c, &state.m);
505 finish:
506 pthread_mutex_unlock(&state.m);
509 static void* slaveThread(void *arg)
511 MMGC_ENTER_RETURN(NULL);
513 ThreadNode* self = (ThreadNode*)arg;
514 MultiworkerState& state = self->state;
516 for (;;) {
517 // Signal that we're ready for more work: add self to the list of free threads
519 state.freeThread(self);
521 // Obtain more work. We have to hold self->m here but the master won't touch corenode and
522 // filename until we register for more work, so they don't have to be copied out of
523 // the thread structure.
525 pthread_mutex_lock(&self->m);
526 // Don't wait when pendingWork == true,
527 // slave might have been already signalled but it didn't notice because it wasn't waiting yet.
528 while (self->pendingWork == false)
529 pthread_cond_wait(&self->c, &self->m);
530 pthread_mutex_unlock(&self->m);
532 if (self->corenode == NULL) {
533 LOGGING( AvmLog("T%d: Exiting\n", self->id); )
534 pthread_exit(NULL);
537 // Perform work
538 LOGGING( AvmLog("T%d: Work starting\n", self->id); )
540 MMGC_GCENTER(self->corenode->core->GetGC());
541 #ifdef _DEBUG
542 self->corenode->core->codeContextThread = VMPI_currentThread();
543 #endif
544 self->corenode->core->evaluateFile(state.settings, self->filename); // Ignore the exit code for now
546 LOGGING( AvmLog("T%d: Work completed\n", self->id); )
548 pthread_mutex_lock(&self->m);
549 self->pendingWork = false;
550 pthread_mutex_unlock(&self->m);
554 return (void*) NULL;
557 #endif // VMCFG_WORKERTHREADS
559 #ifdef VMCFG_EVAL
561 /* static */
562 void Shell::repl(ShellCore* shellCore)
564 const int kMaxCommandLine = 1024;
565 char commandLine[kMaxCommandLine];
566 String* input;
568 AvmLog("avmplus interactive shell\n"
569 "Type '?' for help\n\n");
571 for (;;)
573 bool record_time = false;
574 AvmLog("> ");
576 if(Platform::GetInstance()->getUserInput(commandLine, kMaxCommandLine) == NULL)
577 return;
579 commandLine[kMaxCommandLine-1] = 0;
580 if (VMPI_strncmp(commandLine, "?", 1) == 0) {
581 AvmLog("Text entered at the prompt is compiled and evaluated unless\n"
582 "it is one of these commands:\n\n"
583 " ? print help\n"
584 " .input collect lines until a line that reads '.end',\n"
585 " then eval the collected lines\n"
586 " .load file load the file (source or compiled)\n"
587 " .quit leave the repl\n"
588 " .time expr evaluate expr and report the time it took.\n\n");
589 continue;
592 if (VMPI_strncmp(commandLine, ".load", 5) == 0) {
593 const char* s = commandLine+5;
594 while (*s == ' ' || *s == '\t')
595 s++;
596 // wrong, handles only source code
597 //readFileForEval(NULL, newStringLatin1(s));
598 // FIXME: implement .load
599 // Small amount of generalization of the code currently in the main loop should
600 // take care of it.
601 AvmLog("The .load command is not implemented\n");
602 continue;
605 if (VMPI_strncmp(commandLine, ".input", 6) == 0) {
606 input = shellCore->newStringLatin1("");
607 for (;;) {
608 if(Platform::GetInstance()->getUserInput(commandLine, kMaxCommandLine) == NULL)
609 return;
610 commandLine[kMaxCommandLine-1] = 0;
611 if (VMPI_strncmp(commandLine, ".end", 4) == 0)
612 break;
613 input->appendLatin1(commandLine);
615 goto compute;
618 if (VMPI_strncmp(commandLine, ".quit", 5) == 0) {
619 return;
622 if (VMPI_strncmp(commandLine, ".time", 5) == 0) {
623 record_time = true;
624 input = shellCore->newStringLatin1(commandLine+5);
625 goto compute;
628 input = shellCore->newStringLatin1(commandLine);
630 compute:
631 shellCore->evaluateString(input, record_time);
635 #endif // VMCFG_EVAL
637 // open logfile based on a filename
638 /* static */
639 void Shell::initializeLogging(const char* basename)
641 const char* lastDot = VMPI_strrchr(basename, '.');
642 if(lastDot)
644 //filename could contain '/' or '\' as their separator, look for both
645 const char* lastPathSep1 = VMPI_strrchr(basename, '/');
646 const char* lastPathSep2 = VMPI_strrchr(basename, '\\');
647 if(lastPathSep1 < lastPathSep2) //determine the right-most
648 lastPathSep1 = lastPathSep2;
650 //if dot is before the separator, the filename does not have an extension
651 if(lastDot < lastPathSep1)
652 lastDot = NULL;
655 //if no extension then take the entire filename or
656 size_t logFilenameLen = (lastDot == NULL) ? VMPI_strlen(basename) : (lastDot - basename);
658 char* logFilename = new char[logFilenameLen + 5]; // 5 bytes for ".log" + null char
659 VMPI_strncpy(logFilename,basename,logFilenameLen);
660 VMPI_strcpy(logFilename+logFilenameLen,".log");
662 Platform::GetInstance()->initializeLogging(logFilename);
664 delete [] logFilename;
667 /* static */
668 void Shell::parseCommandLine(int argc, char* argv[], ShellSettings& settings)
670 bool print_version = false;
672 // options filenames -- args
674 settings.programFilename = argv[0]; // How portable / reliable is this?
675 for (int i=1; i < argc ; i++) {
676 const char * const arg = argv[i];
678 if (arg[0] == '-')
680 if (arg[1] == '-' && arg[2] == 0) {
681 if (settings.filenames == NULL)
682 settings.filenames = &argv[i];
683 settings.numfiles = int(&argv[i] - settings.filenames);
684 i++;
685 settings.arguments = &argv[i];
686 settings.numargs = argc - i;
687 break;
690 if (arg[1] == 'D') {
691 if (!VMPI_strcmp(arg+2, "timeout")) {
692 settings.interrupts = true;
694 else if (!VMPI_strcmp(arg+2, "version")) {
695 print_version = true;
697 else if (!VMPI_strcmp(arg+2, "nodebugger")) {
698 // allow this option even in non-DEBUGGER builds to make test scripts simpler
699 settings.nodebugger = true;
701 #if defined(AVMPLUS_IA32) && defined(VMCFG_NANOJIT)
702 else if (!VMPI_strcmp(arg+2, "nosse")) {
703 settings.njconfig.i386_sse2 = false;
705 else if (!VMPI_strcmp(arg+2, "fixedesp")) {
706 settings.njconfig.i386_fixed_esp = true;
708 #endif /* AVMPLUS_IA32 */
709 #if defined(AVMPLUS_ARM) && defined(VMCFG_NANOJIT)
710 else if (!VMPI_strcmp(arg+2, "arm_arch")) {
711 settings.njconfig.arm_arch = (uint8_t)VMPI_strtol(argv[++i], 0, 10);
713 else if (!VMPI_strcmp(arg+2, "arm_vfp")) {
714 settings.njconfig.arm_vfp = true;
716 #endif /* AVMPLUS_IA32 */
717 #ifdef VMCFG_VERIFYALL
718 else if (!VMPI_strcmp(arg+2, "verifyall")) {
719 settings.verifyall = true;
721 else if (!VMPI_strcmp(arg+2, "verifyonly")) {
722 settings.verifyall = true;
723 settings.verifyonly = true;
725 #endif /* VMCFG_VERIFYALL */
726 else if (!VMPI_strcmp(arg+2, "greedy")) {
727 settings.greedy = true;
729 else if (!VMPI_strcmp(arg+2, "nogc")) {
730 settings.nogc = true;
732 else if (!VMPI_strcmp(arg+2, "noincgc")) {
733 settings.incremental = false;
735 #ifdef VMCFG_SELECTABLE_EXACT_TRACING
736 else if (!VMPI_strcmp(arg+2, "noexactgc")) {
737 settings.exactgc = false;
739 #endif
740 else if (!VMPI_strcmp(arg+2, "nofixedcheck")) {
741 settings.fixedcheck = false;
743 else if (!VMPI_strcmp(arg+2, "gcthreshold") && i+1 < argc) {
744 settings.gcthreshold = VMPI_strtol(argv[++i], 0, 10);
746 #if defined(DEBUGGER) && !defined(VMCFG_DEBUGGER_STUB)
747 else if (!VMPI_strcmp(arg+2, "astrace") && i+1 < argc ) {
748 settings.astrace_console = VMPI_strtol(argv[++i], 0, 10);
750 else if (!VMPI_strcmp(arg+2, "language")) {
751 settings.langID=-1;
752 for (int j=0;j<kLanguages;j++) {
753 if (!VMPI_strcmp(argv[i+1],languageNames[j].str)) {
754 settings.langID=j;
755 break;
758 if (settings.langID==-1) {
759 settings.langID = VMPI_atoi(argv[i+1]);
761 i++;
763 #endif /* defined(DEBUGGER) && !defined(VMCFG_DEBUGGER_STUB) */
764 #ifdef VMCFG_SELFTEST
765 else if (!VMPI_strncmp(arg+2, "selftest", 8)) {
766 settings.do_selftest = true;
767 if (arg[10] == '=') {
768 VMPI_strncpy(settings.st_mem, arg+11, sizeof(settings.st_mem));
769 settings.st_mem[sizeof(settings.st_mem)-1] = 0;
770 char *p = settings.st_mem;
771 settings.st_component = p;
772 while (*p && *p != ',')
773 p++;
774 if (*p == ',')
775 *p++ = 0;
776 settings.st_category = p;
777 while (*p && *p != ',')
778 p++;
779 if (*p == ',')
780 *p++ = 0;
781 settings.st_name = p;
782 if (*settings.st_component == 0)
783 settings.st_component = NULL;
784 if (*settings.st_category == 0)
785 settings.st_category = NULL;
786 if (*settings.st_name == 0)
787 settings.st_name = NULL;
790 #endif /* VMCFG_SELFTEST */
791 #ifdef AVMPLUS_VERBOSE
792 else if (!VMPI_strncmp(arg+2, "verbose", 7)) {
793 settings.do_verbose = AvmCore::DEFAULT_VERBOSE_ON; // all 'on' by default
794 if (arg[9] == '=') {
795 char* badFlag;
796 settings.do_verbose = AvmCore::parseVerboseFlags(&arg[10], badFlag);
797 if (badFlag) {
798 AvmLog("Unknown verbose flag while parsing '%s'\n", badFlag);
799 usage();
803 #endif /* AVMPLUS_VERBOSE */
804 #ifdef VMCFG_NANOJIT
805 else if (!VMPI_strcmp(arg+2, "nocse")) {
806 settings.njconfig.cseopt = false;
808 else if (!VMPI_strcmp(arg+2, "jitordie")) {
809 settings.runmode = RM_jit_all;
810 settings.jitordie = true;
812 #endif /* VMCFG_NANOJIT */
813 else if (!VMPI_strcmp(arg+2, "interp")) {
814 settings.runmode = RM_interp_all;
816 else {
817 AvmLog("Unrecognized option %s\n", arg);
818 usage();
821 else if (!VMPI_strcmp(arg, "-cache_bindings") && i+1 < argc) {
822 settings.cacheSizes.bindings = (uint16_t)VMPI_strtol(argv[++i], 0, 10);
824 else if (!VMPI_strcmp(arg, "-cache_metadata") && i+1 < argc) {
825 settings.cacheSizes.metadata = (uint16_t)VMPI_strtol(argv[++i], 0, 10);
827 else if (!VMPI_strcmp(arg, "-cache_methods") && i+1 < argc ) {
828 settings.cacheSizes.methods = (uint16_t)VMPI_strtol(argv[++i], 0, 10);
830 #ifdef VMCFG_NANOJIT
831 else if (!VMPI_strcmp(arg, "-jitharden")) {
832 settings.njconfig.harden_nop_insertion = true;
833 settings.njconfig.harden_function_alignment = true;
835 else if (!VMPI_strcmp(arg, "-Ojit")) {
836 settings.runmode = RM_jit_all;
838 #endif /* VMCFG_NANOJIT */
839 #ifdef AVMPLUS_JITMAX
840 else if (!VMPI_strcmp(arg, "-jitmax") && i+1 < argc ) {
841 extern int jitmin;
842 extern int jitmax;
844 char* val = argv[++i];
845 char* dash = VMPI_strchr(val,'-');
846 if (dash) {
847 if (dash==&val[0])
848 jitmax = VMPI_atoi(&val[1]); // -n form
849 else {
850 int32_t hl = VMPI_strlen(dash);
851 dash[0] = '\0'; // hammer argv ;) - go boom?
852 jitmin = VMPI_atoi(val);
853 if (hl>1)
854 jitmax = VMPI_atoi(&dash[1]);
856 } else {
857 jitmax = VMPI_atoi(val);
860 #endif /* AVMPLUS_JITMAX */
861 else if (!VMPI_strcmp(arg, "-memstats")) {
862 MMgc::GCHeap::GetGCHeap()->Config().gcstats = true;
863 MMgc::GCHeap::GetGCHeap()->Config().autoGCStats = true;
865 else if (!VMPI_strcmp(arg, "-memstats-verbose")) {
866 MMgc::GCHeap::GetGCHeap()->Config().gcstats = true;
867 MMgc::GCHeap::GetGCHeap()->Config().autoGCStats = true;
868 MMgc::GCHeap::GetGCHeap()->Config().verbose = true;
870 else if (!VMPI_strcmp(arg, "-memlimit") && i+1 < argc ) {
871 MMgc::GCHeap::GetGCHeap()->Config().heapLimit = VMPI_strtol(argv[++i], 0, 10);
873 #ifdef MMGC_POLICY_PROFILING
874 else if (!VMPI_strcmp(arg, "-gcbehavior")) {
875 MMgc::GCHeap::GetGCHeap()->Config().gcbehavior = 2;
877 else if (!VMPI_strcmp(arg, "-gcsummary")) {
878 MMgc::GCHeap::GetGCHeap()->Config().gcbehavior = 1;
880 #endif
881 else if (!VMPI_strcmp(arg, "-eagersweep")) {
882 MMgc::GCHeap::GetGCHeap()->Config().eagerSweeping = true;
884 else if (!VMPI_strcmp(arg, "-load") && i+1 < argc ) {
885 double load;
886 double limit;
887 int nchar;
888 const char* val = argv[++i];
889 const char* origval = val;
890 size_t k = 0;
891 // limit=0 is legal, it means unlimited
892 for (;;) {
893 if (k < MMgc::GCHeapConfig::kNumLoadFactors) {
894 if (VMPI_sscanf(val, "%lf,%lf%n", &load, &limit, &nchar) == 2 && load > 1.0 && limit >= 0.0) {
895 MMgc::GCHeap::GetGCHeap()->Config().gcLoad[k] = load;
896 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff[k] = limit;
897 k++;
898 val += nchar;
899 if (*val == 0) {
900 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff[k-1] = DBL_MAX;
901 break;
903 if (*val == ',') {
904 val++;
905 continue;
908 else if (VMPI_sscanf(val, "%lf%n", &load, &nchar) == 1 && val[nchar] == 0 && load > 1.0) {
909 MMgc::GCHeap::GetGCHeap()->Config().gcLoad[k] = load;
910 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff[k] = DBL_MAX;
911 break;
914 AvmLog("Bad value to -load: %s\n", origval);
915 usage();
918 else if (!VMPI_strcmp(arg, "-loadCeiling") && i+1 < argc) {
919 double ceiling;
920 int nchar;
921 const char* val = argv[++i];
922 if (VMPI_sscanf(val, "%lf%n", &ceiling, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val) && ceiling >= 1.0) {
923 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCeiling = ceiling;
925 else {
926 AvmLog("Bad value to -loadCeiling: %s\n", val);
927 usage();
930 else if (!VMPI_strcmp(arg, "-gcwork") && i+1 < argc ) {
931 double work;
932 int nchar;
933 const char* val = argv[++i];
934 if (VMPI_sscanf(val, "%lf%n", &work, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val) && work > 0.0 && work <= 1.0) {
935 MMgc::GCHeap::GetGCHeap()->Config().gcEfficiency = work;
937 else {
938 AvmLog("Bad value to -gcwork: %s\n", val);
939 usage();
942 else if (!VMPI_strcmp(arg, "-stack")) {
943 unsigned stack;
944 int nchar;
945 const char* val = argv[++i];
946 if (VMPI_sscanf(val, "%u%n", &stack, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val) && stack > avmshell::kStackMargin) {
947 settings.stackSize = uint32_t(stack);
949 else
951 AvmLog("Bad argument to -stack\n");
952 usage();
955 else if (!VMPI_strcmp(arg, "-log")) {
956 settings.do_log = true;
958 #ifdef VMCFG_EVAL
959 else if (!VMPI_strcmp(arg, "-repl")) {
960 settings.do_repl = true;
962 #endif /* VMCFG_EVAL */
963 #ifdef VMCFG_WORKERTHREADS
964 else if (!VMPI_strcmp(arg, "-workers") && i+1 < argc ) {
965 const char *val = argv[++i];
966 int nchar;
967 if (val == NULL)
968 val = "";
969 if (VMPI_sscanf(val, "%d,%d,%d%n", &settings.numworkers, &settings.numthreads, &settings.repeats, &nchar) != 3)
970 if (VMPI_sscanf(val, "%d,%d%n", &settings.numworkers, &settings.numthreads, &nchar) != 2) {
971 AvmLog("Bad value to -workers: %s\n", val);
972 usage();
974 if (settings.numthreads < 1 ||
975 settings.numworkers < settings.numthreads ||
976 settings.repeats < 1 ||
977 size_t(nchar) != VMPI_strlen(val)) {
978 AvmLog("Bad value to -workers: %s\n", val);
979 usage();
982 #endif // VMCFG_WORKERTHREADS
983 #ifdef AVMPLUS_WIN32
984 else if (!VMPI_strcmp(arg, "-error")) {
985 show_error = true;
986 #ifndef UNDER_CE
987 SetErrorMode(0); // set to default
988 #endif
990 #endif // AVMPLUS_WIN32
991 #ifdef AVMPLUS_WITH_JNI
992 else if (!VMPI_strcmp(arg, "-jargs")) {
993 // all the following args until the semi colon is for java.
994 //@todo fix up this hard limit
995 bool first = true;
996 Java::startup_options = new char[256];
997 VMPI_memset(Java::startup_options, 0, 256);
999 for(i++; i<argc; i++)
1001 if (*argv[i] == ';')
1002 break;
1003 if (!first) VMPI_strcat(Java::startup_options, " ");
1004 VMPI_strcat(Java::startup_options, argv[i]);
1005 first = false;
1007 AvmAssert(VMPI_strlen(Java::startup_options) < 256);
1009 #endif /* AVMPLUS_WITH_JNI */
1010 #ifdef DEBUGGER
1011 else if (!VMPI_strcmp(arg, "-d")) {
1012 settings.enter_debugger_on_launch = true;
1014 #endif /* DEBUGGER */
1015 else if (!VMPI_strcmp(arg, "-api") && i+1 < argc) {
1016 bool badFlag;
1017 settings.api = ApiUtils::parseApiVersion(argv[i+1], badFlag);
1018 if (badFlag)
1020 AvmLog("Unknown api version'%s'\n", argv[i+1]);
1021 usage();
1023 i++;
1025 else if (VMPI_strcmp(arg, "-swfversion") == 0 && i+1 < argc) {
1026 int j = BugCompatibility::VersionCount;
1027 unsigned swfVersion;
1028 int nchar;
1029 const char* val = argv[++i];
1030 if (VMPI_sscanf(val, "%u%n", &swfVersion, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val))
1032 for (j = 0; j < BugCompatibility::VersionCount; ++j)
1034 if (BugCompatibility::kNames[j] == swfVersion)
1036 settings.swfVersion = (BugCompatibility::Version)j;
1037 break;
1041 if (j == BugCompatibility::VersionCount) {
1042 AvmLog("Unrecognized -swfversion version %s\n", val);
1043 usage();
1046 else {
1047 // Unrecognized command line option
1048 AvmLog("Unrecognized option %s\n", arg);
1049 usage();
1052 else {
1053 if (settings.filenames == NULL)
1054 settings.filenames = &argv[i];
1058 if (settings.filenames == NULL)
1059 settings.filenames = &argv[argc];
1061 if (settings.numfiles == -1)
1062 settings.numfiles = int(&argv[argc] - settings.filenames);
1064 if (settings.arguments == NULL) {
1065 settings.arguments = &argv[argc];
1066 settings.numargs = 0;
1069 AvmAssert(settings.filenames != NULL && settings.numfiles != -1);
1070 AvmAssert(settings.arguments != NULL && settings.numargs != -1);
1072 if (print_version)
1074 AvmLog("shell " AVMPLUS_VERSION_USER " " AVMPLUS_BIN_TYPE );
1075 if (RUNNING_ON_VALGRIND)
1076 AvmLog("-valgrind");
1077 AvmLog(" build " AVMPLUS_BUILD_CODE "\n");
1078 AvmLog("features %s\n", avmfeatures);
1079 Platform::GetInstance()->exit(1);
1082 // Vetting the options
1084 #ifdef AVMSHELL_PROJECTOR_SUPPORT
1085 if (settings.programFilename != NULL && ShellCore::isValidProjectorFile(settings.programFilename)) {
1086 if (settings.do_selftest || settings.do_repl || settings.numfiles > 0) {
1087 AvmLog("Projector files can't be used with -repl, -Dselftest, or program file arguments.\n");
1088 usage();
1090 if (settings.numthreads > 1 || settings.numworkers > 1) {
1091 AvmLog("A projector requires exactly one worker on one thread.\n");
1092 usage();
1094 settings.do_projector = 1;
1095 return;
1097 #endif
1099 #ifndef VMCFG_AOT
1100 if (settings.numfiles == 0) {
1101 // no files, so we need something more
1102 if (!settings.do_selftest && !settings.do_repl) {
1103 AvmLog("You must provide input files, -repl, or -Dselftest, or the executable must be a projector file.\n");
1104 usage();
1107 #endif
1109 #ifdef VMCFG_EVAL
1110 if (settings.do_repl)
1112 if (settings.numthreads > 1 || settings.numworkers > 1) {
1113 AvmLog("The REPL requires exactly one worker on one thread.\n");
1114 usage();
1117 #endif
1119 #ifdef VMCFG_SELFTEST
1120 if (settings.do_selftest)
1122 // Presumably we'd want to use the selftest harness to test multiple workers eventually.
1123 if (settings.numthreads > 1 || settings.numworkers > 1 || settings.numfiles > 0) {
1124 AvmLog("The selftest harness requires exactly one worker on one thread, and no input files.\n");
1125 usage();
1128 #endif
1131 // Does not return
1133 /*static*/
1134 void Shell::usage()
1136 AvmLog("avmplus shell " AVMPLUS_VERSION_USER " " AVMPLUS_BIN_TYPE " build " AVMPLUS_BUILD_CODE "\n\n");
1137 AvmLog("usage: avmplus\n");
1138 #ifdef DEBUGGER
1139 AvmLog(" [-d] enter debugger on start\n");
1140 #endif
1141 AvmLog(" [-memstats] generate statistics on memory usage\n");
1142 AvmLog(" [-memstats-verbose]\n"
1143 " generate more statistics on memory usage\n");
1144 AvmLog(" [-memlimit d] limit the heap size to d pages\n");
1145 AvmLog(" [-eagersweep] sweep the heap synchronously at the end of GC;\n"
1146 " improves usage statistics.\n");
1147 #ifdef MMGC_POLICY_PROFILING
1148 AvmLog(" [-gcbehavior] summarize GC behavior and policy, after every gc\n");
1149 AvmLog(" [-gcsummary] summarize GC behavior and policy, at end only\n");
1150 #endif
1151 AvmLog(" [-load L,B, ...\n"
1152 " GC load factor L up to a post-GC heap size of B megabytes.\n"
1153 " Up to %d pairs can be accommodated, the limit for the last pair\n"
1154 " will be ignored and can be omitted\n", int(MMgc::GCHeapConfig::kNumLoadFactors));
1155 AvmLog(" [-loadCeiling X] GC load multiplier ceiling (default 1.0)\n");
1156 AvmLog(" [-gcwork G] Max fraction of time (default 0.25) we're willing to spend in GC\n");
1157 AvmLog(" [-stack N] Stack size in bytes (will be honored approximately).\n"
1158 " Be aware of the stack margin: %u\n", avmshell::kStackMargin);
1159 AvmLog(" [-cache_bindings N] size of bindings cache (0 = unlimited)\n");
1160 AvmLog(" [-cache_metadata N] size of metadata cache (0 = unlimited)\n");
1161 AvmLog(" [-cache_methods N] size of method cache (0 = unlimited)\n");
1162 AvmLog(" [-Dgreedy] collect before every allocation\n");
1163 AvmLog(" [-Dnogc] don't collect\n");
1164 #ifdef VMCFG_SELECTABLE_EXACT_TRACING
1165 AvmLog(" [-Dnoexactgc] disable exact tracing\n");
1166 #endif
1167 AvmLog(" [-Dnoincgc] don't use incremental collection\n");
1168 AvmLog(" [-Dnodebugger] do not initialize the debugger (in DEBUGGER builds)\n");
1169 AvmLog(" [-Dgcthreshold N] lower bound on allocation budget, in blocks, between collection completions\n");
1170 AvmLog(" [-Dnofixedcheck] don't check FixedMalloc deallocations for correctness (sometimes expensive)\n");
1171 #ifdef DEBUGGER
1172 AvmLog(" [-Dastrace N] display AS execution information, where N is [1..4]\n");
1173 AvmLog(" [-Dlanguage l] localize runtime errors, languages are:\n");
1174 AvmLog(" en,de,es,fr,it,ja,ko,zh-CN,zh-TW\n");
1175 #endif
1176 #ifdef AVMPLUS_VERBOSE
1177 AvmLog(" [-Dverbose[=[parse,verify,interp,traits,builtins,minaddr,memstats,sweep,occupancy,execpolicy"
1178 # ifdef VMCFG_NANOJIT
1179 ",jit,opt,regs,raw,bytes"
1180 # endif
1181 "]]\n");
1182 AvmLog(" With no options, enables extreme! output mode. Otherwise the\n");
1183 AvmLog(" options are mostly self-descriptive except for the following: \n");
1184 AvmLog(" builtins - includes output from builtin methods\n");
1185 AvmLog(" memstats - generate statistics on memory usage \n");
1186 AvmLog(" sweep - [memstats] include detailed sweep information \n");
1187 AvmLog(" occupancy - [memstats] include occupancy bit graph \n");
1188 AvmLog(" execpolicy - shows which execution method (interpretation, compilation) was chosen and why \n");
1189 # ifdef VMCFG_NANOJIT
1190 AvmLog(" jit - output LIR as it is generated, and final assembly code\n");
1191 AvmLog(" opt - [jit] show details about each optimization pass\n");
1192 AvmLog(" regs - [jit] show register allocation state after each assembly instruction\n");
1193 AvmLog(" raw - [jit] assembly code is displayed in raw (i.e unbuffered bottom-up) fashion. \n");
1194 AvmLog(" bytes - [jit] display the byte values of the assembly code. \n");
1195 # endif
1197 AvmLog(" Note that ordering matters for options with dependencies. Dependencies \n");
1198 AvmLog(" are contained in [ ] For example, 'sweep' requires 'memstats' \n");
1199 #endif
1200 #ifdef VMCFG_NANOJIT
1201 AvmLog(" [-Dinterp] do not generate machine code, interpret instead\n");
1202 AvmLog(" [-Ojit] use jit always, never interp (except when the jit fails)\n");
1203 AvmLog(" [-Djitordie] use jit always, and abort when the jit fails\n");
1204 AvmLog(" [-Dnocse] disable CSE optimization \n");
1205 AvmLog(" [-jitharden] enable jit hardening techniques\n");
1206 #ifdef AVMPLUS_IA32
1207 AvmLog(" [-Dnosse] use FPU stack instead of SSE2 instructions\n");
1208 AvmLog(" [-Dfixedesp] pre-decrement stack for all needed call usage upon method entry\n");
1209 #endif
1210 #ifdef AVMPLUS_ARM
1211 AvmLog(" [-Darm_arch N] nanojit assumes ARMvN architecture (default=5)\n");
1212 AvmLog(" [-Darm_vfp] nanojit uses VFP rather than SoftFloat\n");
1213 #endif
1214 #endif
1215 #ifdef AVMPLUS_JITMAX
1216 AvmLog(" [-jitmax N-M] jit the Nth to Mth methods only; N- and -M are also valid.\n");
1217 #endif
1218 #ifdef VMCFG_VERIFYALL
1219 AvmLog(" [-Dverifyall] verify greedily instead of lazily\n");
1220 AvmLog(" [-Dverifyonly] verify greedily and don't execute anything\n");
1221 #endif
1222 #ifdef VMCFG_SELFTEST
1223 AvmLog(" [-Dselftest[=component,category,test]]");
1224 AvmLog(" run selftests\n");
1225 #endif
1226 AvmLog(" [-Dtimeout] enforce maximum 15 seconds execution\n");
1227 AvmLog(" [-Dversion] print the version and the list of compiled-in features and then exit\n");
1228 #ifdef AVMPLUS_WIN32
1229 AvmLog(" [-error] crash opens debug dialog, instead of dumping\n");
1230 #endif
1231 #ifdef VMCFG_EVAL
1232 AvmLog(" [-repl] read-eval-print mode\n");
1233 #endif
1234 #ifdef VMCFG_WORKERTHREADS
1235 AvmLog(" [-workers W,T[,R]]\n");
1236 AvmLog(" Spawn W worker VMs on T threads where W >= T and T >= 1.\n");
1237 AvmLog(" The files provided are handed off to the workers in the order given,\n");
1238 AvmLog(" as workers become available, and these workers are scheduled onto threads\n");
1239 AvmLog(" in a deterministic order that prevents them from having affinity to a thread.\n");
1240 AvmLog(" To test this functionality you want many more files than workers and many more\n");
1241 AvmLog(" workers than threads, and at least two threads.\n");
1242 AvmLog(" If R > 0 is provided then it is the number of times the list of files is repeated.\n");
1243 #endif
1244 AvmLog(" [-swfversion version]\n");
1245 AvmLog(" Run with a given bug-compatibility version in use by default.\n");
1246 AvmLog(" (This can be overridden on a per-ABC basis by embedded metadata.)\n");
1247 AvmLog(" Legal versions are:\n");
1248 for (int j = 0; j < BugCompatibility::VersionCount; ++j)
1250 AvmLog(" %d\n",BugCompatibility::kNames[j]);
1252 AvmLog(" [-log]\n");
1253 AvmLog(" [-api N] execute ABCs as version N (see api-versions.h)\n");
1254 AvmLog(" [-jargs ... ;] args passed to Java runtime\n");
1255 AvmLog(" [filename.{abc,swf} ...\n");
1256 AvmLog(" [-- application argument ...]\n");
1257 Platform::GetInstance()->exit(1);