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
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.
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 ***** */
42 #include "../nanojit/nanojit.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"
57 extern bool show_error
; // in avmshellWin.cpp
60 ShellSettings::ShellSettings()
62 , programFilename(NULL
)
76 ShellCoreImpl::ShellCoreImpl(MMgc::GC
* gc
, ShellSettings
& settings
, bool mainthread
)
79 , mainthread(mainthread
)
84 void ShellCoreImpl::setStackLimit()
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
92 minstack
= uintptr_t(&minstack
) - settings
.stackSize
+ avmshell::kStackMargin
;
96 #ifdef VMCFG_WORKERTHREADS
98 minstack
= Platform::GetInstance()->getMainThreadStackLimit();
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
105 pthread_attr_init(&attr
);
106 pthread_attr_getstacksize(&attr
, &stackheight
);
107 pthread_attr_destroy(&attr
);
108 minstack
= uintptr_t(&stackheight
) - stackheight
+ avmshell::kStackMargin
;
111 minstack
= Platform::GetInstance()->getMainThreadStackLimit();
115 // call the non-virtual setter on AvmCore
116 AvmCore::setStackLimit(minstack
);
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
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
);
148 multiWorker(settings
);
150 singleWorker(settings
);
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();
164 // In the single worker case everything is run on the main thread. This
165 // is where we handler the repl, selftest, and projectors.
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
) );
176 ShellCore
* shell
= new ShellCoreImpl(gc
, settings
, true);
177 Shell::singleWorkerHelper(shell
, settings
);
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
);
196 #ifdef AVMSHELL_PROJECTOR_SUPPORT
197 if (settings
.do_projector
) {
198 AvmAssert(settings
.programFilename
!= NULL
);
199 int exitCode
= shell
->executeProjector(settings
.programFilename
);
201 Platform::GetInstance()->exit(exitCode
);
206 int exitCode
= shell
->evaluateFile(settings
, NULL
);
208 Platform::GetInstance()->exit(exitCode
);
212 // execute each abc file
213 for (int i
=0 ; i
< settings
.numfiles
; i
++ ) {
214 int exitCode
= shell
->evaluateFile(settings
, settings
.filenames
[i
]);
216 Platform::GetInstance()->exit(exitCode
);
220 if (settings
.do_repl
)
225 #ifdef VMCFG_WORKERTHREADS
227 //#define LOGGING(x) 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
;
246 CoreNode(ShellCore
* core
, int id
)
255 // Destruction order matters.
256 MMgc::GC
* gc
= core
->GetGC();
260 core
->codeContextThread
= VMPI_currentThread();
269 ShellCore
* const core
;
271 CoreNode
* next
; // For the LRU list of available cores
276 ThreadNode(MultiworkerState
& state
, int id
)
284 // 'thread' is initialized after construction
285 pthread_cond_init(&c
, NULL
);
286 pthread_mutex_init(&m
, NULL
);
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
;
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
)
321 , numthreads(settings
.numthreads
)
322 , numcores(settings
.numworkers
)
324 , free_threads_last(NULL
)
326 , free_cores_last(NULL
)
327 , num_free_threads(0)
329 pthread_mutex_init(&m
, NULL
);
330 pthread_cond_init(&c
, NULL
);
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
;
348 free_threads_last
= t
;
350 if (t
->corenode
!= NULL
) {
351 if (free_cores_last
!= NULL
)
352 free_cores_last
->next
= t
->corenode
;
354 free_cores
= t
->corenode
;
355 free_cores_last
= t
->corenode
;
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
)
371 free_threads
= free_threads
->next
;
372 if (free_threads
== NULL
)
373 free_threads_last
= NULL
;
377 free_cores
= free_cores
->next
;
378 if (free_cores
== NULL
)
379 free_cores_last
= NULL
;
387 ShellSettings
& settings
;
391 // Queues of available threads and cores. Protected by m, availability signaled on 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
);
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
);
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
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
++ )
469 for ( int i
=0 ; i
< numcores
; i
++ )
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
);
487 ThreadNode
* threadnode
;
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
]);
493 if (nextfile
== numfiles
) {
501 LOGGING( AvmLog("Waiting for available threads.\n"); )
502 pthread_cond_wait(&state
.c
, &state
.m
);
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
;
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
); )
538 LOGGING( AvmLog("T%d: Work starting\n", self
->id
); )
540 MMGC_GCENTER(self
->corenode
->core
->GetGC());
542 self
->corenode
->core
->codeContextThread
= VMPI_currentThread();
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
);
557 #endif // VMCFG_WORKERTHREADS
562 void Shell::repl(ShellCore
* shellCore
)
564 const int kMaxCommandLine
= 1024;
565 char commandLine
[kMaxCommandLine
];
568 AvmLog("avmplus interactive shell\n"
569 "Type '?' for help\n\n");
573 bool record_time
= false;
576 if(Platform::GetInstance()->getUserInput(commandLine
, kMaxCommandLine
) == NULL
)
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"
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");
592 if (VMPI_strncmp(commandLine
, ".load", 5) == 0) {
593 const char* s
= commandLine
+5;
594 while (*s
== ' ' || *s
== '\t')
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
601 AvmLog("The .load command is not implemented\n");
605 if (VMPI_strncmp(commandLine
, ".input", 6) == 0) {
606 input
= shellCore
->newStringLatin1("");
608 if(Platform::GetInstance()->getUserInput(commandLine
, kMaxCommandLine
) == NULL
)
610 commandLine
[kMaxCommandLine
-1] = 0;
611 if (VMPI_strncmp(commandLine
, ".end", 4) == 0)
613 input
->appendLatin1(commandLine
);
618 if (VMPI_strncmp(commandLine
, ".quit", 5) == 0) {
622 if (VMPI_strncmp(commandLine
, ".time", 5) == 0) {
624 input
= shellCore
->newStringLatin1(commandLine
+5);
628 input
= shellCore
->newStringLatin1(commandLine
);
631 shellCore
->evaluateString(input
, record_time
);
637 // open logfile based on a filename
639 void Shell::initializeLogging(const char* basename
)
641 const char* lastDot
= VMPI_strrchr(basename
, '.');
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
)
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
;
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
];
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
);
685 settings
.arguments
= &argv
[i
];
686 settings
.numargs
= argc
- i
;
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;
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")) {
752 for (int j
=0;j
<kLanguages
;j
++) {
753 if (!VMPI_strcmp(argv
[i
+1],languageNames
[j
].str
)) {
758 if (settings
.langID
==-1) {
759 settings
.langID
= VMPI_atoi(argv
[i
+1]);
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
!= ',')
776 settings
.st_category
= p
;
777 while (*p
&& *p
!= ',')
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
796 settings
.do_verbose
= AvmCore::parseVerboseFlags(&arg
[10], badFlag
);
798 AvmLog("Unknown verbose flag while parsing '%s'\n", badFlag
);
803 #endif /* AVMPLUS_VERBOSE */
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
;
817 AvmLog("Unrecognized option %s\n", arg
);
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);
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
) {
844 char* val
= argv
[++i
];
845 char* dash
= VMPI_strchr(val
,'-');
848 jitmax
= VMPI_atoi(&val
[1]); // -n form
850 int32_t hl
= VMPI_strlen(dash
);
851 dash
[0] = '\0'; // hammer argv ;) - go boom?
852 jitmin
= VMPI_atoi(val
);
854 jitmax
= VMPI_atoi(&dash
[1]);
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;
881 else if (!VMPI_strcmp(arg
, "-eagersweep")) {
882 MMgc::GCHeap::GetGCHeap()->Config().eagerSweeping
= true;
884 else if (!VMPI_strcmp(arg
, "-load") && i
+1 < argc
) {
888 const char* val
= argv
[++i
];
889 const char* origval
= val
;
891 // limit=0 is legal, it means unlimited
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
;
900 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff
[k
-1] = DBL_MAX
;
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
;
914 AvmLog("Bad value to -load: %s\n", origval
);
918 else if (!VMPI_strcmp(arg
, "-loadCeiling") && i
+1 < argc
) {
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
;
926 AvmLog("Bad value to -loadCeiling: %s\n", val
);
930 else if (!VMPI_strcmp(arg
, "-gcwork") && i
+1 < argc
) {
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
;
938 AvmLog("Bad value to -gcwork: %s\n", val
);
942 else if (!VMPI_strcmp(arg
, "-stack")) {
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
);
951 AvmLog("Bad argument to -stack\n");
955 else if (!VMPI_strcmp(arg
, "-log")) {
956 settings
.do_log
= true;
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
];
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
);
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
);
982 #endif // VMCFG_WORKERTHREADS
984 else if (!VMPI_strcmp(arg
, "-error")) {
987 SetErrorMode(0); // set to default
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
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
] == ';')
1003 if (!first
) VMPI_strcat(Java::startup_options
, " ");
1004 VMPI_strcat(Java::startup_options
, argv
[i
]);
1007 AvmAssert(VMPI_strlen(Java::startup_options
) < 256);
1009 #endif /* AVMPLUS_WITH_JNI */
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
) {
1017 settings
.api
= ApiUtils::parseApiVersion(argv
[i
+1], badFlag
);
1020 AvmLog("Unknown api version'%s'\n", argv
[i
+1]);
1025 else if (VMPI_strcmp(arg
, "-swfversion") == 0 && i
+1 < argc
) {
1026 int j
= BugCompatibility::VersionCount
;
1027 unsigned swfVersion
;
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
;
1041 if (j
== BugCompatibility::VersionCount
) {
1042 AvmLog("Unrecognized -swfversion version %s\n", val
);
1047 // Unrecognized command line option
1048 AvmLog("Unrecognized option %s\n", arg
);
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);
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");
1090 if (settings
.numthreads
> 1 || settings
.numworkers
> 1) {
1091 AvmLog("A projector requires exactly one worker on one thread.\n");
1094 settings
.do_projector
= 1;
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");
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");
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");
1136 AvmLog("avmplus shell " AVMPLUS_VERSION_USER
" " AVMPLUS_BIN_TYPE
" build " AVMPLUS_BUILD_CODE
"\n\n");
1137 AvmLog("usage: avmplus\n");
1139 AvmLog(" [-d] enter debugger on start\n");
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");
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");
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");
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");
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"
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");
1197 AvmLog(" Note that ordering matters for options with dependencies. Dependencies \n");
1198 AvmLog(" are contained in [ ] For example, 'sweep' requires 'memstats' \n");
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");
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");
1211 AvmLog(" [-Darm_arch N] nanojit assumes ARMvN architecture (default=5)\n");
1212 AvmLog(" [-Darm_vfp] nanojit uses VFP rather than SoftFloat\n");
1215 #ifdef AVMPLUS_JITMAX
1216 AvmLog(" [-jitmax N-M] jit the Nth to Mth methods only; N- and -M are also valid.\n");
1218 #ifdef VMCFG_VERIFYALL
1219 AvmLog(" [-Dverifyall] verify greedily instead of lazily\n");
1220 AvmLog(" [-Dverifyonly] verify greedily and don't execute anything\n");
1222 #ifdef VMCFG_SELFTEST
1223 AvmLog(" [-Dselftest[=component,category,test]]");
1224 AvmLog(" run selftests\n");
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");
1232 AvmLog(" [-repl] read-eval-print mode\n");
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");
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);