* Moved strace sourced to src/bin/debug/.

* Factored a few functions out of strace that can be reused.
* Added the beginnings of a "profile" command. It is very much work in
  progress, though it is already able to profile the main thread of a
  program.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27533 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-09-15 13:45:43 +00:00
parent c7f27caff8
commit 8df6a8dbf5
19 changed files with 641 additions and 164 deletions

View File

@ -207,7 +207,6 @@ SubInclude HAIKU_TOP src bin screen_blanker ;
SubInclude HAIKU_TOP src bin screenmode ;
SubInclude HAIKU_TOP src bin sed ;
SubInclude HAIKU_TOP src bin sharutils ;
SubInclude HAIKU_TOP src bin strace ;
SubInclude HAIKU_TOP src bin unrar ;
SubInclude HAIKU_TOP src bin vim ;
SubInclude HAIKU_TOP src bin zic ;
@ -215,6 +214,9 @@ SubInclude HAIKU_TOP src bin zic ;
# RCS commands
SubInclude HAIKU_TOP src bin rcs ;
# debugging tools
SubInclude HAIKU_TOP src bin debug ;
# Network command line tools
SubInclude HAIKU_TOP src bin network ;

20
src/bin/debug/Jamfile Normal file
View File

@ -0,0 +1,20 @@
SubDir HAIKU_TOP src bin debug ;
UsePrivateHeaders debug ;
UsePrivateHeaders libroot ;
UsePrivateHeaders shared ;
UsePrivateSystemHeaders ;
StaticLibrary <bin>debug_utils.a : debug_utils.cpp ;
BinCommand profile
: profile.cpp
:
<bin>debug_utils.a
libdebug.so
$(TARGET_LIBSTDC++)
be
;
HaikuSubInclude strace ;

View File

@ -0,0 +1,183 @@
/*
* Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "debug_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string>
#include <debugger.h>
#include <libroot_private.h>
#include <syscalls.h>
extern const char* __progname;
static const char* kCommandName = __progname;
// find_program
static status_t
find_program(const char* programName, std::string& resolvedPath)
{
// If the program name is absolute, then there's nothing to do.
// If the program name consists of more than one path element, then we
// consider it a relative path and don't search in PATH either.
if (*programName == '/' || strchr(programName, '/')) {
resolvedPath = programName;
return B_OK;
}
// get the PATH environment variable
const char* paths = getenv("PATH");
if (!paths)
return B_ENTRY_NOT_FOUND;
// iterate through the paths
do {
const char* pathEnd = strchr(paths, ':');
int pathLen = (pathEnd ? pathEnd - paths : strlen(paths));
// We skip empty paths.
if (pathLen > 0) {
// get the program path
std::string path(paths, pathLen);
path += "/";
path += programName;
// stat() the path to be sure, there is a file
struct stat st;
if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
resolvedPath = path;
return B_OK;
}
}
paths = (pathEnd ? pathEnd + 1 : NULL);
} while (paths);
// not found in PATH
return B_ENTRY_NOT_FOUND;
}
// #pragma mark -
// load_program
thread_id
load_program(const char* const* args, int32 argCount, bool traceLoading)
{
// clone the argument vector so that we can change it
const char** mutableArgs = new const char*[argCount];
for (int i = 0; i < argCount; i++)
mutableArgs[i] = args[i];
// resolve the program path
std::string programPath;
status_t error = find_program(args[0], programPath);
if (error != B_OK) {
delete[] mutableArgs;
return error;
}
mutableArgs[0] = programPath.c_str();
// count environment variables
int envCount = 0;
while (environ[envCount] != NULL)
envCount++;
// flatten the program args and environment
char** flatArgs = NULL;
size_t flatArgsSize;
error = __flatten_process_args(mutableArgs, argCount, environ, envCount,
&flatArgs, &flatArgsSize);
// load the program
thread_id thread;
if (error == B_OK) {
thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount,
B_NORMAL_PRIORITY, (traceLoading ? 0 : B_WAIT_TILL_LOADED), -1, 0);
free(flatArgs);
} else
thread = error;
delete[] mutableArgs;
return thread;
}
// set_team_debugging_flags
void
set_team_debugging_flags(port_id nubPort, int32 flags)
{
debug_nub_set_team_flags message;
message.flags = flags;
while (true) {
status_t error = write_port(nubPort, B_DEBUG_MESSAGE_SET_TEAM_FLAGS,
&message, sizeof(message));
if (error == B_OK)
return;
if (error != B_INTERRUPTED) {
fprintf(stderr, "%s: Failed to set team debug flags: %s\n",
kCommandName, strerror(error));
exit(1);
}
}
}
// set_thread_debugging_flags
void
set_thread_debugging_flags(port_id nubPort, thread_id thread, int32 flags)
{
debug_nub_set_thread_flags message;
message.thread = thread;
message.flags = flags;
while (true) {
status_t error = write_port(nubPort, B_DEBUG_MESSAGE_SET_THREAD_FLAGS,
&message, sizeof(message));
if (error == B_OK)
return;
if (error != B_INTERRUPTED) {
fprintf(stderr, "%s: Failed to set thread debug flags: %s\n",
kCommandName, strerror(error));
exit(1);
}
}
}
// continue_thread
void
continue_thread(port_id nubPort, thread_id thread)
{
debug_nub_continue_thread message;
message.thread = thread;
message.handle_event = B_THREAD_DEBUG_HANDLE_EVENT;
message.single_step = false;
while (true) {
status_t error = write_port(nubPort, B_DEBUG_MESSAGE_CONTINUE_THREAD,
&message, sizeof(message));
if (error == B_OK)
return;
if (error != B_INTERRUPTED) {
fprintf(stderr, "%s: Failed to run thread %ld: %s\n",
kCommandName, thread, strerror(error));
exit(1);
}
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef BIN_DEBUG_DEBUG_UTILS_H
#define BIN_DEBUG_DEBUG_UTILS_H
#include <OS.h>
thread_id load_program(const char* const* args, int32 argCount,
bool traceLoading);
void set_team_debugging_flags(port_id nubPort, int32 flags);
void set_thread_debugging_flags(port_id nubPort, thread_id thread,
int32 flags);
void continue_thread(port_id nubPort, thread_id thread);
#endif // BIN_DEBUG_DEBUG_UTILS_H

403
src/bin/debug/profile.cpp Normal file
View File

@ -0,0 +1,403 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <debugger.h>
#include <OS.h>
#include <String.h>
#include <debug_support.h>
#include <ObjectList.h>
#include "debug_utils.h"
extern const char* __progname;
static const char* kCommandName = __progname;
class Symbol {
public:
Symbol(addr_t base, size_t size, const char* name)
:
base(base),
size(size),
name(name)
{
}
const char* Name() const { return name.String(); }
addr_t base;
size_t size;
BString name;
};
// TODO: Adjust!
static const char* kUsage =
"Usage: %s [ <options> ] <command line>\n"
"Executes the given command line <command line> and print an analysis of\n"
"the user and kernel times of all threads that ran during that time.\n"
"\n"
"Options:\n"
" -h, --help - Print this usage info.\n"
" -o <output> - Print the results to file <output>.\n"
" -s - Also perform a scheduling analysis over the time the\n"
" child process ran. This requires that scheduler kernel\n"
" tracing had been enabled at compile time.\n"
;
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, __progname);
exit(error ? 1 : 0);
}
/*
// get_id
static bool
get_id(const char *str, int32 &id)
{
int32 len = strlen(str);
for (int32 i = 0; i < len; i++) {
if (!isdigit(str[i]))
return false;
}
id = atol(str);
return true;
}
*/
int
main(int argc, const char* const* argv)
{
// const char* outputFile = NULL;
// bool schedulingAnalysis = false;
while (true) {
static struct option sLongOptions[] = {
{ "help", no_argument, 0, 'h' },
// { "output", required_argument, 0, 'o' },
{ 0, 0, 0, 0 }
};
opterr = 0; // don't print errors
int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage_and_exit(false);
break;
/* case 'o':
outputFile = optarg;
break;
case 's':
schedulingAnalysis = true;
break;
*/
default:
print_usage_and_exit(true);
break;
}
}
if (optind >= argc)
print_usage_and_exit(true);
const char* const* programArgs = argv + optind;
int programArgCount = argc - optind;
// get thread/team to be debugged
thread_id thread = -1;
team_id team = -1;
// if (programArgCount > 1
// || !get_id(*programArgs, (traceTeam ? team : thread))) {
// we've been given an executable and need to load it
thread = load_program(programArgs, programArgCount, false);
if (thread < 0) {
fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
programArgs[0], strerror(thread));
exit(1);
}
// }
// get the team ID, if we have none yet
if (team < 0) {
thread_info threadInfo;
status_t error = get_thread_info(thread, &threadInfo);
if (error != B_OK) {
fprintf(stderr, "%s: Failed to get info for thread %ld: %s\n",
kCommandName, thread, strerror(error));
exit(1);
}
team = threadInfo.team;
}
// create a debugger port
port_id debuggerPort = create_port(10, "debugger port");
if (debuggerPort < 0) {
fprintf(stderr, "%s: Failed to create debugger port: %s\n",
kCommandName, strerror(debuggerPort));
exit(1);
}
// install ourselves as the team debugger
port_id nubPort = install_team_debugger(team, debuggerPort);
if (nubPort < 0) {
fprintf(stderr, "%s: Failed to install team debugger: %s\n",
kCommandName, strerror(nubPort));
exit(1);
}
// init debug context
debug_context debugContext;
status_t error = init_debug_context(&debugContext, team, nubPort);
if (error != B_OK) {
fprintf(stderr, "%s: Failed to init debug context: %s\n",
kCommandName, strerror(error));
exit(1);
}
// create symbol lookup context
debug_symbol_lookup_context* lookupContext;
error = debug_create_symbol_lookup_context(&debugContext,
&lookupContext);
if (error != B_OK) {
fprintf(stderr, "%s: Failed to create symbol lookup context: %s\n",
kCommandName, strerror(error));
exit(1);
}
// iterate through the team's images and collect the symbols
BObjectList<Symbol> symbols(1000, true);
image_info imageInfo;
int32 cookie = 0;
while (get_next_image_info(team, &cookie, &imageInfo) == B_OK) {
printf("Loading symbols of image \"%s\" (%ld)...\n",
imageInfo.name, imageInfo.id);
// create symbol iterator
debug_symbol_iterator* iterator;
error = debug_create_image_symbol_iterator(lookupContext, imageInfo.id,
&iterator);
if (error != B_OK) {
printf("Failed to init symbol iterator: %s\n", strerror(error));
continue;
}
// iterate through the images
char symbolName[1024];
int32 symbolType;
void* symbolLocation;
size_t symbolSize;
while (debug_next_image_symbol(iterator, symbolName, sizeof(symbolName),
&symbolType, &symbolLocation, &symbolSize) == B_OK) {
//if (symbolSize == 0) {
// printf(" %s %p (%6lu) %s\n",
// symbolType == B_SYMBOL_TYPE_TEXT ? "text" : "data",
// symbolLocation, symbolSize, symbolName);
//}
if (symbolType == B_SYMBOL_TYPE_TEXT) {
Symbol* symbol = new(std::nothrow) Symbol(
(addr_t)symbolLocation, symbolSize, symbolName);
if (symbol == NULL || !symbols.AddItem(symbol)) {
fprintf(stderr, "%s: Out of memory\n", kCommandName);
exit(1);
}
}
}
debug_delete_image_symbol_iterator(iterator);
}
debug_delete_symbol_lookup_context(lookupContext);
// prepare the start profiler message
int32 symbolCount = symbols.CountItems();
if (symbolCount == 0) {
fprintf(stderr, "%s: Got no symbols at all, exiting...\n",
kCommandName);
exit(1);
}
size_t startProfilerSize = sizeof(debug_nub_start_profiler)
+ (symbolCount - 1) * sizeof(debug_profile_function);
debug_nub_start_profiler* startProfiler
= (debug_nub_start_profiler*)malloc(startProfilerSize);
if (startProfiler == NULL) {
fprintf(stderr, "%s: Out of memory\n", kCommandName);
exit(1);
}
startProfiler->reply_port = debugContext.reply_port;
startProfiler->thread = thread;
startProfiler->interval = 10;
startProfiler->function_count = symbolCount;
for (int32 i = 0; i < symbolCount; i++) {
Symbol* symbol = symbols.ItemAt(i);
debug_profile_function& function = startProfiler->functions[i];
function.base = symbol->base;
function.size = symbol->size;
}
// allocate memory for the reply
size_t maxMessageSize = max_c(sizeof(debug_debugger_message_data),
sizeof(debug_profiler_stopped) + 8 * symbolCount);
debug_debugger_message_data* message = (debug_debugger_message_data*)
malloc(maxMessageSize);
if (message == NULL) {
fprintf(stderr, "%s: Out of memory\n", kCommandName);
exit(1);
}
// set team debugging flags
int32 teamDebugFlags = 0;
set_team_debugging_flags(nubPort, teamDebugFlags);
// set thread debugging flags and start profiling
if (thread >= 0) {
int32 threadDebugFlags = 0;
// if (!traceTeam) {
// threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
// | (traceChildThreads
// ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
// }
set_thread_debugging_flags(nubPort, thread, threadDebugFlags);
// start profiling
debug_nub_start_profiler_reply reply;
error = send_debug_message(&debugContext, B_DEBUG_START_PROFILER,
startProfiler, startProfilerSize, &reply, sizeof(reply));
if (error != B_OK || (error = reply.error) != B_OK) {
fprintf(stderr, "%s: Failed to start profiler: %s\n",
kCommandName, strerror(error));
exit(1);
}
// resume the target thread to be sure, it's running
resume_thread(thread);
}
// debug loop
while (true) {
bool quitLoop = false;
int32 code;
ssize_t messageSize = read_port(debuggerPort, &code, message,
maxMessageSize);
if (messageSize < 0) {
if (messageSize == B_INTERRUPTED)
continue;
fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
kCommandName, strerror(messageSize));
exit(1);
}
switch (code) {
case B_DEBUGGER_MESSAGE_PROFILER_STOPPED:
printf("B_DEBUGGER_MESSAGE_PROFILER_STOPPED\n");
printf(" total ticks: %lld, missed: %lld\n",
message->profiler_stopped.total_ticks,
message->profiler_stopped.missed_ticks);
for (int32 i = 0; i < symbolCount; i++) {
int64 hits = message->profiler_stopped.function_ticks[i];
if (hits > 0)
printf("%10lld %s\n", hits, symbols.ItemAt(i)->Name());
}
break;
/*
typedef struct {
debug_origin origin;
int32 function_count;
int64 total_ticks; // total number of sample ticks
int64 missed_ticks; // ticks that didn't hit a function
int64 function_ticks[1]; // number of hits for each function
} debug_profiler_stopped;
*/
case B_DEBUGGER_MESSAGE_POST_SYSCALL:
case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
case B_DEBUGGER_MESSAGE_SINGLE_STEP:
case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
case B_DEBUGGER_MESSAGE_TEAM_CREATED:
case B_DEBUGGER_MESSAGE_THREAD_CREATED:
case B_DEBUGGER_MESSAGE_THREAD_DELETED:
case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
break;
case B_DEBUGGER_MESSAGE_TEAM_DELETED:
// the debugged team is gone
quitLoop = true;
break;
}
if (quitLoop)
break;
// tell the thread to continue (only when there is a thread and the
// message was synchronous)
if (message->origin.thread >= 0 && message->origin.nub_port >= 0)
continue_thread(message->origin.nub_port, message->origin.thread);
}
destroy_debug_context(&debugContext);
//kill_thread(thread);
//exit(0);
/*
typedef struct {
debug_origin origin;
int32 function_count;
int64 total_ticks; // total number of sample ticks
int64 missed_ticks; // ticks that didn't hit a function
int64 function_ticks[1]; // number of hits for each function
} debug_profiler_stopped;
struct debug_profile_function {
addr_t base; // function base address
size_t size; // function size
};
typedef struct {
port_id reply_port; // port to send the reply to
thread_id thread; // thread to profile
bigtime_t interval; // sample interval
int32 function_count; // number of functions we count hits for
struct debug_profile_function functions[1];
// functions that shall be tracked
} debug_nub_start_profiler;
*/
return 0;
}

View File

@ -1,4 +1,4 @@
SubDir HAIKU_TOP src bin strace ;
SubDir HAIKU_TOP src bin debug strace ;
UsePrivateHeaders device ;
UsePrivateHeaders drivers ;
@ -7,6 +7,8 @@ UsePrivateHeaders shared ;
UsePrivateHeaders net ;
UsePrivateSystemHeaders ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
# find headers generated by gensyscalls
SubDirHdrs $(TARGET_COMMON_DEBUG_LOCATE_TARGET) ;
@ -46,7 +48,11 @@ for i in $(straceSyscallsIndices) {
OPTIM = $(oldOptim) ;
BinCommand strace : $(straceSources)
: $(straceSyscallsObjects) libroot.so $(TARGET_LIBSTDC++) ;
:
$(straceSyscallsObjects)
<bin>debug_utils.a
$(TARGET_LIBSTDC++)
;
# We need to specify the dependency on the generated syscalls file explicitly.
Includes $(straceSyscallsSource) : <syscalls>strace_syscalls.h ;

View File

@ -16,9 +16,10 @@
#include <debugger.h>
#include <image.h>
#include <libroot_private.h>
#include <syscalls.h>
#include "debug_utils.h"
#include "Context.h"
#include "MemoryReader.h"
#include "Syscall.h"
@ -114,7 +115,7 @@ static const char *kSignalName[] = {
/* 22 */ "SIGTRAP",
/* 23 */ "SIGPOLL",
/* 24 */ "SIGPROF",
/* 25 */ "SIGSYS",
/* 25 */ "SIGSYS",
/* 26 */ "SIGURG",
/* 27 */ "SIGVTALRM",
/* 28 */ "SIGXCPU",
@ -161,164 +162,6 @@ get_id(const char *str, int32 &id)
return true;
}
// find_program
static
status_t
find_program(const char *programName, string &resolvedPath)
{
// If the program name is absolute, then there's nothing to do.
// If the program name consists of more than one path element, then we
// consider it a relative path and don't search in PATH either.
if (*programName == '/' || strchr(programName, '/')) {
resolvedPath = programName;
return B_OK;
}
// get the PATH environment variable
const char *paths = getenv("PATH");
if (!paths)
return B_ENTRY_NOT_FOUND;
// iterate through the paths
do {
const char *pathEnd = strchr(paths, ':');
int pathLen = (pathEnd ? pathEnd - paths : strlen(paths));
// We skip empty paths.
if (pathLen > 0) {
// get the program path
string path(paths, pathLen);
path += "/";
path += programName;
// stat() the path to be sure, there is a file
struct stat st;
if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
resolvedPath = path;
return B_OK;
}
}
paths = (pathEnd ? pathEnd + 1 : NULL);
} while (paths);
// not found in PATH
return B_ENTRY_NOT_FOUND;
}
// load_program
thread_id
load_program(const char *const *args, int32 argCount, bool traceLoading)
{
// clone the argument vector so that we can change it
const char **mutableArgs = new const char*[argCount];
for (int i = 0; i < argCount; i++)
mutableArgs[i] = args[i];
// resolve the program path
string programPath;
status_t error = find_program(args[0], programPath);
if (error != B_OK) {
delete[] mutableArgs;
return error;
}
mutableArgs[0] = programPath.c_str();
// count environment variables
int envCount = 0;
while (environ[envCount] != NULL)
envCount++;
// flatten the program args and environment
char** flatArgs = NULL;
size_t flatArgsSize;
error = __flatten_process_args(mutableArgs, argCount, environ, envCount,
&flatArgs, &flatArgsSize);
// load the program
thread_id thread;
if (error == B_OK) {
thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount,
B_NORMAL_PRIORITY, (traceLoading ? 0 : B_WAIT_TILL_LOADED), -1, 0);
free(flatArgs);
} else
thread = error;
delete[] mutableArgs;
return thread;
}
// set_team_debugging_flags
static
void
set_team_debugging_flags(port_id nubPort, int32 flags)
{
debug_nub_set_team_flags message;
message.flags = flags;
while (true) {
status_t error = write_port(nubPort, B_DEBUG_MESSAGE_SET_TEAM_FLAGS,
&message, sizeof(message));
if (error == B_OK)
return;
if (error != B_INTERRUPTED) {
fprintf(stderr, "%s: Failed to set team debug flags: %s\n",
kCommandName, strerror(error));
exit(1);
}
}
}
// set_thread_debugging_flags
static
void
set_thread_debugging_flags(port_id nubPort, thread_id thread, int32 flags)
{
debug_nub_set_thread_flags message;
message.thread = thread;
message.flags = flags;
while (true) {
status_t error = write_port(nubPort, B_DEBUG_MESSAGE_SET_THREAD_FLAGS,
&message, sizeof(message));
if (error == B_OK)
return;
if (error != B_INTERRUPTED) {
fprintf(stderr, "%s: Failed to set thread debug flags: %s\n",
kCommandName, strerror(error));
exit(1);
}
}
}
// continue_thread
static
void
continue_thread(port_id nubPort, thread_id thread)
{
debug_nub_continue_thread message;
message.thread = thread;
message.handle_event = B_THREAD_DEBUG_HANDLE_EVENT;
message.single_step = false;
while (true) {
status_t error = write_port(nubPort, B_DEBUG_MESSAGE_CONTINUE_THREAD,
&message, sizeof(message));
if (error == B_OK)
return;
if (error != B_INTERRUPTED) {
fprintf(stderr, "%s: Failed to run thread %ld: %s\n",
kCommandName, thread, strerror(error));
exit(1);
}
}
}
// get_syscall
Syscall *
get_syscall(const char *name)

View File

@ -102,7 +102,7 @@ MakeLocate <syscalls>syscalls.S.inc : [ FDirName $(dir) system libroot os ] ;
MakeLocate <syscalls>syscall_dispatcher.h : [ FDirName $(dir) system kernel ] ;
MakeLocate <syscalls>syscall_numbers.h : [ FDirName $(dir) system kernel ] ;
MakeLocate <syscalls>syscall_table.h : [ FDirName $(dir) system kernel ] ;
MakeLocate <syscalls>strace_syscalls.h : [ FDirName $(dir) bin strace ] ;
MakeLocate <syscalls>strace_syscalls.h : [ FDirName $(dir) bin debug strace ] ;
rule GenSyscallsFile {
Depends $(1) : gensyscalls ;