Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / shell / avmshell.cpp
blob23de6e16945e86984a122999b29c266bfad940a8
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 "extensions-tracers.hh"
47 #include "avmshell-tracers.hh"
49 namespace avmshell
51 #ifdef AVMPLUS_WIN32
52 extern bool show_error; // in avmshellWin.cpp
53 #endif
55 ShellSettings::ShellSettings()
56 : ShellCoreSettings()
57 , programFilename(NULL)
58 , filenames(NULL)
59 , numfiles(-1)
60 , do_selftest(false)
61 , do_repl(false)
62 , do_log(false)
63 , do_projector(false)
64 , numthreads(1)
65 , numworkers(1)
66 , repeats(1)
67 , stackSize(0)
71 ShellCoreImpl::ShellCoreImpl(MMgc::GC* gc, ShellSettings& settings, bool mainthread)
72 : ShellCore(gc, settings.apiVersionSeries)
73 , settings(settings)
74 , mainthread(mainthread)
78 /* virtual */
79 void ShellCoreImpl::setStackLimit()
81 uintptr_t minstack;
82 if (settings.stackSize != 0)
84 // Here we really depend on being called fairly high up on
85 // the thread's stack, because we don't know where the highest
86 // stack address is.
87 minstack = uintptr_t(&minstack) - settings.stackSize + avmshell::kStackMargin;
89 else
91 #ifdef VMCFG_WORKERTHREADS
92 if (mainthread)
93 minstack = Platform::GetInstance()->getMainThreadStackLimit();
94 else {
95 // Here we really depend on being called fairly high up on
96 // the thread's stack, because we don't know where the highest
97 // stack address is.
98 size_t stackheight;
99 pthread_attr_t attr;
100 pthread_attr_init(&attr);
101 pthread_attr_getstacksize(&attr, &stackheight);
102 pthread_attr_destroy(&attr);
103 minstack = uintptr_t(&stackheight) - stackheight + avmshell::kStackMargin;
105 #else
106 minstack = Platform::GetInstance()->getMainThreadStackLimit();
107 #endif
110 // call the non-virtual setter on AvmCore
111 AvmCore::setStackLimit(minstack);
114 /* static */
115 int Shell::run(int argc, char *argv[])
117 MMgc::GCHeap::EnterLockInit();
118 MMgc::GCHeapConfig conf;
119 //conf.verbose = AvmCore::DEFAULT_VERBOSE_ON;
120 MMgc::GCHeap::Init(conf);
122 // Note that output from the command line parser (usage messages, error messages,
123 // and printed version number / feature list) will not go to the log file. We
124 // could fix this if it's a hardship.
127 MMGC_ENTER_RETURN(OUT_OF_MEMORY);
128 ShellSettings settings;
129 parseCommandLine(argc, argv, settings);
132 // code coverage/cheap test
133 MMGC_ENTER_SUSPEND;
136 if (settings.do_log)
137 initializeLogging(settings.numfiles > 0 ? settings.filenames[0] : "AVMLOG");
139 #ifdef VMCFG_WORKERTHREADS
140 if (settings.numworkers == 1 && settings.numthreads == 1 && settings.repeats == 1)
141 singleWorker(settings);
142 else
143 multiWorker(settings);
144 #else
145 singleWorker(settings);
146 #endif
149 #ifdef AVMPLUS_WITH_JNI
150 // This surely does not belong here?
151 if (Java::startup_options) delete Java::startup_options;
152 #endif /* AVMPLUS_WITH_JNI */
154 MMgc::GCHeap::Destroy();
155 MMgc::GCHeap::EnterLockDestroy();
156 return 0;
159 // In the single worker case everything is run on the main thread. This
160 // is where we handler the repl, selftest, and projectors.
162 /* static */
163 void Shell::singleWorker(ShellSettings& settings)
165 MMgc::GCConfig gcconfig;
166 gcconfig.collectionThreshold = settings.gcthreshold;
167 gcconfig.exactTracing = settings.exactgc;
168 gcconfig.markstackAllowance = settings.markstackAllowance;
169 MMgc::GC *gc = mmfx_new( MMgc::GC(MMgc::GCHeap::GetGCHeap(), settings.gcMode(), &gcconfig) );
171 MMGC_GCENTER(gc);
172 ShellCore* shell = new ShellCoreImpl(gc, settings, true);
173 Shell::singleWorkerHelper(shell, settings);
174 delete shell;
176 mmfx_delete( gc );
179 /* static */
180 void Shell::singleWorkerHelper(ShellCore* shell, ShellSettings& settings)
182 if (!shell->setup(settings))
183 Platform::GetInstance()->exit(1);
185 #ifdef VMCFG_SELFTEST
186 if (settings.do_selftest) {
187 shell->executeSelftest(settings);
188 return;
190 #endif
192 #ifdef AVMSHELL_PROJECTOR_SUPPORT
193 if (settings.do_projector) {
194 AvmAssert(settings.programFilename != NULL);
195 int exitCode = shell->executeProjector(settings.programFilename);
196 if (exitCode != 0)
197 Platform::GetInstance()->exit(exitCode);
199 #endif
201 #ifdef VMCFG_AOT
202 int exitCode = shell->evaluateFile(settings, NULL);
203 if (exitCode != 0)
204 Platform::GetInstance()->exit(exitCode);
205 return;
206 #endif
208 // For -testswf we must have exactly one file
209 if (settings.do_testSWFHasAS3 && settings.numfiles != 1)
210 Platform::GetInstance()->exit(1);
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;
418 gcconfig.markstackAllowance = settings.markstackAllowance;
420 // Going multi-threaded.
422 // Create and start threads. They add themselves to the free list.
423 for ( int i=0 ; i < numthreads ; i++ ) {
424 threads[i] = new ThreadNode(state, i);
425 pthread_create(&threads[i]->thread, NULL, slaveThread, threads[i]);
428 // Create collectors and cores.
429 // Extra credit: perform setup in parallel on the threads.
430 for ( int i=0 ; i < numcores ; i++ ) {
431 MMgc::GC* gc = new MMgc::GC(MMgc::GCHeap::GetGCHeap(), settings.gcMode(), &gcconfig);
432 MMGC_GCENTER(gc);
433 cores[i] = new CoreNode(new ShellCoreImpl(gc, settings, false), i);
434 if (!cores[i]->core->setup(settings))
435 Platform::GetInstance()->exit(1);
438 // Add the cores to the free list.
439 for ( int i=numcores-1 ; i >= 0 ; i-- ) {
440 cores[i]->next = state.free_cores;
441 state.free_cores = cores[i];
443 state.free_cores_last = cores[numcores-1];
445 // No locks are held by the master at this point
446 masterThread(state);
447 // No locks are held by the master at this point
449 // Some threads may still be computing, so just wait for them
450 pthread_mutex_lock(&state.m);
451 while (state.num_free_threads < numthreads)
452 pthread_cond_wait(&state.c, &state.m);
453 pthread_mutex_unlock(&state.m);
455 // Shutdown: feed NULL to all threads to make them exit.
456 for ( int i=0 ; i < numthreads ; i++ )
457 threads[i]->startWork(NULL,NULL);
459 // Wait for all threads to exit.
460 for ( int i=0 ; i < numthreads ; i++ ) {
461 pthread_join(threads[i]->thread, NULL);
462 LOGGING( AvmLog("T%d: joined the main thread\n", i); )
465 // Single threaded again.
467 for ( int i=0 ; i < numthreads ; i++ )
468 delete threads[i];
470 for ( int i=0 ; i < numcores ; i++ )
471 delete cores[i];
473 delete [] threads;
474 delete [] cores;
477 static void masterThread(MultiworkerState& state)
479 const int numfiles(state.settings.numfiles);
480 const int repeats(state.settings.repeats);
481 char** const filenames(state.settings.filenames);
483 pthread_mutex_lock(&state.m);
484 int r=0;
485 for (;;) {
486 int nextfile = 0;
487 for (;;) {
488 ThreadNode* threadnode;
489 CoreNode* corenode;
490 while (state.getThreadAndCore(&threadnode, &corenode)) {
491 LOGGING( AvmLog("Scheduling %s on T%d with C%d\n", filenames[nextfile], threadnode->id, corenode->id); )
492 threadnode->startWork(corenode, filenames[nextfile]);
493 nextfile++;
494 if (nextfile == numfiles) {
495 r++;
496 if (r == repeats)
497 goto finish;
498 nextfile = 0;
502 LOGGING( AvmLog("Waiting for available threads.\n"); )
503 pthread_cond_wait(&state.c, &state.m);
506 finish:
507 pthread_mutex_unlock(&state.m);
510 static void* slaveThread(void *arg)
512 MMGC_ENTER_RETURN(NULL);
514 ThreadNode* self = (ThreadNode*)arg;
515 MultiworkerState& state = self->state;
517 for (;;) {
518 // Signal that we're ready for more work: add self to the list of free threads
520 state.freeThread(self);
522 // Obtain more work. We have to hold self->m here but the master won't touch corenode and
523 // filename until we register for more work, so they don't have to be copied out of
524 // the thread structure.
526 pthread_mutex_lock(&self->m);
527 // Don't wait when pendingWork == true,
528 // slave might have been already signalled but it didn't notice because it wasn't waiting yet.
529 while (self->pendingWork == false)
530 pthread_cond_wait(&self->c, &self->m);
531 pthread_mutex_unlock(&self->m);
533 if (self->corenode == NULL) {
534 LOGGING( AvmLog("T%d: Exiting\n", self->id); )
535 pthread_exit(NULL);
538 // Perform work
539 LOGGING( AvmLog("T%d: Work starting\n", self->id); )
541 MMGC_GCENTER(self->corenode->core->GetGC());
542 #ifdef _DEBUG
543 self->corenode->core->codeContextThread = VMPI_currentThread();
544 #endif
545 self->corenode->core->evaluateFile(state.settings, self->filename); // Ignore the exit code for now
547 LOGGING( AvmLog("T%d: Work completed\n", self->id); )
549 pthread_mutex_lock(&self->m);
550 self->pendingWork = false;
551 pthread_mutex_unlock(&self->m);
555 return (void*) NULL;
558 #endif // VMCFG_WORKERTHREADS
560 #ifdef VMCFG_EVAL
562 /* static */
563 void Shell::repl(ShellCore* shellCore)
565 const int kMaxCommandLine = 1024;
566 char commandLine[kMaxCommandLine];
567 String* input;
569 AvmLog("avmplus interactive shell\n"
570 "Type '?' for help\n\n");
572 for (;;)
574 bool record_time = false;
575 AvmLog("> ");
577 if(Platform::GetInstance()->getUserInput(commandLine, kMaxCommandLine) == NULL)
578 return;
580 commandLine[kMaxCommandLine-1] = 0;
581 if (VMPI_strncmp(commandLine, "?", 1) == 0) {
582 AvmLog("Text entered at the prompt is compiled and evaluated unless\n"
583 "it is one of these commands:\n\n"
584 " ? print help\n"
585 " .input collect lines until a line that reads '.end',\n"
586 " then eval the collected lines\n"
587 " .load file load the file (source or compiled)\n"
588 " .quit leave the repl\n"
589 " .time expr evaluate expr and report the time it took.\n\n");
590 continue;
593 if (VMPI_strncmp(commandLine, ".load", 5) == 0) {
594 const char* s = commandLine+5;
595 while (*s == ' ' || *s == '\t')
596 s++;
597 // wrong, handles only source code
598 //readFileForEval(NULL, newStringLatin1(s));
599 // FIXME: implement .load
600 // Small amount of generalization of the code currently in the main loop should
601 // take care of it.
602 AvmLog("The .load command is not implemented\n");
603 continue;
606 if (VMPI_strncmp(commandLine, ".input", 6) == 0) {
607 input = shellCore->newStringLatin1("");
608 for (;;) {
609 if(Platform::GetInstance()->getUserInput(commandLine, kMaxCommandLine) == NULL)
610 return;
611 commandLine[kMaxCommandLine-1] = 0;
612 if (VMPI_strncmp(commandLine, ".end", 4) == 0)
613 break;
614 input->appendLatin1(commandLine);
616 goto compute;
619 if (VMPI_strncmp(commandLine, ".quit", 5) == 0) {
620 return;
623 if (VMPI_strncmp(commandLine, ".time", 5) == 0) {
624 record_time = true;
625 input = shellCore->newStringLatin1(commandLine+5);
626 goto compute;
629 input = shellCore->newStringLatin1(commandLine);
631 compute:
632 shellCore->evaluateString(input, record_time);
636 #endif // VMCFG_EVAL
638 // open logfile based on a filename
639 /* static */
640 void Shell::initializeLogging(const char* basename)
642 const char* lastDot = VMPI_strrchr(basename, '.');
643 if(lastDot)
645 //filename could contain '/' or '\' as their separator, look for both
646 const char* lastPathSep1 = VMPI_strrchr(basename, '/');
647 const char* lastPathSep2 = VMPI_strrchr(basename, '\\');
648 if(lastPathSep1 < lastPathSep2) //determine the right-most
649 lastPathSep1 = lastPathSep2;
651 //if dot is before the separator, the filename does not have an extension
652 if(lastDot < lastPathSep1)
653 lastDot = NULL;
656 //if no extension then take the entire filename or
657 size_t logFilenameLen = (lastDot == NULL) ? VMPI_strlen(basename) : (lastDot - basename);
659 char* logFilename = new char[logFilenameLen + 5]; // 5 bytes for ".log" + null char
660 VMPI_strncpy(logFilename,basename,logFilenameLen);
661 VMPI_strcpy(logFilename+logFilenameLen,".log");
663 Platform::GetInstance()->initializeLogging(logFilename);
665 delete [] logFilename;
668 /* static */
669 void Shell::parseCommandLine(int argc, char* argv[], ShellSettings& settings)
671 bool print_version = false;
673 // options filenames -- args
675 settings.programFilename = argv[0]; // How portable / reliable is this?
676 for (int i=1; i < argc ; i++) {
677 const char * const arg = argv[i];
679 if (arg[0] == '-')
681 if (arg[1] == '-' && arg[2] == 0) {
682 if (settings.filenames == NULL)
683 settings.filenames = &argv[i];
684 settings.numfiles = int(&argv[i] - settings.filenames);
685 i++;
686 settings.arguments = &argv[i];
687 settings.numargs = argc - i;
688 break;
691 if (arg[1] == 'D') {
692 if (!VMPI_strcmp(arg+2, "timeout")) {
693 settings.interrupts = true;
695 else if (!VMPI_strcmp(arg+2, "version")) {
696 print_version = true;
698 else if (!VMPI_strcmp(arg+2, "nodebugger")) {
699 // allow this option even in non-DEBUGGER builds to make test scripts simpler
700 settings.nodebugger = true;
702 #if defined(AVMPLUS_IA32) && defined(VMCFG_NANOJIT)
703 else if (!VMPI_strcmp(arg+2, "nosse")) {
704 settings.njconfig.i386_sse2 = false;
706 else if (!VMPI_strcmp(arg+2, "fixedesp")) {
707 settings.njconfig.i386_fixed_esp = true;
709 #endif /* AVMPLUS_IA32 */
710 #if defined(AVMPLUS_ARM) && defined(VMCFG_NANOJIT)
711 else if (!VMPI_strcmp(arg+2, "arm_arch")) {
712 settings.njconfig.arm_arch = (uint8_t)VMPI_strtol(argv[++i], 0, 10);
714 else if (!VMPI_strcmp(arg+2, "arm_vfp")) {
715 settings.njconfig.arm_vfp = true;
717 #endif /* AVMPLUS_IA32 */
718 #ifdef VMCFG_VERIFYALL
719 else if (!VMPI_strcmp(arg+2, "verifyall")) {
720 settings.verifyall = true;
722 else if (!VMPI_strcmp(arg+2, "verifyonly")) {
723 settings.verifyall = true;
724 settings.verifyonly = true;
726 #endif /* VMCFG_VERIFYALL */
727 else if (!VMPI_strcmp(arg+2, "greedy")) {
728 settings.greedy = true;
730 else if (!VMPI_strcmp(arg+2, "nogc")) {
731 settings.nogc = true;
733 else if (!VMPI_strcmp(arg+2, "noincgc")) {
734 settings.incremental = false;
736 #ifdef VMCFG_SELECTABLE_EXACT_TRACING
737 else if (!VMPI_strcmp(arg+2, "noexactgc")) {
738 settings.exactgc = false;
740 #endif
741 else if (!VMPI_strcmp(arg+2, "nofixedcheck")) {
742 settings.fixedcheck = false;
744 else if (!VMPI_strcmp(arg+2, "gcthreshold") && i+1 < argc) {
745 settings.gcthreshold = VMPI_strtol(argv[++i], 0, 10);
747 #if defined(DEBUGGER) && !defined(VMCFG_DEBUGGER_STUB)
748 else if (!VMPI_strcmp(arg+2, "astrace") && i+1 < argc ) {
749 settings.astrace_console = VMPI_strtol(argv[++i], 0, 10);
751 else if (!VMPI_strcmp(arg+2, "language")) {
752 settings.langID=-1;
753 for (int j=0;j<kLanguages;j++) {
754 if (!VMPI_strcmp(argv[i+1],languageNames[j].str)) {
755 settings.langID=j;
756 break;
759 if (settings.langID==-1) {
760 settings.langID = VMPI_atoi(argv[i+1]);
762 i++;
764 #endif /* defined(DEBUGGER) && !defined(VMCFG_DEBUGGER_STUB) */
765 #ifdef VMCFG_SELFTEST
766 else if (!VMPI_strncmp(arg+2, "selftest", 8)) {
767 settings.do_selftest = true;
768 if (arg[10] == '=') {
769 VMPI_strncpy(settings.st_mem, arg+11, sizeof(settings.st_mem));
770 settings.st_mem[sizeof(settings.st_mem)-1] = 0;
771 char *p = settings.st_mem;
772 settings.st_component = p;
773 while (*p && *p != ',')
774 p++;
775 if (*p == ',')
776 *p++ = 0;
777 settings.st_category = p;
778 while (*p && *p != ',')
779 p++;
780 if (*p == ',')
781 *p++ = 0;
782 settings.st_name = p;
783 if (*settings.st_component == 0)
784 settings.st_component = NULL;
785 if (*settings.st_category == 0)
786 settings.st_category = NULL;
787 if (*settings.st_name == 0)
788 settings.st_name = NULL;
791 #endif /* VMCFG_SELFTEST */
792 #ifdef AVMPLUS_VERBOSE
793 else if (!VMPI_strncmp(arg+2, "verbose", 7)) {
794 settings.do_verbose = AvmCore::DEFAULT_VERBOSE_ON; // all 'on' by default
795 if (arg[9] == '=') {
796 char* badFlag;
797 settings.do_verbose = AvmCore::parseVerboseFlags(&arg[10], badFlag);
798 if (badFlag) {
799 AvmLog("Unknown verbose flag while parsing '%s'\n", badFlag);
800 usage();
804 #endif /* AVMPLUS_VERBOSE */
805 #ifdef VMCFG_NANOJIT
806 else if (!VMPI_strcmp(arg+2, "nocse")) {
807 settings.njconfig.cseopt = false;
809 else if (!VMPI_strcmp(arg+2, "jitordie")) {
810 settings.runmode = RM_jit_all;
811 settings.jitordie = true;
813 #endif /* VMCFG_NANOJIT */
814 else if (!VMPI_strcmp(arg+2, "interp")) {
815 settings.runmode = RM_interp_all;
817 else {
818 AvmLog("Unrecognized option %s\n", arg);
819 usage();
822 else if (!VMPI_strcmp(arg, "-cache_bindings") && i+1 < argc) {
823 settings.cacheSizes.bindings = (uint16_t)VMPI_strtol(argv[++i], 0, 10);
825 else if (!VMPI_strcmp(arg, "-cache_metadata") && i+1 < argc) {
826 settings.cacheSizes.metadata = (uint16_t)VMPI_strtol(argv[++i], 0, 10);
828 else if (!VMPI_strcmp(arg, "-cache_methods") && i+1 < argc ) {
829 settings.cacheSizes.methods = (uint16_t)VMPI_strtol(argv[++i], 0, 10);
831 else if (!VMPI_strcmp(arg, "-swfHasAS3")) {
832 settings.do_testSWFHasAS3 = true;
834 #ifdef VMCFG_NANOJIT
835 else if (!VMPI_strcmp(arg, "-jitharden")) {
836 settings.njconfig.harden_nop_insertion = true;
837 settings.njconfig.harden_function_alignment = true;
839 else if (!VMPI_strcmp(arg, "-Ojit")) {
840 settings.runmode = RM_jit_all;
842 #endif /* VMCFG_NANOJIT */
843 #ifdef AVMPLUS_JITMAX
844 else if (!VMPI_strcmp(arg, "-jitmax") && i+1 < argc ) {
845 extern int jitmin;
846 extern int jitmax;
848 char* val = argv[++i];
849 char* dash = VMPI_strchr(val,'-');
850 if (dash) {
851 if (dash==&val[0])
852 jitmax = VMPI_atoi(&val[1]); // -n form
853 else {
854 int32_t hl = VMPI_strlen(dash);
855 dash[0] = '\0'; // hammer argv ;) - go boom?
856 jitmin = VMPI_atoi(val);
857 if (hl>1)
858 jitmax = VMPI_atoi(&dash[1]);
860 } else {
861 jitmax = VMPI_atoi(val);
864 #endif /* AVMPLUS_JITMAX */
865 else if (!VMPI_strcmp(arg, "-memstats")) {
866 MMgc::GCHeap::GetGCHeap()->Config().gcstats = true;
867 MMgc::GCHeap::GetGCHeap()->Config().autoGCStats = true;
869 else if (!VMPI_strcmp(arg, "-memstats-verbose")) {
870 MMgc::GCHeap::GetGCHeap()->Config().gcstats = true;
871 MMgc::GCHeap::GetGCHeap()->Config().autoGCStats = true;
872 MMgc::GCHeap::GetGCHeap()->Config().verbose = true;
874 else if (!VMPI_strcmp(arg, "-memlimit") && i+1 < argc ) {
875 MMgc::GCHeap::GetGCHeap()->Config().heapLimit = VMPI_strtol(argv[++i], 0, 10);
877 #ifdef MMGC_POLICY_PROFILING
878 else if (!VMPI_strcmp(arg, "-gcbehavior")) {
879 MMgc::GCHeap::GetGCHeap()->Config().gcbehavior = 2;
881 else if (!VMPI_strcmp(arg, "-gcsummary")) {
882 MMgc::GCHeap::GetGCHeap()->Config().gcbehavior = 1;
884 #endif
885 else if (!VMPI_strcmp(arg, "-eagersweep")) {
886 MMgc::GCHeap::GetGCHeap()->Config().eagerSweeping = true;
888 else if (!VMPI_strcmp(arg, "-load") && i+1 < argc ) {
889 double load;
890 double limit;
891 int nchar;
892 const char* val = argv[++i];
893 const char* origval = val;
894 size_t k = 0;
895 // limit=0 is legal, it means unlimited
896 for (;;) {
897 if (k < MMgc::GCHeapConfig::kNumLoadFactors) {
898 if (VMPI_sscanf(val, "%lf,%lf%n", &load, &limit, &nchar) == 2 && load > 1.0 && limit >= 0.0) {
899 MMgc::GCHeap::GetGCHeap()->Config().gcLoad[k] = load;
900 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff[k] = limit;
901 k++;
902 val += nchar;
903 if (*val == 0) {
904 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff[k-1] = DBL_MAX;
905 break;
907 if (*val == ',') {
908 val++;
909 continue;
912 else if (VMPI_sscanf(val, "%lf%n", &load, &nchar) == 1 && val[nchar] == 0 && load > 1.0) {
913 MMgc::GCHeap::GetGCHeap()->Config().gcLoad[k] = load;
914 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff[k] = DBL_MAX;
915 break;
918 AvmLog("Bad value to -load: %s\n", origval);
919 usage();
922 else if (!VMPI_strcmp(arg, "-loadCeiling") && i+1 < argc) {
923 double ceiling;
924 int nchar;
925 const char* val = argv[++i];
926 if (VMPI_sscanf(val, "%lf%n", &ceiling, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val) && ceiling >= 1.0) {
927 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCeiling = ceiling;
929 else {
930 AvmLog("Bad value to -loadCeiling: %s\n", val);
931 usage();
934 else if (!VMPI_strcmp(arg, "-gcwork") && i+1 < argc ) {
935 double work;
936 int nchar;
937 const char* val = argv[++i];
938 if (VMPI_sscanf(val, "%lf%n", &work, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val) && work > 0.0 && work <= 1.0) {
939 MMgc::GCHeap::GetGCHeap()->Config().gcEfficiency = work;
941 else {
942 AvmLog("Bad value to -gcwork: %s\n", val);
943 usage();
946 else if (!VMPI_strcmp(arg, "-stack")) {
947 unsigned stack;
948 int nchar;
949 const char* val = argv[++i];
950 if (VMPI_sscanf(val, "%u%n", &stack, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val) && stack > avmshell::kStackMargin) {
951 settings.stackSize = uint32_t(stack);
953 else
955 AvmLog("Bad argument to -stack\n");
956 usage();
959 #ifdef MMGC_MARKSTACK_ALLOWANCE
960 else if (!VMPI_strcmp(arg, "-gcstack")) {
961 int stack;
962 int nchar;
963 const char* val = argv[++i];
964 if (VMPI_sscanf(val, "%d%n", &stack, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val) && stack >= 0) {
965 settings.markstackAllowance = int32_t(stack);
967 else
969 AvmLog("Bad argument to -gcstack\n");
970 usage();
973 #endif
974 else if (!VMPI_strcmp(arg, "-log")) {
975 settings.do_log = true;
977 #ifdef VMCFG_EVAL
978 else if (!VMPI_strcmp(arg, "-repl")) {
979 settings.do_repl = true;
981 #endif /* VMCFG_EVAL */
982 #ifdef VMCFG_WORKERTHREADS
983 else if (!VMPI_strcmp(arg, "-workers") && i+1 < argc ) {
984 const char *val = argv[++i];
985 int nchar;
986 if (val == NULL)
987 val = "";
988 if (VMPI_sscanf(val, "%d,%d,%d%n", &settings.numworkers, &settings.numthreads, &settings.repeats, &nchar) != 3)
989 if (VMPI_sscanf(val, "%d,%d%n", &settings.numworkers, &settings.numthreads, &nchar) != 2) {
990 AvmLog("Bad value to -workers: %s\n", val);
991 usage();
993 if (settings.numthreads < 1 ||
994 settings.numworkers < settings.numthreads ||
995 settings.repeats < 1 ||
996 size_t(nchar) != VMPI_strlen(val)) {
997 AvmLog("Bad value to -workers: %s\n", val);
998 usage();
1001 #endif // VMCFG_WORKERTHREADS
1002 #ifdef AVMPLUS_WIN32
1003 else if (!VMPI_strcmp(arg, "-error")) {
1004 show_error = true;
1005 #ifndef UNDER_CE
1006 SetErrorMode(0); // set to default
1007 #endif
1009 #endif // AVMPLUS_WIN32
1010 #ifdef AVMPLUS_WITH_JNI
1011 else if (!VMPI_strcmp(arg, "-jargs")) {
1012 // all the following args until the semi colon is for java.
1013 //@todo fix up this hard limit
1014 bool first = true;
1015 Java::startup_options = new char[256];
1016 VMPI_memset(Java::startup_options, 0, 256);
1018 for(i++; i<argc; i++)
1020 if (*argv[i] == ';')
1021 break;
1022 if (!first) VMPI_strcat(Java::startup_options, " ");
1023 VMPI_strcat(Java::startup_options, argv[i]);
1024 first = false;
1026 AvmAssert(VMPI_strlen(Java::startup_options) < 256);
1028 #endif /* AVMPLUS_WITH_JNI */
1029 #ifdef DEBUGGER
1030 else if (!VMPI_strcmp(arg, "-d")) {
1031 settings.enter_debugger_on_launch = true;
1033 #endif /* DEBUGGER */
1034 else if (!VMPI_strcmp(arg, "-api") && i+1 < argc) {
1035 if (!AvmCore::parseApiVersion(argv[i+1], settings.apiVersion, settings.apiVersionSeries))
1037 AvmLog("Unknown api version'%s'\n", argv[i+1]);
1038 usage();
1040 i++;
1042 else if (VMPI_strcmp(arg, "-swfversion") == 0 && i+1 < argc) {
1043 int j = BugCompatibility::VersionCount;
1044 unsigned swfVersion;
1045 int nchar;
1046 const char* val = argv[++i];
1047 if (VMPI_sscanf(val, "%u%n", &swfVersion, &nchar) == 1 && size_t(nchar) == VMPI_strlen(val))
1049 for (j = 0; j < BugCompatibility::VersionCount; ++j)
1051 if (BugCompatibility::kNames[j] == swfVersion)
1053 settings.swfVersion = (BugCompatibility::Version)j;
1054 break;
1058 if (j == BugCompatibility::VersionCount) {
1059 AvmLog("Unrecognized -swfversion version %s\n", val);
1060 usage();
1063 else {
1064 // Unrecognized command line option
1065 AvmLog("Unrecognized option %s\n", arg);
1066 usage();
1069 else {
1070 if (settings.filenames == NULL)
1071 settings.filenames = &argv[i];
1075 if (settings.filenames == NULL)
1076 settings.filenames = &argv[argc];
1078 if (settings.numfiles == -1)
1079 settings.numfiles = int(&argv[argc] - settings.filenames);
1081 if (settings.arguments == NULL) {
1082 settings.arguments = &argv[argc];
1083 settings.numargs = 0;
1086 AvmAssert(settings.filenames != NULL && settings.numfiles != -1);
1087 AvmAssert(settings.arguments != NULL && settings.numargs != -1);
1089 if (print_version)
1091 AvmLog("shell " AVMPLUS_VERSION_USER " " AVMPLUS_BIN_TYPE );
1092 if (RUNNING_ON_VALGRIND)
1093 AvmLog("-valgrind");
1094 AvmLog(" build " AVMPLUS_BUILD_CODE "\n");
1095 #ifdef AVMPLUS_DESC_STRING
1096 if (VMPI_strcmp(AVMPLUS_DESC_STRING, ""))
1098 AvmLog("Description: " AVMPLUS_DESC_STRING "\n");
1100 #endif
1101 AvmLog("features %s\n", avmfeatures);
1102 Platform::GetInstance()->exit(1);
1105 // Vetting the options
1107 #ifdef AVMSHELL_PROJECTOR_SUPPORT
1108 if (settings.programFilename != NULL && ShellCore::isValidProjectorFile(settings.programFilename)) {
1109 if (settings.do_selftest || settings.do_repl || settings.numfiles > 0) {
1110 AvmLog("Projector files can't be used with -repl, -Dselftest, or program file arguments.\n");
1111 usage();
1113 if (settings.numthreads > 1 || settings.numworkers > 1) {
1114 AvmLog("A projector requires exactly one worker on one thread.\n");
1115 usage();
1117 settings.do_projector = 1;
1118 return;
1120 #endif
1122 #ifndef VMCFG_AOT
1123 if (settings.numfiles == 0) {
1124 // no files, so we need something more
1125 if (!settings.do_selftest && !settings.do_repl) {
1126 AvmLog("You must provide input files, -repl, or -Dselftest, or the executable must be a projector file.\n");
1127 usage();
1130 #endif
1132 #ifdef VMCFG_EVAL
1133 if (settings.do_repl)
1135 if (settings.numthreads > 1 || settings.numworkers > 1) {
1136 AvmLog("The REPL requires exactly one worker on one thread.\n");
1137 usage();
1140 #endif
1142 #ifdef VMCFG_SELFTEST
1143 if (settings.do_selftest)
1145 // Presumably we'd want to use the selftest harness to test multiple workers eventually.
1146 if (settings.numthreads > 1 || settings.numworkers > 1 || settings.numfiles > 0) {
1147 AvmLog("The selftest harness requires exactly one worker on one thread, and no input files.\n");
1148 usage();
1151 #endif
1154 // Does not return
1156 /*static*/
1157 void Shell::usage()
1159 AvmLog("avmplus shell " AVMPLUS_VERSION_USER " " AVMPLUS_BIN_TYPE " build " AVMPLUS_BUILD_CODE "\n\n");
1160 #ifdef AVMPLUS_DESC_STRING
1161 if (VMPI_strcmp(AVMPLUS_DESC_STRING, ""))
1163 AvmLog("Description: " AVMPLUS_DESC_STRING "\n\n");
1165 #endif
1166 AvmLog("usage: avmplus\n");
1167 #ifdef DEBUGGER
1168 AvmLog(" [-d] enter debugger on start\n");
1169 #endif
1170 AvmLog(" [-memstats] generate statistics on memory usage\n");
1171 AvmLog(" [-memstats-verbose]\n"
1172 " generate more statistics on memory usage\n");
1173 AvmLog(" [-memlimit d] limit the heap size to d pages\n");
1174 AvmLog(" [-eagersweep] sweep the heap synchronously at the end of GC;\n"
1175 " improves usage statistics.\n");
1176 #ifdef MMGC_POLICY_PROFILING
1177 AvmLog(" [-gcbehavior] summarize GC behavior and policy, after every gc\n");
1178 AvmLog(" [-gcsummary] summarize GC behavior and policy, at end only\n");
1179 #endif
1180 AvmLog(" [-load L,B, ...\n"
1181 " GC load factor L up to a post-GC heap size of B megabytes.\n"
1182 " Up to %d pairs can be accommodated, the limit for the last pair\n"
1183 " will be ignored and can be omitted\n", int(MMgc::GCHeapConfig::kNumLoadFactors));
1184 AvmLog(" [-loadCeiling X] GC load multiplier ceiling (default 1.0)\n");
1185 AvmLog(" [-gcwork G] Max fraction of time (default 0.25) we're willing to spend in GC\n");
1186 AvmLog(" [-stack N] Stack size in bytes (will be honored approximately).\n"
1187 " Be aware of the stack margin: %u\n", avmshell::kStackMargin);
1188 #ifdef MMGC_MARKSTACK_ALLOWANCE
1189 AvmLog(" [-gcstack N] Mark stack size allowance (# of segments), for testing.\n");
1190 #endif
1191 AvmLog(" [-cache_bindings N] size of bindings cache (0 = unlimited)\n");
1192 AvmLog(" [-cache_metadata N] size of metadata cache (0 = unlimited)\n");
1193 AvmLog(" [-cache_methods N] size of method cache (0 = unlimited)\n");
1194 AvmLog(" [-Dgreedy] collect before every allocation\n");
1195 AvmLog(" [-Dnogc] don't collect\n");
1196 #ifdef VMCFG_SELECTABLE_EXACT_TRACING
1197 AvmLog(" [-Dnoexactgc] disable exact tracing\n");
1198 #endif
1199 AvmLog(" [-Dnoincgc] don't use incremental collection\n");
1200 AvmLog(" [-Dnodebugger] do not initialize the debugger (in DEBUGGER builds)\n");
1201 AvmLog(" [-Dgcthreshold N] lower bound on allocation budget, in blocks, between collection completions\n");
1202 AvmLog(" [-Dnofixedcheck] don't check FixedMalloc deallocations for correctness (sometimes expensive)\n");
1203 #ifdef DEBUGGER
1204 AvmLog(" [-Dastrace N] display AS execution information, where N is [1..4]\n");
1205 AvmLog(" [-Dlanguage l] localize runtime errors, languages are:\n");
1206 AvmLog(" en,de,es,fr,it,ja,ko,zh-CN,zh-TW\n");
1207 #endif
1208 #ifdef AVMPLUS_VERBOSE
1209 AvmLog(" [-Dverbose[=[parse,verify,interp,traits,builtins,minaddr,memstats,sweep,occupancy,execpolicy"
1210 # ifdef VMCFG_NANOJIT
1211 ",jit,opt,regs,raw,bytes"
1212 # endif
1213 "]]\n");
1214 AvmLog(" With no options, enables extreme! output mode. Otherwise the\n");
1215 AvmLog(" options are mostly self-descriptive except for the following: \n");
1216 AvmLog(" builtins - includes output from builtin methods\n");
1217 AvmLog(" memstats - generate statistics on memory usage \n");
1218 AvmLog(" sweep - [memstats] include detailed sweep information \n");
1219 AvmLog(" occupancy - [memstats] include occupancy bit graph \n");
1220 AvmLog(" execpolicy - shows which execution method (interpretation, compilation) was chosen and why \n");
1221 # ifdef VMCFG_NANOJIT
1222 AvmLog(" jit - output LIR as it is generated, and final assembly code\n");
1223 AvmLog(" opt - [jit] show details about each optimization pass\n");
1224 AvmLog(" regs - [jit] show register allocation state after each assembly instruction\n");
1225 AvmLog(" raw - [jit] assembly code is displayed in raw (i.e unbuffered bottom-up) fashion. \n");
1226 AvmLog(" bytes - [jit] display the byte values of the assembly code. \n");
1227 # endif
1229 AvmLog(" Note that ordering matters for options with dependencies. Dependencies \n");
1230 AvmLog(" are contained in [ ] For example, 'sweep' requires 'memstats' \n");
1231 #endif
1232 #ifdef VMCFG_NANOJIT
1233 AvmLog(" [-Dinterp] do not generate machine code, interpret instead\n");
1234 AvmLog(" [-Ojit] use jit always, never interp (except when the jit fails)\n");
1235 AvmLog(" [-Djitordie] use jit always, and abort when the jit fails\n");
1236 AvmLog(" [-Dnocse] disable CSE optimization \n");
1237 AvmLog(" [-jitharden] enable jit hardening techniques\n");
1238 #ifdef AVMPLUS_IA32
1239 AvmLog(" [-Dnosse] use FPU stack instead of SSE2 instructions\n");
1240 AvmLog(" [-Dfixedesp] pre-decrement stack for all needed call usage upon method entry\n");
1241 #endif
1242 #ifdef AVMPLUS_ARM
1243 AvmLog(" [-Darm_arch N] nanojit assumes ARMvN architecture (default=5)\n");
1244 AvmLog(" [-Darm_vfp] nanojit uses VFP rather than SoftFloat\n");
1245 #endif
1246 #endif
1247 #ifdef AVMPLUS_JITMAX
1248 AvmLog(" [-jitmax N-M] jit the Nth to Mth methods only; N- and -M are also valid.\n");
1249 #endif
1250 #ifdef VMCFG_VERIFYALL
1251 AvmLog(" [-Dverifyall] verify greedily instead of lazily\n");
1252 AvmLog(" [-Dverifyonly] verify greedily and don't execute anything\n");
1253 #endif
1254 #ifdef VMCFG_SELFTEST
1255 AvmLog(" [-Dselftest[=component,category,test]]");
1256 AvmLog(" run selftests\n");
1257 #endif
1258 AvmLog(" [-Dtimeout] enforce maximum 15 seconds execution\n");
1259 AvmLog(" [-Dversion] print the version and the list of compiled-in features and then exit\n");
1260 #ifdef AVMPLUS_WIN32
1261 AvmLog(" [-error] crash opens debug dialog, instead of dumping\n");
1262 #endif
1263 #ifdef VMCFG_EVAL
1264 AvmLog(" [-repl] read-eval-print mode\n");
1265 #endif
1266 #ifdef VMCFG_WORKERTHREADS
1267 AvmLog(" [-workers W,T[,R]]\n");
1268 AvmLog(" Spawn W worker VMs on T threads where W >= T and T >= 1.\n");
1269 AvmLog(" The files provided are handed off to the workers in the order given,\n");
1270 AvmLog(" as workers become available, and these workers are scheduled onto threads\n");
1271 AvmLog(" in a deterministic order that prevents them from having affinity to a thread.\n");
1272 AvmLog(" To test this functionality you want many more files than workers and many more\n");
1273 AvmLog(" workers than threads, and at least two threads.\n");
1274 AvmLog(" If R > 0 is provided then it is the number of times the list of files is repeated.\n");
1275 #endif
1276 AvmLog(" [-swfHasAS3] Exit with code 0 if the single file argument is a swf that contains a DoABC or DoABC2 tag,\n");
1277 AvmLog(" otherwise exit with code 1. Do not execute or verify anything.\n");
1278 AvmLog(" [-swfversion version]\n");
1279 AvmLog(" Run with a given bug-compatibility version in use by default.\n");
1280 AvmLog(" (This can be overridden on a per-ABC basis by embedded metadata.)\n");
1281 AvmLog(" Legal versions are:\n");
1282 for (int j = 0; j < BugCompatibility::VersionCount; ++j)
1284 AvmLog(" %d\n",BugCompatibility::kNames[j]);
1286 AvmLog(" [-log]\n");
1287 AvmLog(" [-api N] execute ABCs as version N (see api-versions.h)\n");
1288 AvmLog(" [-jargs ... ;] args passed to Java runtime\n");
1289 AvmLog(" [filename.{abc,swf} ...\n");
1290 AvmLog(" [-- application argument ...]\n");
1291 Platform::GetInstance()->exit(1);