Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/main.c
Line
Count
Source (jump to first uncovered line)
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <moar.h>
5
6
#if MVM_TRACING
7
#  define TRACING_OPT "[--tracing] "
8
#  define TRACING_USAGE "\n    --tracing         output a line to stderr on every interpreter instr"
9
#else
10
#  define TRACING_OPT ""
11
#  define TRACING_USAGE ""
12
#endif
13
14
#ifdef HAVE_TELEMEH
15
#  define TELEMEH_USAGE "    MVM_TELEMETRY_LOG           Log internal events at high precision to this file\n"
16
#else
17
#  define TELEMEH_USAGE ""
18
#endif
19
20
#ifndef _WIN32
21
#  include "signal.h"
22
#endif
23
24
#ifndef _WIN32
25
#  include <unistd.h>
26
#else
27
#  include <process.h>
28
#endif
29
30
#ifdef _WIN32
31
#  define snprintf _snprintf
32
#endif
33
34
#if defined(_MSC_VER)
35
#define strtoll _strtoi64
36
#endif
37
38
/* flags need to be sorted alphabetically */
39
40
enum {
41
    NOT_A_FLAG = -2,
42
    UNKNOWN_FLAG = -1,
43
44
    FLAG_CRASH,
45
    FLAG_SUSPEND,
46
    FLAG_DUMP,
47
    FLAG_FULL_CLEANUP,
48
    FLAG_HELP,
49
    FLAG_TRACING,
50
    FLAG_VERSION,
51
52
    OPT_EXECNAME,
53
    OPT_LIBPATH,
54
    OPT_DEBUGPORT
55
};
56
57
static const char *const FLAGS[] = {
58
    "--crash",
59
    "--debug-suspend",
60
    "--dump",
61
    "--full-cleanup",
62
    "--help",
63
    "--tracing",
64
    "--version",
65
};
66
67
static const char USAGE[] = "\
68
USAGE: moar [--crash] [--libpath=...] " TRACING_OPT "input.moarvm [program args]\n\
69
       moar --dump input.moarvm\n\
70
       moar --help\n\
71
\n\
72
    --help            display this message\n\
73
    --dump            dump the bytecode to stdout instead of executing\n\
74
    --full-cleanup    try to free all memory and exit cleanly\n\
75
    --crash           abort instead of exiting on unhandled exception\n\
76
    --libpath         specify path loadbytecode should search in\n\
77
    --version         show version information\n\
78
    --debug-port=1234 listen for incoming debugger connections\n\
79
    --debug-suspend   pause execution at the entry point"
80
    TRACING_USAGE
81
    "\n\
82
\n\
83
The following environment variables are respected:\n\
84
\n\
85
    MVM_SPESH_DISABLE           Disables all dynamic optimization\n\
86
    MVM_SPESH_INLINE_DISABLE    Disables inlining\n\
87
    MVM_SPESH_OSR_DISABLE       Disables on-stack replacement\n\
88
    MVM_SPESH_BLOCKING          Blocks log-sending thread while specializer runs\n\
89
    MVM_SPESH_LOG               Specifies a dynamic optimizer log file\n\
90
    MVM_SPESH_NODELAY           Run dynamic optimization even for cold frames\n\
91
    MVM_SPESH_LIMIT             Limit the maximum number of specializations\n\
92
    MVM_JIT_DISABLE             Disables JITting to machine code\n\
93
    MVM_JIT_EXPR_DISABLE        Disable advanced 'expression' JIT\n\
94
    MVM_JIT_LOG                 Specifies a JIT-compiler log file\n\
95
    MVM_JIT_BYTECODE_DIR        Specifies a directory for JIT bytecode dumps\n\
96
    MVM_CROSS_THREAD_WRITE_LOG  Log unprotected cross-thread object writes to stderr\n\
97
    MVM_COVERAGE_LOG            Append (de-duped by default) line-by-line coverage messages to this file\n\
98
    MVM_COVERAGE_CONTROL        If set to 1, non-de-duping coverage started with nqp::coveragecontrol(1),\n\
99
                                  if set to 2, non-de-duping coverage started right away\n"
100
    TELEMEH_USAGE;
101
102
static int cmp_flag(const void *key, const void *value)
103
0
{
104
0
    return strcmp(key, *(char **)value);
105
0
}
106
107
0
static int starts_with(const char *str, const char *want) {
108
0
    size_t str_len  = strlen(str);
109
0
    size_t want_len = strlen(want);
110
0
    return str_len < want_len
111
0
        ? 0
112
0
        : strncmp(str, want, want_len) == 0;
113
0
}
114
115
static int parse_flag(const char *arg)
116
144
{
117
144
    const char *const *found;
118
144
119
144
    if (!arg || arg[0] != '-')
120
144
        return NOT_A_FLAG;
121
144
122
0
    found = bsearch(arg, FLAGS, sizeof FLAGS / sizeof *FLAGS, sizeof *FLAGS, cmp_flag);
123
0
124
0
    if (found)
125
0
        return (int)(found - FLAGS);
126
0
    else if (starts_with(arg, "--libpath="))
127
0
        return OPT_LIBPATH;
128
0
    else if (starts_with(arg, "--execname="))
129
0
        return OPT_EXECNAME;
130
0
    else if (starts_with(arg, "--debug-port="))
131
0
        return OPT_DEBUGPORT;
132
0
    else
133
0
        return UNKNOWN_FLAG;
134
0
}
135
136
#ifndef _WIN32
137
int main(int argc, char *argv[])
138
#else
139
int wmain(int argc, wchar_t *wargv[])
140
#endif
141
144
{
142
144
    MVMInstance *instance;
143
144
    const char  *input_file;
144
144
    const char  *executable_name = NULL;
145
144
    const char  *lib_path[8];
146
144
147
144
#ifdef _WIN32
148
    char **argv = MVM_UnicodeToUTF8_argv(argc, wargv);
149
#endif
150
144
151
144
    int dump         = 0;
152
144
    int full_cleanup = 0;
153
144
    int argi         = 1;
154
144
    int lib_path_i   = 0;
155
144
    int flag;
156
144
157
144
    unsigned int interval_id;
158
144
    char telemeh_inited = 0;
159
144
160
144
    MVMuint32 debugserverport = 0;
161
144
    int start_suspended = 0;
162
144
163
144
    for (; (flag = parse_flag(argv[argi])) != NOT_A_FLAG; ++argi) {
164
0
        switch (flag) {
165
0
            case FLAG_CRASH:
166
0
            MVM_crash_on_error();
167
0
            continue;
168
0
169
0
            case FLAG_DUMP:
170
0
            dump = 1;
171
0
            continue;
172
0
173
0
            case FLAG_FULL_CLEANUP:
174
0
            full_cleanup = 1;
175
0
            continue;
176
0
177
0
            case FLAG_HELP:
178
0
            puts(USAGE);
179
0
            return EXIT_SUCCESS;
180
0
181
0
#if MVM_TRACING
182
            case FLAG_TRACING:
183
            MVM_interp_enable_tracing();
184
            continue;
185
#endif
186
0
187
0
            case FLAG_SUSPEND:
188
0
            start_suspended = 1;
189
0
            continue;
190
0
191
0
            case OPT_EXECNAME:
192
0
            executable_name = argv[argi] + strlen("--execname=");
193
0
            continue;
194
0
195
0
            case OPT_LIBPATH:
196
0
            if (lib_path_i == 7) { /* 0..7 == 8 */
197
0
                fprintf(stderr, "ERROR: Only up to eight --libpath options are allowed.\n");
198
0
                return EXIT_FAILURE;
199
0
            }
200
0
201
0
            lib_path[lib_path_i++] = argv[argi] + strlen("--libpath=");
202
0
            continue;
203
0
204
0
            case FLAG_VERSION: {
205
0
            char *spesh_disable;
206
0
            char *jit_disable;
207
0
208
0
            printf("This is MoarVM version %s", MVM_VERSION);
209
0
            if (MVM_jit_support()) {
210
0
                printf(" built with JIT support");
211
0
212
0
                spesh_disable = getenv("MVM_SPESH_DISABLE");
213
0
                jit_disable = getenv("MVM_JIT_DISABLE");
214
0
                if (spesh_disable && strlen(spesh_disable) != 0) {
215
0
                    printf(" (disabled via MVM_SPESH_DISABLE)");
216
0
                } else if (jit_disable && strlen(jit_disable) != 0) {
217
0
                    printf(" (disabled via MVM_JIT_DISABLE)");
218
0
                }
219
0
            }
220
0
            printf("\n");
221
0
            return EXIT_SUCCESS;
222
0
            }
223
0
224
0
            case OPT_DEBUGPORT: {
225
0
                MVMint64 port;
226
0
                char *portstr = argv[argi] + strlen("--debugport=") + 1;
227
0
                char *endptr;
228
0
                port = strtoll(portstr, &endptr, 10);
229
0
                if (*endptr != '\0') {
230
0
                    fprintf(stderr, "ERROR: Invalid characters in debug port flag: %s\n", portstr);
231
0
                    return EXIT_FAILURE;
232
0
                }
233
0
                if (port <= 1024 || port > 65535) {
234
0
                    fprintf(stderr, "ERROR: debug server port out of range. We only accept ports above 1024 and below 65535. (got: %lu)\n", port);
235
0
                    return EXIT_FAILURE;
236
0
                }
237
0
                debugserverport = (MVMuint32)port;
238
0
                break;
239
0
            }
240
0
241
0
            default:
242
0
            fprintf(stderr, "ERROR: Unknown flag %s.\n\n%s\n", argv[argi], USAGE);
243
0
            return EXIT_FAILURE;
244
0
        }
245
0
    }
246
144
247
144
#ifdef HAVE_TELEMEH
248
    if (getenv("MVM_TELEMETRY_LOG")) {
249
        char path[256];
250
        FILE *fp;
251
        snprintf(path, 255, "%s.%d", getenv("MVM_TELEMETRY_LOG"),
252
#ifdef _WIN32
253
             _getpid()
254
#else
255
             getpid()
256
#endif
257
             );
258
        fp = fopen(path, "w");
259
        if (fp) {
260
            MVM_telemetry_init(fp);
261
            telemeh_inited = 1;
262
            interval_id = MVM_telemetry_interval_start(0, "moarvm startup");
263
        }
264
    }
265
#endif
266
144
267
144
    lib_path[lib_path_i] = NULL;
268
144
269
144
    if (argi >= argc) {
270
0
        fprintf(stderr, "ERROR: Missing input file.\n\n%s\n", USAGE);
271
0
        return EXIT_FAILURE;
272
0
    }
273
144
274
144
    instance   = MVM_vm_create_instance();
275
144
    input_file = argv[argi++];
276
144
277
144
    /* stash the rest of the raw command line args in the instance */
278
144
    MVM_vm_set_clargs(instance, argc - argi, argv + argi);
279
144
    MVM_vm_set_prog_name(instance, input_file);
280
144
    MVM_vm_set_exec_name(instance, executable_name);
281
144
    MVM_vm_set_lib_path(instance, lib_path_i, lib_path);
282
144
283
144
    /* Ignore SIGPIPE by default, since we error-check reads/writes. This does
284
144
     * not prevent users from setting up their own signal handler for SIGPIPE,
285
144
     * which will take precedence over this ignore. */
286
144
#ifndef _WIN32
287
144
    signal(SIGPIPE, SIG_IGN);
288
144
#endif
289
144
290
144
    if (debugserverport > 0) {
291
0
        MVM_debugserver_init(instance->main_thread, debugserverport);
292
0
293
0
        if (start_suspended) {
294
0
            instance->main_thread->gc_status = MVMGCStatus_INTERRUPT | MVMSuspendState_SUSPEND_REQUEST;
295
0
        }
296
0
    }
297
144
298
144
    if (dump) MVM_vm_dump_file(instance, input_file);
299
144
    else MVM_vm_run_file(instance, input_file);
300
144
301
144
#ifdef HAVE_TELEMEH
302
    if (getenv("MVM_TELEMETRY_LOG") && telemeh_inited) {
303
        MVM_telemetry_interval_stop(0, interval_id, "moarvm teardown");
304
        MVM_telemetry_finish();
305
    }
306
#endif
307
144
308
144
    if (full_cleanup) {
309
0
        MVM_vm_destroy_instance(instance);
310
0
        return EXIT_SUCCESS;
311
0
    }
312
144
    else {
313
144
        MVM_vm_exit(instance);
314
144
    }
315
144
}