/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * splrun.c: SPL command line interpreter */ #include #include #include #include #include #include #include #include #include #ifndef USEWIN32API # include #endif #include "spl.h" #include "compat.h" static int got_sigint = 0; void help(int ret) { printf("\n"); switch ( ret ) { case 2: printf("Input-Output Error!\n\n"); break; } printf("Usage: splrun [Options] [-d dump-file] [{-x|-m} bytecode-file] \\\n"); printf(" { spl-source-file | -q spl-source | -a assembler-file | \\\n"); printf(" -c bytecode-file | {-R|-r} restore-file }\n\n"); printf(" -N Do not execute the program - just compile or restore\n"); printf(" -A Show assembler dump (only available when compiling)\n"); printf(" -O Show assembler dump before the optimizer modified it\n"); printf(" -D Enable Debug output for all instructions executed\n"); printf(" -C Run reference-counter checks after each instruction\n"); printf(" -S Print machine state to standard error on exit\n"); printf(" -SS Print machine state to standard error after each instruction\n"); printf(" -T Create a tree-dump to standard error on exit\n"); printf(" -F Show global SPL malloc/free counters on exit\n"); printf(" -P Run the parser in debug mode (set spl_yydebug to 1)\n"); printf(" -X Print a dummy C source file for xgettext (don't mix with -o)\n"); printf(" -t Print data segment (when -A is also passed)\n"); printf(" -e Compile with debug symbols\n"); printf(" -E Run command line debugger\n\n"); printf(" -o Do not run the optimizer\n\n"); printf(" -p name[=val] Set parameter to be passed to program\n\n"); printf(" -K directory Set the code cache directory\n\n"); printf(" -M path Set the module search path\n\n"); printf(" -b addr Set breakpoint\n"); printf(" -s Stepping one instruction forward\n\n"); printf(" -ss Step one instruction, dump, reload, ..\n"); printf(" (needs \"-d file\" option)\n\n"); printf(" -B file Collect benchmark data and write to file\n\n"); printf(" -q source Execute a literally passed script\n\n"); printf(" -a file Assemble and execute\n"); printf(" -c file Execute Pre-compiled binary\n"); printf(" -x file Write compiled binary to file\n\n"); printf(" -m file Write a executeable SPL binary file\n\n"); printf(" -d file Dump state to file on exit\n"); printf(" -r file Restore state from dumped file\n"); printf(" -R file Restore from and dump to this file\n\n"); exit(ret); } static void sigint_handler(int UNUSED(dummy)); static void sigint_handler(int UNUSED(dummy)) { got_sigint = 1; return; } struct parameter { char *name, *value; struct parameter *next; }; int main(int argc, char **argv) { int opt_dump_asm = 0; int opt_dump_asm_preopt = 0; int opt_no_optimizer = 0; int opt_no_exec = 0; int opt_debugger = 0; int opt_debug_exec = 0; int opt_debug_sym = 0; int opt_dump_state = 0; int opt_stepping = 0; FILE *opt_dump_file = 0; FILE *opt_restore_file = 0; FILE *opt_bench_file = 0; int *bench_data_usec = 0; int *bench_data_iter = 0; float bench_total_usec = 0; struct spl_code *bench_data_code = 0; char *opt_gen_bytecode = 0; int opt_gen_bytecode_exec = 0; int opt_read_assembler = 0; int opt_read_bytecode = 0; int opt_show_malloc_counter = 0; int opt_print_data_seg = 0; int opt_treedump = 0; int opt_rccheck = 0; int opt, rc = 0, ret = 1; int breakpoint = -1; char *module_path = 0; char *codecache_dir = 0; char *cmdline_script = 0; struct parameter *para_list = 0; signal(SIGINT, sigint_handler); while ( (opt = getopt(argc, argv, "+p:q:AtODCNSeEod:r:R:b:sB:PXacm:x:K:M:TF")) != -1 ) { switch ( opt ) { case 'p': { struct parameter *p = malloc(sizeof(struct parameter)); p->name = strdup(optarg); p->value = strchr(p->name, '='); if (p->value) { *p->value = 0; p->value++; } p->next = para_list; para_list = p; break; } case 'q': { cmdline_script = optarg; break; } case 'A': opt_dump_asm = 1; break; case 't': opt_print_data_seg = 1; break; case 'O': opt_dump_asm_preopt = 1; break; case 'D': opt_debug_exec = 1; break; case 'C': opt_rccheck = 1; break; case 'N': opt_no_exec = 1; break; case 'S': opt_dump_state++; break; case 'e': opt_debug_sym = 1; break; case 'E': opt_debugger = 1; break; case 'o': opt_no_optimizer = 1; break; case 'd': opt_dump_file = fopen(optarg, "w+"); break; case 'R': opt_dump_file = opt_restore_file = fopen(optarg, "r+"); break; case 'r': opt_restore_file = fopen(optarg, "r"); break; case 'b': breakpoint = atoi(optarg); break; case 's': opt_stepping++; break; case 'B': opt_bench_file = fopen(optarg, "w+"); break; case 'P': spl_yydebug = 1; break; case 'X': spl_dump_translatable_strings = 1; break; case 'a': opt_read_assembler = 1; break; case 'c': opt_read_bytecode = 1; break; case 'm': opt_gen_bytecode_exec = 1; case 'x': opt_gen_bytecode = optarg; break; case 'K': codecache_dir = optarg; break; case 'M': module_path = optarg; break; case 'T': opt_treedump = 1; break; case 'F': opt_show_malloc_counter = 1; break; default: help(1); } } if ( !opt_restore_file && !cmdline_script && optind+1 > argc ) help(1); if ( opt_restore_file && optind != argc ) help(1); if ( opt_stepping > 1 && !opt_dump_file ) help(1); if ( spl_dump_translatable_strings && opt_no_optimizer ) help(1); struct spl_vm *vm; struct spl_task *task; dump_reload_loop: vm = spl_vm_create(); if (module_path) vm->path = strdup(module_path); else my_asprintf(&vm->path, ".:./spl_modules:%s", spl_system_modules_dir()); vm->codecache_dir = codecache_dir ? strdup(codecache_dir) : 0; vm->runloop = spl_simple_runloop; if ( opt_restore_file ) { if (!spl_restore_ascii(vm, opt_restore_file)) return 1; task = vm->task_list; if ( opt_dump_file == opt_restore_file ) { rewind(opt_dump_file); #ifndef USEWIN32API /* hmm.. there is no ftruncate on mingw32 ? * ok - so we simply do not truncate the file, * the restore function will ignore everything * after the END token anyways. */ ftruncate(fileno(opt_dump_file), 0); #endif } else fclose(opt_restore_file); } else if ( opt_read_bytecode ) { struct spl_code *code = spl_code_get(0); code->code_type = SPL_CODE_MAPPED; code->code = spl_mmap_file(argv[optind++], &code->size); if (!code->code) help(2); if (*code->code == '#') { spl_state_counter_free_inc(); code->code_type = SPL_CODE_STATIC; while (*code->code != SPL_OP_SIG && code->size > 0) { code->code++; code->size--; } } task = spl_task_create(vm, "main"); spl_task_setcode(task, code); } else { task = spl_task_create(vm, "main"); struct spl_asm *as = spl_asm_create(); as->vm = vm; if ( opt_read_assembler ) { FILE *f = fopen(argv[optind++], "r"); char line[1024]; int lineno=1; if (!f) help(2); while ( fgets(line, 1024, f) ) { if ( spl_asm_parse_line(as, line) < 0 ) { printf("... in line %d.\n", lineno); exit(1); } lineno++; } if ( spl_asm_resolve_labels(as) < 0 ) exit(1); fclose(f); } else { char *spl_source = cmdline_script ?: spl_malloc_file(argv[optind++], 0); if ( !spl_source) { spl_asm_destroy(as); spl_vm_destroy(vm); help(2); } if ( spl_compiler(as, spl_source, cmdline_script ? "command line" : argv[optind-1], spl_malloc_file, opt_debug_sym) ) return 1; spl_asm_add(as, SPL_OP_HALT, 0); if (!cmdline_script) free(spl_source); } if (opt_dump_asm_preopt) spl_asm_print(as, opt_print_data_seg); if ( !opt_no_optimizer ) spl_optimizer(as); if (opt_dump_asm) spl_asm_print(as, opt_print_data_seg); if (opt_gen_bytecode) { int fd = open(opt_gen_bytecode, O_WRONLY|O_TRUNC|O_CREAT|MY_O_BINARY, opt_gen_bytecode_exec ? 0777 : 0666); if ( fd < 0) { printf("Can't write bytecode file!\n"); return 1; } if ( opt_gen_bytecode_exec ) { char *magic_cookie = "#!/bin/sh\nexec splrun -c \"$0\" \"$@\"; exit $?\n"; write(fd, magic_cookie, strlen(magic_cookie)); } spl_asm_write(as, fd); spl_asm_destroy(as); spl_vm_destroy(vm); return 0; } spl_task_setcode(task, spl_asm_dump(as)); task->code->id = strdup(cmdline_script ? "command line" : argv[optind-1]); spl_asm_destroy(as); } if (opt_bench_file && task->code) { bench_data_code = spl_code_get(task->code); bench_data_usec = calloc(bench_data_code->size, sizeof(int)); bench_data_iter = calloc(bench_data_code->size, sizeof(int)); } task->debug = opt_debug_exec; if ( optind < argc ) { int spl_argc = 0; struct spl_node *spl_argv = spl_get(0); spl_create(task, vm->root, "argv", spl_argv, SPL_CREATE_LOCAL); while (optind < argc) { spl_create(task, spl_argv, 0, SPL_NEW_STRING_DUP(argv[optind++]), SPL_CREATE_LOCAL); spl_argc++; } spl_create(task, vm->root, "argc", SPL_NEW_INT(spl_argc), SPL_CREATE_LOCAL); } if ( para_list ) { struct spl_node *pn = spl_get(0); spl_create(task, vm->root, "parameters", pn, SPL_CREATE_LOCAL); while (para_list) { struct parameter *p = para_list; spl_create(task, pn, p->name, p->value ? SPL_NEW_STRING_DUP(p->value) : spl_get(0), SPL_CREATE_LOCAL); para_list = p->next; free(p->name); free(p); } } if ( !opt_no_exec ) { #ifndef USEWIN32API struct rusage ru_before, ru_after; int ru_code_ip = 0; #endif spl_builtin_register_all(vm); while ( 1 ) { spl_gc_maybe(vm); task = spl_schedule(task); if ( !task ) break; if ( task->code_ip == breakpoint ) { fprintf(stderr, "** breakpoint reached **\n"); break; } #ifndef USEWIN32API if (bench_data_code) { getrusage(RUSAGE_SELF, &ru_before); ru_code_ip = task->code_ip; } #endif if ( !task->code || (rc = (opt_debugger ? spl_debug_exec : spl_exec)(task)) || got_sigint || opt_stepping) break; #ifndef USEWIN32API if (bench_data_code && ru_code_ip) { getrusage(RUSAGE_SELF, &ru_after); int ip = bench_data_code == task->code ? ru_code_ip : 0; int usec = (ru_after.ru_utime.tv_usec - ru_before.ru_utime.tv_usec) + ((ru_after.ru_utime.tv_sec - ru_before.ru_utime.tv_sec) * 1000000); bench_total_usec+=usec; bench_data_usec[ip]+=usec; bench_data_iter[ip]++; } #endif if ( opt_rccheck ) spl_rccheck(vm); if ( opt_dump_state > 1 ) { spl_dump_ascii(vm, stderr); fprintf(stderr, "----\n"); } } } if ( opt_dump_file ) { if (spl_dump_ascii(vm, opt_dump_file)) { fprintf(stderr, "Unable to dump VM state (see dumpfile for details).\n"); } else { if ( opt_stepping > 1 && task && task->code ) { rewind(opt_dump_file); opt_restore_file = opt_dump_file; spl_vm_destroy(vm); goto dump_reload_loop; } } fclose(opt_dump_file); } if ( opt_dump_state ) spl_dump_ascii(vm, stderr); if ( opt_treedump ) spl_treedump(vm, stderr, SPL_TREEDUMP_FLAG_ALL, 0); if ( ret != 1 ) fprintf(stderr, "Program returned %d.\n", ret); if (opt_debugger) spl_debug_reset(); if (bench_data_code) { char *sep = "-------+-------------------------------------+" "--------------------------------\n"; char title[100]; snprintf(title, 100, "%-6s | %4s %7s %9s %12s |\n", "", "%TM", "ITER", "USEC", "US/IT"); fprintf(opt_bench_file, "%s%s", title, sep); for (int i=0; isize; i++) { if (!bench_data_iter[i]) continue; fprintf(opt_bench_file, ":%-5d | %4.1f %7d %9d %12.2f | %-*s", i, (float)bench_data_usec[i]*100 / bench_total_usec, bench_data_iter[i], bench_data_usec[i], (float)bench_data_usec[i]/bench_data_iter[i], bench_data_code->code[i] < 0x60 ? 10 : 0, spl_asm_op2txt(bench_data_code->code[i])); if (bench_data_code->code[i] < 0x60) { int arg, arg_size = 4 - (bench_data_code->code[i] & 3); arg = spl_bytes_to_int(arg_size, bench_data_code->code+i+1); if (bench_data_code->code[i] < 0x20) fprintf(opt_bench_file, ":%d", arg+i+arg_size+1); else { fputc('"', opt_bench_file); for (int j = 0; (bench_data_code->code+i+arg+arg_size+1)[j]; j++) switch ((bench_data_code->code+i+arg+arg_size+1)[j]) { case '\\': fprintf(opt_bench_file, "\\\\"); break; case '\"': fprintf(opt_bench_file, "\\\""); break; case '\n': fprintf(opt_bench_file, "\\n"); break; case '\r': fprintf(opt_bench_file, "\\r"); break; case '\t': fprintf(opt_bench_file, "\\t"); break; default: fputc((bench_data_code->code+i+arg+arg_size+1)[j], opt_bench_file); } fputc('"', opt_bench_file); } } fprintf(opt_bench_file, "\n"); } fprintf(opt_bench_file, "%s", sep); spl_code_put(bench_data_code); free(bench_data_usec); free(bench_data_iter); bench_data_code = 0; } if (opt_bench_file) fclose(opt_bench_file); spl_vm_destroy(vm); if ( spl_state_counter_malloc_get() != spl_state_counter_free_get() ) fprintf(stderr, "Malloc/free counter unbalanced: %d / %d\n", spl_state_counter_malloc_get(), spl_state_counter_free_get()); else if ( opt_show_malloc_counter ) fprintf(stderr, "Malloc/free counter: %d / %d\n", spl_state_counter_malloc_get(), spl_state_counter_free_get()); return rc; }