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 "extensions-tracers.hh"
47 #include "avmshell-tracers.hh"
52 extern bool show_error
; // in avmshellWin.cpp
55 ShellSettings::ShellSettings()
57 , programFilename(NULL
)
71 ShellCoreImpl::ShellCoreImpl(MMgc::GC
* gc
, ShellSettings
& settings
, bool mainthread
)
72 : ShellCore(gc
, settings
.apiVersionSeries
)
74 , mainthread(mainthread
)
79 void ShellCoreImpl::setStackLimit()
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
87 minstack
= uintptr_t(&minstack
) - settings
.stackSize
+ avmshell::kStackMargin
;
91 #ifdef VMCFG_WORKERTHREADS
93 minstack
= Platform::GetInstance()->getMainThreadStackLimit();
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
100 pthread_attr_init(&attr
);
101 pthread_attr_getstacksize(&attr
, &stackheight
);
102 pthread_attr_destroy(&attr
);
103 minstack
= uintptr_t(&stackheight
) - stackheight
+ avmshell::kStackMargin
;
106 minstack
= Platform::GetInstance()->getMainThreadStackLimit();
110 // call the non-virtual setter on AvmCore
111 AvmCore::setStackLimit(minstack
);
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
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
);
143 multiWorker(settings
);
145 singleWorker(settings
);
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();
159 // In the single worker case everything is run on the main thread. This
160 // is where we handler the repl, selftest, and projectors.
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
) );
172 ShellCore
* shell
= new ShellCoreImpl(gc
, settings
, true);
173 Shell::singleWorkerHelper(shell
, settings
);
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
);
192 #ifdef AVMSHELL_PROJECTOR_SUPPORT
193 if (settings
.do_projector
) {
194 AvmAssert(settings
.programFilename
!= NULL
);
195 int exitCode
= shell
->executeProjector(settings
.programFilename
);
197 Platform::GetInstance()->exit(exitCode
);
202 int exitCode
= shell
->evaluateFile(settings
, NULL
);
204 Platform::GetInstance()->exit(exitCode
);
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
]);
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
;
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
);
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
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
++ )
470 for ( int i
=0 ; i
< numcores
; i
++ )
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
);
488 ThreadNode
* threadnode
;
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
]);
494 if (nextfile
== numfiles
) {
502 LOGGING( AvmLog("Waiting for available threads.\n"); )
503 pthread_cond_wait(&state
.c
, &state
.m
);
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
;
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
); )
539 LOGGING( AvmLog("T%d: Work starting\n", self
->id
); )
541 MMGC_GCENTER(self
->corenode
->core
->GetGC());
543 self
->corenode
->core
->codeContextThread
= VMPI_currentThread();
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
);
558 #endif // VMCFG_WORKERTHREADS
563 void Shell::repl(ShellCore
* shellCore
)
565 const int kMaxCommandLine
= 1024;
566 char commandLine
[kMaxCommandLine
];
569 AvmLog("avmplus interactive shell\n"
570 "Type '?' for help\n\n");
574 bool record_time
= false;
577 if(Platform::GetInstance()->getUserInput(commandLine
, kMaxCommandLine
) == NULL
)
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"
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");
593 if (VMPI_strncmp(commandLine
, ".load", 5) == 0) {
594 const char* s
= commandLine
+5;
595 while (*s
== ' ' || *s
== '\t')
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
602 AvmLog("The .load command is not implemented\n");
606 if (VMPI_strncmp(commandLine
, ".input", 6) == 0) {
607 input
= shellCore
->newStringLatin1("");
609 if(Platform::GetInstance()->getUserInput(commandLine
, kMaxCommandLine
) == NULL
)
611 commandLine
[kMaxCommandLine
-1] = 0;
612 if (VMPI_strncmp(commandLine
, ".end", 4) == 0)
614 input
->appendLatin1(commandLine
);
619 if (VMPI_strncmp(commandLine
, ".quit", 5) == 0) {
623 if (VMPI_strncmp(commandLine
, ".time", 5) == 0) {
625 input
= shellCore
->newStringLatin1(commandLine
+5);
629 input
= shellCore
->newStringLatin1(commandLine
);
632 shellCore
->evaluateString(input
, record_time
);
638 // open logfile based on a filename
640 void Shell::initializeLogging(const char* basename
)
642 const char* lastDot
= VMPI_strrchr(basename
, '.');
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
)
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
;
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
];
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
);
686 settings
.arguments
= &argv
[i
];
687 settings
.numargs
= argc
- i
;
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;
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")) {
753 for (int j
=0;j
<kLanguages
;j
++) {
754 if (!VMPI_strcmp(argv
[i
+1],languageNames
[j
].str
)) {
759 if (settings
.langID
==-1) {
760 settings
.langID
= VMPI_atoi(argv
[i
+1]);
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
!= ',')
777 settings
.st_category
= p
;
778 while (*p
&& *p
!= ',')
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
797 settings
.do_verbose
= AvmCore::parseVerboseFlags(&arg
[10], badFlag
);
799 AvmLog("Unknown verbose flag while parsing '%s'\n", badFlag
);
804 #endif /* AVMPLUS_VERBOSE */
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
;
818 AvmLog("Unrecognized option %s\n", arg
);
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;
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
) {
848 char* val
= argv
[++i
];
849 char* dash
= VMPI_strchr(val
,'-');
852 jitmax
= VMPI_atoi(&val
[1]); // -n form
854 int32_t hl
= VMPI_strlen(dash
);
855 dash
[0] = '\0'; // hammer argv ;) - go boom?
856 jitmin
= VMPI_atoi(val
);
858 jitmax
= VMPI_atoi(&dash
[1]);
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;
885 else if (!VMPI_strcmp(arg
, "-eagersweep")) {
886 MMgc::GCHeap::GetGCHeap()->Config().eagerSweeping
= true;
888 else if (!VMPI_strcmp(arg
, "-load") && i
+1 < argc
) {
892 const char* val
= argv
[++i
];
893 const char* origval
= val
;
895 // limit=0 is legal, it means unlimited
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
;
904 MMgc::GCHeap::GetGCHeap()->Config().gcLoadCutoff
[k
-1] = DBL_MAX
;
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
;
918 AvmLog("Bad value to -load: %s\n", origval
);
922 else if (!VMPI_strcmp(arg
, "-loadCeiling") && i
+1 < argc
) {
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
;
930 AvmLog("Bad value to -loadCeiling: %s\n", val
);
934 else if (!VMPI_strcmp(arg
, "-gcwork") && i
+1 < argc
) {
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
;
942 AvmLog("Bad value to -gcwork: %s\n", val
);
946 else if (!VMPI_strcmp(arg
, "-stack")) {
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
);
955 AvmLog("Bad argument to -stack\n");
959 #ifdef MMGC_MARKSTACK_ALLOWANCE
960 else if (!VMPI_strcmp(arg
, "-gcstack")) {
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
);
969 AvmLog("Bad argument to -gcstack\n");
974 else if (!VMPI_strcmp(arg
, "-log")) {
975 settings
.do_log
= true;
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
];
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
);
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
);
1001 #endif // VMCFG_WORKERTHREADS
1002 #ifdef AVMPLUS_WIN32
1003 else if (!VMPI_strcmp(arg
, "-error")) {
1006 SetErrorMode(0); // set to default
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
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
] == ';')
1022 if (!first
) VMPI_strcat(Java::startup_options
, " ");
1023 VMPI_strcat(Java::startup_options
, argv
[i
]);
1026 AvmAssert(VMPI_strlen(Java::startup_options
) < 256);
1028 #endif /* AVMPLUS_WITH_JNI */
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]);
1042 else if (VMPI_strcmp(arg
, "-swfversion") == 0 && i
+1 < argc
) {
1043 int j
= BugCompatibility::VersionCount
;
1044 unsigned swfVersion
;
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
;
1058 if (j
== BugCompatibility::VersionCount
) {
1059 AvmLog("Unrecognized -swfversion version %s\n", val
);
1064 // Unrecognized command line option
1065 AvmLog("Unrecognized option %s\n", arg
);
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);
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");
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");
1113 if (settings
.numthreads
> 1 || settings
.numworkers
> 1) {
1114 AvmLog("A projector requires exactly one worker on one thread.\n");
1117 settings
.do_projector
= 1;
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");
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");
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");
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");
1166 AvmLog("usage: avmplus\n");
1168 AvmLog(" [-d] enter debugger on start\n");
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");
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");
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");
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");
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");
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"
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");
1229 AvmLog(" Note that ordering matters for options with dependencies. Dependencies \n");
1230 AvmLog(" are contained in [ ] For example, 'sweep' requires 'memstats' \n");
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");
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");
1243 AvmLog(" [-Darm_arch N] nanojit assumes ARMvN architecture (default=5)\n");
1244 AvmLog(" [-Darm_vfp] nanojit uses VFP rather than SoftFloat\n");
1247 #ifdef AVMPLUS_JITMAX
1248 AvmLog(" [-jitmax N-M] jit the Nth to Mth methods only; N- and -M are also valid.\n");
1250 #ifdef VMCFG_VERIFYALL
1251 AvmLog(" [-Dverifyall] verify greedily instead of lazily\n");
1252 AvmLog(" [-Dverifyonly] verify greedily and don't execute anything\n");
1254 #ifdef VMCFG_SELFTEST
1255 AvmLog(" [-Dselftest[=component,category,test]]");
1256 AvmLog(" run selftests\n");
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");
1264 AvmLog(" [-repl] read-eval-print mode\n");
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");
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);