* 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:
parent
c7f27caff8
commit
8df6a8dbf5
@ -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
20
src/bin/debug/Jamfile
Normal 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 ;
|
183
src/bin/debug/debug_utils.cpp
Normal file
183
src/bin/debug/debug_utils.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
20
src/bin/debug/debug_utils.h
Normal file
20
src/bin/debug/debug_utils.h
Normal 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
403
src/bin/debug/profile.cpp
Normal 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;
|
||||
}
|
@ -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 ;
|
@ -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)
|
@ -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 ;
|
||||
|
Loading…
Reference in New Issue
Block a user