util: Pull most code out of m5.c.
By pulling the code out, this code can be tested by unit tests. Change-Id: I2d0510995d3e97d721f1de3024120f0c90b7a5ba Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/27547 Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Gabe Black <gabeblack@google.com> Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br> Maintainer: Gabe Black <gabeblack@google.com>
This commit is contained in:
@@ -28,9 +28,13 @@ import os
|
||||
Import('*')
|
||||
|
||||
# Raw source files.
|
||||
m5_mmap = 'm5_mmap.c'
|
||||
args = 'args.c'
|
||||
call_type = 'call_type.c'
|
||||
commands = 'commands.c'
|
||||
m5 = 'm5.c'
|
||||
m5_mmap = 'm5_mmap.c'
|
||||
usage = 'usage.c'
|
||||
|
||||
jni = 'jni_gem5Op.c'
|
||||
lua = 'lua_gem5Op.c'
|
||||
|
||||
@@ -65,7 +69,7 @@ libm5 = static_env.StaticLibrary('out/m5', [ m5_mmap ] + m5ops)
|
||||
#
|
||||
ct_support = list([ File('%s_call_type.c' % ct.name) for ct in call_types ])
|
||||
m5_bin = static_env.Program('out/m5',
|
||||
ct_support + [ args, m5, m5_mmap, libm5 ])
|
||||
ct_support + [ args, call_type, commands, m5, m5_mmap, libm5, usage ])
|
||||
|
||||
|
||||
# The shared version of the m5 op call sights, used by mutliple targets below.
|
||||
|
||||
@@ -42,17 +42,15 @@ M5OP_FOREACH
|
||||
};
|
||||
|
||||
int
|
||||
addr_call_type_detect(int *argc, char **argv[])
|
||||
addr_call_type_detect(Args *args)
|
||||
{
|
||||
static const char *prefix = "--addr";
|
||||
const size_t prefix_len = strlen(prefix);
|
||||
uint64_t addr_override;
|
||||
|
||||
// If the first argument starts with --addr...
|
||||
if (*argc > 0 && memcmp((*argv)[0], prefix, prefix_len) == 0) {
|
||||
char *argv0 = (*argv)[0];
|
||||
(*argc)--;
|
||||
(*argv)++;
|
||||
if (args->argc && memcmp(args->argv[0], prefix, prefix_len) == 0) {
|
||||
const char *argv0 = pop_arg(args);
|
||||
|
||||
// If there's more text in this argument...
|
||||
if (strlen(argv0) != prefix_len) {
|
||||
@@ -60,8 +58,9 @@ addr_call_type_detect(int *argc, char **argv[])
|
||||
if (argv0[prefix_len] != '=')
|
||||
return -1;
|
||||
// Attempt to extract an address after the '='.
|
||||
char *temp_argv[] = { &argv0[prefix_len + 1] };
|
||||
if (!parse_int_args(1, temp_argv, &addr_override, 1))
|
||||
const char *temp_argv[] = { &argv0[prefix_len + 1] };
|
||||
Args temp_args = { 1, temp_argv };
|
||||
if (!parse_int_args(&temp_args, &addr_override, 1))
|
||||
return -1;
|
||||
// If we found an address, use it to override m5op_addr.
|
||||
m5op_addr = addr_override;
|
||||
@@ -69,10 +68,8 @@ addr_call_type_detect(int *argc, char **argv[])
|
||||
}
|
||||
// If an address override wasn't part of the first argument, check if
|
||||
// it's the second argument. If not, then there's no override.
|
||||
if (*argc > 0 && parse_int_args(1, *argv, &addr_override, 1)) {
|
||||
if (args->argc && parse_int_args(args, &addr_override, 1)) {
|
||||
m5op_addr = addr_override;
|
||||
(*argc)--;
|
||||
(*argv)++;
|
||||
return 1;
|
||||
}
|
||||
// If the default address was zero, an override is required.
|
||||
|
||||
@@ -28,10 +28,11 @@
|
||||
#ifndef __ADDR_CALL_TYPE_H__
|
||||
#define __ADDR_CALL_TYPE_H__
|
||||
|
||||
#include "args.h"
|
||||
#include "dispatch_table.h"
|
||||
|
||||
// Returns 0 if not detected, 1 if detected successfully, and -1 on error.
|
||||
int addr_call_type_detect(int *argc, char **argv[]);
|
||||
int addr_call_type_detect(Args *args);
|
||||
DispatchTable *addr_call_type_init();
|
||||
|
||||
#endif // __ADDR_CALL_TYPE_H__
|
||||
|
||||
@@ -42,10 +42,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "args.h"
|
||||
|
||||
int
|
||||
parse_int_args(int argc, char *argv[], uint64_t ints[], int len)
|
||||
parse_int_args(Args *args, uint64_t ints[], int len)
|
||||
{
|
||||
if (argc > len)
|
||||
if (args->argc > len)
|
||||
return 0;
|
||||
|
||||
// On 32 bit platforms we need to use strtoull to do the conversion
|
||||
@@ -54,31 +56,33 @@ parse_int_args(int argc, char *argv[], uint64_t ints[], int len)
|
||||
#else
|
||||
#define strto64 strtoull
|
||||
#endif
|
||||
int i;
|
||||
for (i = 0; i < len; ++i)
|
||||
ints[i] = (i < argc) ? strto64(argv[i], NULL, 0) : 0;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
const char *arg = pop_arg(args);
|
||||
ints[i] = arg ? strto64(arg, NULL, 0) : 0;
|
||||
}
|
||||
|
||||
#undef strto64
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
pack_str_into_regs(const char *str, uint64_t regs[], int num_regs)
|
||||
pack_arg_into_regs(Args *args, uint64_t regs[], int num_regs)
|
||||
{
|
||||
const size_t RegSize = sizeof(regs[0]);
|
||||
const size_t MaxLen = num_regs * RegSize;
|
||||
const char *arg = pop_arg(args);
|
||||
|
||||
size_t len = strlen(str);
|
||||
memset(regs, 0, MaxLen);
|
||||
|
||||
size_t len = arg ? strlen(arg) : 0;
|
||||
|
||||
if (len > MaxLen)
|
||||
return 0;
|
||||
|
||||
memset(regs, 0, MaxLen);
|
||||
|
||||
while (len) {
|
||||
for (int offset = 0; offset < RegSize && len; offset++, len--) {
|
||||
int shift = offset * 8;
|
||||
*regs |= (uint64_t)(uint8_t)*str++ << shift;
|
||||
*regs |= (uint64_t)(uint8_t)*arg++ << shift;
|
||||
}
|
||||
regs++;
|
||||
}
|
||||
|
||||
@@ -41,9 +41,25 @@
|
||||
#ifndef __ARGS_H__
|
||||
#define __ARGS_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int parse_int_args(int argc, char *argv[], uint64_t ints[], int len);
|
||||
int pack_str_into_regs(const char *str, uint64_t regs[], int num_regs);
|
||||
typedef struct Args
|
||||
{
|
||||
int argc;
|
||||
const char **argv;
|
||||
} Args;
|
||||
|
||||
static inline const char *
|
||||
pop_arg(Args *args)
|
||||
{
|
||||
if (!args->argc)
|
||||
return NULL;
|
||||
args->argc--;
|
||||
return (args->argv++)[0];
|
||||
}
|
||||
|
||||
int parse_int_args(Args *args, uint64_t ints[], int len);
|
||||
int pack_arg_into_regs(Args *args, uint64_t regs[], int num_regs);
|
||||
|
||||
#endif // __ARGS_H__
|
||||
|
||||
64
util/m5/src/call_type.c
Normal file
64
util/m5/src/call_type.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met: redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer;
|
||||
* redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution;
|
||||
* neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "args.h"
|
||||
#include "call_type.h"
|
||||
#include "usage.h"
|
||||
|
||||
#if ENABLE_CT_addr
|
||||
#include "addr_call_type.h"
|
||||
#endif
|
||||
#if ENABLE_CT_inst
|
||||
#include "inst_call_type.h"
|
||||
#endif
|
||||
#if ENABLE_CT_semi
|
||||
#include "semi_call_type.h"
|
||||
#endif
|
||||
|
||||
#define default_call_type_init() \
|
||||
M5OP_MERGE_TOKENS(DEFAULT_CALL_TYPE, _call_type_init())
|
||||
|
||||
DispatchTable *
|
||||
init_call_type(Args *args)
|
||||
{
|
||||
# if ENABLE_CT_inst
|
||||
if (inst_call_type_detect(args))
|
||||
return inst_call_type_init();
|
||||
# endif
|
||||
# if ENABLE_CT_addr
|
||||
int detect = addr_call_type_detect(args);
|
||||
if (detect < 0)
|
||||
usage();
|
||||
if (detect > 0)
|
||||
return addr_call_type_init();
|
||||
# endif
|
||||
# if ENABLE_CT_semi
|
||||
if (semi_call_type_detect(args))
|
||||
return semi_call_type_init();
|
||||
# endif
|
||||
return default_call_type_init();
|
||||
}
|
||||
@@ -28,19 +28,9 @@
|
||||
#ifndef __CALL_TYPE_H__
|
||||
#define __CALL_TYPE_H__
|
||||
|
||||
#include "args.h"
|
||||
#include "dispatch_table.h"
|
||||
|
||||
#if ENABLE_CT_addr
|
||||
#include "addr_call_type.h"
|
||||
#endif
|
||||
#if ENABLE_CT_inst
|
||||
#include "inst_call_type.h"
|
||||
#endif
|
||||
#if ENABLE_CT_semi
|
||||
#include "semi_call_type.h"
|
||||
#endif
|
||||
|
||||
#define default_call_type_init() \
|
||||
M5OP_MERGE_TOKENS(DEFAULT_CALL_TYPE, _call_type_init())
|
||||
DispatchTable *init_call_type(Args *args);
|
||||
|
||||
#endif // __CALL_TYPE_H__
|
||||
|
||||
257
util/m5/src/commands.c
Normal file
257
util/m5/src/commands.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2005 The Regents of The University of Michigan
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met: redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer;
|
||||
* redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution;
|
||||
* neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "args.h"
|
||||
#include "commands.h"
|
||||
#include "usage.h"
|
||||
|
||||
static int
|
||||
read_file(DispatchTable *dt, int dest_fid)
|
||||
{
|
||||
uint8_t buf[256*1024];
|
||||
int offset = 0;
|
||||
int len, ret;
|
||||
|
||||
// Touch all buffer pages to ensure they are mapped in the
|
||||
// page table. This is required in the case of X86_FS, where
|
||||
// Linux does demand paging.
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
while ((len = (*dt->m5_read_file)(buf, sizeof(buf), offset)) > 0) {
|
||||
uint8_t *base = buf;
|
||||
offset += len;
|
||||
do {
|
||||
ret = write(dest_fid, base, len);
|
||||
if (ret < 0) {
|
||||
perror("Failed to write file");
|
||||
exit(2);
|
||||
} else if (ret == 0) {
|
||||
fprintf(stderr, "Failed to write file: "
|
||||
"Unhandled short write\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
base += ret;
|
||||
len -= ret;
|
||||
} while (len);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void
|
||||
write_file(DispatchTable *dt, const char *filename, const char *host_filename)
|
||||
{
|
||||
fprintf(stderr, "opening %s\n", filename);
|
||||
int src_fid = open(filename, O_RDONLY);
|
||||
|
||||
if (src_fid < 0) {
|
||||
fprintf(stderr, "error opening %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[256*1024];
|
||||
int offset = 0;
|
||||
int len;
|
||||
int bytes = 0;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
while ((len = read(src_fid, buf, sizeof(buf))) > 0) {
|
||||
bytes += (*dt->m5_write_file)(buf, len, offset, host_filename);
|
||||
offset += len;
|
||||
}
|
||||
fprintf(stderr, "written %d bytes\n", bytes);
|
||||
|
||||
close(src_fid);
|
||||
}
|
||||
|
||||
static void
|
||||
do_exit(DispatchTable *dt, Args *args)
|
||||
{
|
||||
if (args->argc > 1)
|
||||
usage();
|
||||
|
||||
uint64_t ints[1];
|
||||
if (!parse_int_args(args, ints, 1))
|
||||
usage();
|
||||
(*dt->m5_exit)(ints[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
do_fail(DispatchTable *dt, Args *args)
|
||||
{
|
||||
if (args->argc < 1 || args->argc > 2)
|
||||
usage();
|
||||
|
||||
uint64_t ints[2] = { 0, 0 };
|
||||
if (!parse_int_args(args, ints, args->argc))
|
||||
usage();
|
||||
(*dt->m5_fail)(ints[1], ints[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
do_reset_stats(DispatchTable *dt, Args *args)
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(args, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_reset_stats)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
do_dump_stats(DispatchTable *dt, Args *args)
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(args, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_dump_stats)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
do_dump_reset_stats(DispatchTable *dt, Args *args)
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(args, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_dump_reset_stats)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
do_read_file(DispatchTable *dt, Args *args)
|
||||
{
|
||||
if (args->argc > 0)
|
||||
usage();
|
||||
|
||||
read_file(dt, STDOUT_FILENO);
|
||||
}
|
||||
|
||||
static void
|
||||
do_write_file(DispatchTable *dt, Args *args)
|
||||
{
|
||||
if (args->argc != 1 && args->argc != 2)
|
||||
usage();
|
||||
|
||||
const char *filename = pop_arg(args);
|
||||
const char *host_filename = pop_arg(args);
|
||||
if (!host_filename)
|
||||
host_filename = filename;
|
||||
|
||||
write_file(dt, filename, host_filename);
|
||||
}
|
||||
|
||||
static void
|
||||
do_checkpoint(DispatchTable *dt, Args *args)
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(args, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_checkpoint)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
do_addsymbol(DispatchTable *dt, Args *args)
|
||||
{
|
||||
if (args->argc != 2)
|
||||
usage();
|
||||
|
||||
uint64_t addr = strtoul(pop_arg(args), NULL, 0);
|
||||
const char *symbol = pop_arg(args);
|
||||
(*dt->m5_add_symbol)(addr, symbol);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_loadsymbol(DispatchTable *dt, Args *args)
|
||||
{
|
||||
if (args->argc > 0)
|
||||
usage();
|
||||
|
||||
(*dt->m5_load_symbol)();
|
||||
}
|
||||
|
||||
static void
|
||||
do_initparam(DispatchTable *dt, Args *args)
|
||||
{
|
||||
if (args->argc > 1)
|
||||
usage();
|
||||
|
||||
uint64_t key_str[2];
|
||||
if (!pack_arg_into_regs(args, key_str, 2))
|
||||
usage();
|
||||
uint64_t val = (*dt->m5_init_param)(key_str[0], key_str[1]);
|
||||
printf("%"PRIu64, val);
|
||||
}
|
||||
|
||||
struct CommandInfo command_table[] = {
|
||||
{ "addsymbol", do_addsymbol, "<address> <symbol> // Adds a "
|
||||
"symbol with address \"address\" "
|
||||
"to gem5's symbol table" },
|
||||
{ "checkpoint", do_checkpoint, "[delay [period]] // After "
|
||||
"delay (default 0) take a "
|
||||
"checkpoint, and then optionally "
|
||||
"every period after" },
|
||||
{ "dumpresetstats", do_dump_reset_stats, "[delay [period]] // After "
|
||||
"delay (default 0) dump and "
|
||||
"reset the stats, and then "
|
||||
"optionally every period after" },
|
||||
{ "dumpstats", do_dump_stats, "[delay [period]] // After "
|
||||
"delay (default 0) dump the "
|
||||
"stats, and then optionally "
|
||||
"every period after" },
|
||||
{ "exit", do_exit, "[delay] // Exit after delay, "
|
||||
"or immediately" },
|
||||
{ "fail", do_fail, "<code> [delay] // Exit with "
|
||||
"failure code code after delay, "
|
||||
"or immediately" },
|
||||
{ "initparam", do_initparam, "[key] // optional key may be at "
|
||||
"most 16 characters long" },
|
||||
{ "loadsymbol", do_loadsymbol, "load a preselected symbol file "
|
||||
"into gem5's symbol table" },
|
||||
{ "readfile", do_read_file, "read a preselected file from "
|
||||
"the host and write it to "
|
||||
"stdout" },
|
||||
{ "resetstats", do_reset_stats, "[delay [period]] // After "
|
||||
"delay (default 0) reset the "
|
||||
"stats, and then optionally "
|
||||
"every period after" },
|
||||
{ "writefile", do_write_file, "<filename> [host filename] // "
|
||||
"Write a file to the host, "
|
||||
"optionally with a different "
|
||||
"name" },
|
||||
};
|
||||
|
||||
int num_commands = sizeof(command_table) / sizeof(CommandInfo);
|
||||
52
util/m5/src/commands.h
Normal file
52
util/m5/src/commands.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2005 The Regents of The University of Michigan
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met: redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer;
|
||||
* redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution;
|
||||
* neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __COMMANDS_H__
|
||||
#define __COMMANDS_H__
|
||||
|
||||
#include "args.h"
|
||||
#include "dispatch_table.h"
|
||||
|
||||
typedef struct CommandInfo
|
||||
{
|
||||
// The name of the command.
|
||||
char *name;
|
||||
// A function which processes command line arguments and passes them to
|
||||
// the underlying function through the dispatch table.
|
||||
void (*func)(DispatchTable *dt, Args *args);
|
||||
// Help text for this command.
|
||||
char *usage;
|
||||
} CommandInfo;
|
||||
|
||||
// The commands themselves.
|
||||
extern CommandInfo command_table[];
|
||||
|
||||
// The number of commands.
|
||||
extern int num_commands;
|
||||
|
||||
#endif // __COMMANDS_H__
|
||||
@@ -36,11 +36,10 @@ M5OP_FOREACH
|
||||
};
|
||||
|
||||
int
|
||||
inst_call_type_detect(int *argc, char **argv[])
|
||||
inst_call_type_detect(Args *args)
|
||||
{
|
||||
if (*argc > 0 && strcmp((*argv)[0], "--inst") == 0) {
|
||||
(*argc)--;
|
||||
(*argv)++;
|
||||
if (args->argc && strcmp(args->argv[0], "--inst") == 0) {
|
||||
pop_arg(args);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
#ifndef __INST_CALL_TYPE_H__
|
||||
#define __INST_CALL_TYPE_H__
|
||||
|
||||
#include "args.h"
|
||||
#include "dispatch_table.h"
|
||||
|
||||
int inst_call_type_detect(int *argc, char **argv[]);
|
||||
int inst_call_type_detect(Args *args);
|
||||
DispatchTable *inst_call_type_init();
|
||||
|
||||
#endif // __INST_CALL_TYPE_H__
|
||||
|
||||
329
util/m5/src/m5.c
329
util/m5/src/m5.c
@@ -38,337 +38,38 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gem5/asm/generic/m5ops.h>
|
||||
#include <gem5/m5ops.h>
|
||||
|
||||
#include "args.h"
|
||||
#include "call_type.h"
|
||||
#include "dispatch_table.h"
|
||||
#include "m5_mmap.h"
|
||||
|
||||
char *progname;
|
||||
char *command = "unspecified";
|
||||
void usage();
|
||||
#include "commands.h"
|
||||
#include "usage.h"
|
||||
|
||||
int
|
||||
read_file(DispatchTable *dt, int dest_fid)
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
uint8_t buf[256*1024];
|
||||
int offset = 0;
|
||||
int len, ret;
|
||||
Args args = { argc, argv };
|
||||
|
||||
// Touch all buffer pages to ensure they are mapped in the
|
||||
// page table. This is required in the case of X86_FS, where
|
||||
// Linux does demand paging.
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
while ((len = (*dt->m5_read_file)(buf, sizeof(buf), offset)) > 0) {
|
||||
uint8_t *base = buf;
|
||||
offset += len;
|
||||
do {
|
||||
ret = write(dest_fid, base, len);
|
||||
if (ret < 0) {
|
||||
perror("Failed to write file");
|
||||
exit(2);
|
||||
} else if (ret == 0) {
|
||||
fprintf(stderr, "Failed to write file: "
|
||||
"Unhandled short write\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
base += ret;
|
||||
len -= ret;
|
||||
} while (len);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void
|
||||
write_file(DispatchTable *dt, const char *filename, const char *host_filename)
|
||||
{
|
||||
fprintf(stderr, "opening %s\n", filename);
|
||||
int src_fid = open(filename, O_RDONLY);
|
||||
|
||||
if (src_fid < 0) {
|
||||
fprintf(stderr, "error opening %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[256*1024];
|
||||
int offset = 0;
|
||||
int len;
|
||||
int bytes = 0;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
while ((len = read(src_fid, buf, sizeof(buf))) > 0) {
|
||||
bytes += (*dt->m5_write_file)(buf, len, offset, host_filename);
|
||||
offset += len;
|
||||
}
|
||||
fprintf(stderr, "written %d bytes\n", bytes);
|
||||
|
||||
close(src_fid);
|
||||
}
|
||||
|
||||
void
|
||||
do_exit(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1)
|
||||
if (!args.argc)
|
||||
usage();
|
||||
|
||||
uint64_t ints[1];
|
||||
if (!parse_int_args(argc, argv, ints, 1))
|
||||
usage();
|
||||
(*dt->m5_exit)(ints[0]);
|
||||
}
|
||||
progname = pop_arg(&args);
|
||||
|
||||
void
|
||||
do_fail(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
if (argc < 1 || argc > 2)
|
||||
DispatchTable *dt = init_call_type(&args);
|
||||
|
||||
const char *command = pop_arg(&args);
|
||||
|
||||
if (!command)
|
||||
usage();
|
||||
|
||||
uint64_t ints[2] = {0,0};
|
||||
if (!parse_int_args(argc, argv, ints, argc))
|
||||
usage();
|
||||
(*dt->m5_fail)(ints[1], ints[0]);
|
||||
}
|
||||
|
||||
void
|
||||
do_reset_stats(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(argc, argv, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_reset_stats)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
void
|
||||
do_dump_stats(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(argc, argv, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_dump_stats)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
void
|
||||
do_dump_reset_stats(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(argc, argv, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_dump_reset_stats)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
void
|
||||
do_read_file(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
if (argc > 0)
|
||||
usage();
|
||||
|
||||
read_file(dt, STDOUT_FILENO);
|
||||
}
|
||||
|
||||
void
|
||||
do_write_file(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
if (argc != 1 && argc != 2)
|
||||
usage();
|
||||
|
||||
const char *filename = argv[0];
|
||||
const char *host_filename = (argc == 2) ? argv[1] : argv[0];
|
||||
|
||||
write_file(dt, filename, host_filename);
|
||||
}
|
||||
|
||||
void
|
||||
do_checkpoint(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
uint64_t ints[2];
|
||||
if (!parse_int_args(argc, argv, ints, 2))
|
||||
usage();
|
||||
(*dt->m5_checkpoint)(ints[0], ints[1]);
|
||||
}
|
||||
|
||||
void
|
||||
do_addsymbol(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
usage();
|
||||
|
||||
uint64_t addr = strtoul(argv[0], NULL, 0);
|
||||
char *symbol = argv[1];
|
||||
(*dt->m5_add_symbol)(addr, symbol);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
do_loadsymbol(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
if (argc > 0)
|
||||
usage();
|
||||
|
||||
(*dt->m5_load_symbol)();
|
||||
}
|
||||
|
||||
void
|
||||
do_initparam(DispatchTable *dt, int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1)
|
||||
usage();
|
||||
|
||||
uint64_t key_str[2];
|
||||
if (!pack_str_into_regs(argc == 0 ? "" : argv[0], key_str, 2))
|
||||
usage();
|
||||
uint64_t val = (*dt->m5_init_param)(key_str[0], key_str[1]);
|
||||
printf("%"PRIu64, val);
|
||||
}
|
||||
|
||||
struct MainFunc
|
||||
{
|
||||
char *name;
|
||||
void (*func)(DispatchTable *dt, int argc, char *argv[]);
|
||||
char *usage;
|
||||
};
|
||||
|
||||
struct MainFunc mainfuncs[] = {
|
||||
{ "addsymbol", do_addsymbol, "<address> <symbol> // Adds a "
|
||||
"symbol with address \"address\" "
|
||||
"to gem5's symbol table" },
|
||||
{ "checkpoint", do_checkpoint, "[delay [period]] // After "
|
||||
"delay (default 0) take a "
|
||||
"checkpoint, and then optionally "
|
||||
"every period after" },
|
||||
{ "dumpresetstats", do_dump_reset_stats, "[delay [period]] // After "
|
||||
"delay (default 0) dump and "
|
||||
"reset the stats, and then "
|
||||
"optionally every period after" },
|
||||
{ "dumpstats", do_dump_stats, "[delay [period]] // After "
|
||||
"delay (default 0) dump the "
|
||||
"stats, and then optionally "
|
||||
"every period after" },
|
||||
{ "exit", do_exit, "[delay] // Exit after delay, "
|
||||
"or immediately" },
|
||||
{ "fail", do_fail, "<code> [delay] // Exit with "
|
||||
"failure code code after delay, "
|
||||
"or immediately" },
|
||||
{ "initparam", do_initparam, "[key] // optional key may be at "
|
||||
"most 16 characters long" },
|
||||
{ "loadsymbol", do_loadsymbol, "load a preselected symbol file "
|
||||
"into gem5's symbol table" },
|
||||
{ "readfile", do_read_file, "read a preselected file from "
|
||||
"the host and write it to "
|
||||
"stdout" },
|
||||
{ "resetstats", do_reset_stats, "[delay [period]] // After "
|
||||
"delay (default 0) reset the "
|
||||
"stats, and then optionally "
|
||||
"every period after" },
|
||||
{ "writefile", do_write_file, "<filename> [host filename] // "
|
||||
"Write a file to the host, "
|
||||
"optionally with a different "
|
||||
"name" },
|
||||
};
|
||||
int numfuncs = sizeof(mainfuncs) / sizeof(mainfuncs[0]);
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [call type] <command> [arguments]\n", progname);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Call types:\n");
|
||||
# if ENABLE_CT_addr
|
||||
fprintf(stderr, " --addr %s%s\n",
|
||||
# if defined(M5OP_ADDR)
|
||||
"[address override]",
|
||||
# else
|
||||
"<address override>",
|
||||
# endif
|
||||
DEFAULT_CT_addr ? " (default)" : "");
|
||||
fprintf(stderr, " Use the address based invocation method.\n");
|
||||
# if defined(M5OP_ADDR)
|
||||
fprintf(stderr, " The default address is %#"PRIx64".\n",
|
||||
(uint64_t)M5OP_ADDR);
|
||||
# endif
|
||||
# endif
|
||||
# if ENABLE_CT_inst
|
||||
fprintf(stderr, " --inst%s\n", DEFAULT_CT_inst ? " (default)" : "");
|
||||
fprintf(stderr, " Use the instruction based invocation method.\n");
|
||||
# endif
|
||||
# if ENABLE_CT_semi
|
||||
fprintf(stderr, " --semi%s\n", DEFAULT_CT_semi ? " (default)" : "");
|
||||
fprintf(stderr, " Use the semi-hosting based invocation method.\n");
|
||||
# endif
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Commands:\n");
|
||||
for (int i = 0; i < numfuncs; ++i)
|
||||
fprintf(stderr, " %s %s\n", mainfuncs[i].name, mainfuncs[i].usage);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "All times in nanoseconds!\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
progname = argv[0];
|
||||
|
||||
argv++;
|
||||
argc--;
|
||||
|
||||
DispatchTable *dt = NULL;
|
||||
|
||||
# if ENABLE_CT_inst
|
||||
if (!dt && inst_call_type_detect(&argc, &argv)) {
|
||||
dt = inst_call_type_init();
|
||||
}
|
||||
# endif
|
||||
# if ENABLE_CT_addr
|
||||
if (!dt) {
|
||||
int detect = addr_call_type_detect(&argc, &argv);
|
||||
if (detect < 0)
|
||||
usage();
|
||||
if (detect > 0)
|
||||
dt = addr_call_type_init();
|
||||
}
|
||||
# endif
|
||||
# if ENABLE_CT_semi
|
||||
if (!dt && semi_call_type_detect(&argc, &argv)) {
|
||||
dt = semi_call_type_init();
|
||||
}
|
||||
# endif
|
||||
if (!dt)
|
||||
dt = default_call_type_init();
|
||||
|
||||
if (!argc)
|
||||
usage(1);
|
||||
|
||||
command = argv[0];
|
||||
|
||||
argv++;
|
||||
argc--;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < numfuncs; ++i) {
|
||||
if (strcmp(command, mainfuncs[i].name) != 0)
|
||||
for (int i = 0; i < num_commands; ++i) {
|
||||
if (strcmp(command, command_table[i].name) != 0)
|
||||
continue;
|
||||
|
||||
mainfuncs[i].func(dt, argc, argv);
|
||||
command_table[i].func(dt, &args);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
usage(1);
|
||||
usage();
|
||||
}
|
||||
|
||||
@@ -40,11 +40,10 @@ M5OP_FOREACH
|
||||
};
|
||||
|
||||
int
|
||||
semi_call_type_detect(int *argc, char **argv[])
|
||||
semi_call_type_detect(Args *args)
|
||||
{
|
||||
if (*argc > 0 && strcmp((*argv)[0], "--semi") == 0) {
|
||||
(*argc)--;
|
||||
(*argv)++;
|
||||
if (args->argc && strcmp(args->argv[0], "--semi") == 0) {
|
||||
pop_arg(args);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
#ifndef __SEMI_CALL_TYPE_H__
|
||||
#define __SEMI_CALL_TYPE_H__
|
||||
|
||||
#include "args.h"
|
||||
#include "dispatch_table.h"
|
||||
|
||||
int semi_call_type_detect(int *argc, char **argv[]);
|
||||
int semi_call_type_detect(Args *args);
|
||||
DispatchTable *semi_call_type_init();
|
||||
|
||||
#endif // __SEMI_CALL_TYPE_H__
|
||||
|
||||
88
util/m5/src/usage.c
Normal file
88
util/m5/src/usage.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2017 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2003-2005 The Regents of The University of Michigan
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met: redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer;
|
||||
* redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution;
|
||||
* neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "commands.h"
|
||||
#include "usage.h"
|
||||
|
||||
const char *progname = "{progname}";
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [call type] <command> [arguments]\n", progname);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Call types:\n");
|
||||
# if ENABLE_CT_addr
|
||||
fprintf(stderr, " --addr %s%s\n",
|
||||
# if defined(M5OP_ADDR)
|
||||
"[address override]",
|
||||
# else
|
||||
"<address override>",
|
||||
# endif
|
||||
DEFAULT_CT_addr ? " (default)" : "");
|
||||
fprintf(stderr, " Use the address based invocation method.\n");
|
||||
# if defined(M5OP_ADDR)
|
||||
fprintf(stderr, " The default address is %#"PRIx64".\n",
|
||||
(uint64_t)M5OP_ADDR);
|
||||
# endif
|
||||
# endif
|
||||
# if ENABLE_CT_inst
|
||||
fprintf(stderr, " --inst%s\n", DEFAULT_CT_inst ? " (default)" : "");
|
||||
fprintf(stderr, " Use the instruction based invocation method.\n");
|
||||
# endif
|
||||
# if ENABLE_CT_semi
|
||||
fprintf(stderr, " --semi%s\n", DEFAULT_CT_semi ? " (default)" : "");
|
||||
fprintf(stderr, " Use the semi-hosting based invocation method.\n");
|
||||
# endif
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Commands:\n");
|
||||
for (int i = 0; i < num_commands; ++i) {
|
||||
fprintf(stderr, " %s %s\n",
|
||||
command_table[i].name, command_table[i].usage);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "All times in nanoseconds!\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
48
util/m5/src/usage.h
Normal file
48
util/m5/src/usage.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2017 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2003-2005 The Regents of The University of Michigan
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met: redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer;
|
||||
* redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution;
|
||||
* neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __USAGE_H__
|
||||
#define __USAGE_H__
|
||||
|
||||
extern const char *progname;
|
||||
|
||||
void usage();
|
||||
|
||||
#endif // __USAGE_H__
|
||||
Reference in New Issue
Block a user