1
0
forked from 0ad/0ad

Fixes for the Mac.

# The engine now builds and runs on Mac OS X.

Most changes were localized to the sysdep/ folder and the build system.
This
is what porting should be like! Hopefully nothing breaks on the other
platforms though ;-)

This was SVN commit r5081.
This commit is contained in:
Simon Brenner 2007-05-17 03:37:49 +00:00
parent b01acce45f
commit 7d4af5979b
15 changed files with 538 additions and 412 deletions

View File

@ -9,6 +9,18 @@
-- directory in which all library subdirectories reside.
libraries_dir = "../../../libraries/"
local function add_extern_lib_paths(extern_lib)
-- Add '<libraries root>/<libraryname>/lib' and '/include' to the includepaths and libpaths
-- Often, the headers in libraries/ are windows-specific (always, except
-- for cxxtest and fcollada). So don't add the include dir unless on
-- windows or processing one of those libs.
if OS == "windows" or extern_lib == 'cxxtest' or extern_lib == 'fcollada' then
tinsert(package.includepaths, libraries_dir .. extern_lib .. "/include")
end
tinsert(package.libpaths, libraries_dir .. extern_lib .. "/lib")
end
-- library definitions
-- in a perfect world, libraries would have a common installation template,
@ -85,6 +97,7 @@ extern_lib_defs = {
openal = {
win_names = { "openal32" },
unix_names = { "openal" },
osx_frameworks = { "OpenAL" },
dbg_suffix = "",
},
opengl = {
@ -127,7 +140,13 @@ extern_lib_defs = {
},
sdl = {
unix_names = { "SDL" },
add_func = function()
add_extern_lib_paths("sdl")
if OS ~= "windows" then
tinsert(package.buildoptions, "`sdl-config --cflags`")
tinsert(package.linkoptions, "`sdl-config --libs`")
end
end
}
}
@ -159,14 +178,9 @@ local function add_delayload(name, suffix, def)
end
local function add_extern_lib(extern_lib, def)
-- Add '<libraries root>/<libraryname>/lib' and '/include' to the includepaths and libpaths
if OS ~= "linux" or extern_lib == 'cxxtest' or extern_lib == 'fcollada' then
tinsert(package.includepaths, libraries_dir .. extern_lib .. "/include")
end
tinsert(package.libpaths, libraries_dir .. extern_lib .. "/lib")
add_extern_lib_paths(extern_lib)
-- careful: make sure to only use *_names when on the correct platform.
local names = {}
@ -192,13 +206,21 @@ local function add_extern_lib(extern_lib, def)
suffix = ""
end
for i,name in names do
tinsert(package.config["Debug" ].links, name .. suffix)
-- 'Testing' config uses 'Debug' DLLs
tinsert(package.config["Testing"].links, name .. suffix)
tinsert(package.config["Release"].links, name)
add_delayload(name, suffix, def)
-- OS X "Frameworks" need to be added in a special way to the link
-- i.e. by linkoptions += "-framework ..."
if OS == "macosx" and def.osx_frameworks then
for i,name in def.osx_frameworks do
tinsert(package.linkoptions, "-framework " .. name)
end
else
for i,name in names do
tinsert(package.config["Debug" ].links, name .. suffix)
-- 'Testing' config uses 'Debug' DLLs
tinsert(package.config["Testing"].links, name .. suffix)
tinsert(package.config["Release"].links, name)
add_delayload(name, suffix, def)
end
end
end

View File

@ -118,16 +118,26 @@ function package_set_build_flags()
"-fvisibility-inlines-hidden",
})
-- Include and lib paths:
-- X11 includes may be installed in one of a gaszillion of three places
-- And MacPorts uses /opt/local as its prefix
-- Famous last words: "You can't include too much! ;-)"
package.includepaths = {
"/usr/X11R6/include/X11",
"/usr/X11R6/include",
"/usr/include/X11",
"/opt/local/include",
}
package.libpaths = {
"/usr/X11R6/lib",
"/usr/i686-pc-linux-gnu/lib", -- needed for ICC to find libbfd
"/opt/local/lib",
"/usr/X11R6/lib"
}
if OS=="linux" and options["icc"] then
tinsert(package.libpaths,
"/usr/i686-pc-linux-gnu/lib") -- needed for ICC to find libbfd
end
package.defines = {
"__STDC_VERSION__=199901L",
-- "CONFIG_USE_MMGR",
}
end
@ -385,7 +395,7 @@ function setup_all_libs ()
linux = { "lib/sysdep/unix" },
-- note: RC file must be added to main_exe package.
windows = { "lib/sysdep/win", "lib/sysdep/win/wposix" },
macosx = { "lib/sysdep/osx" },
macosx = { "lib/sysdep/osx", "lib/sysdep/unix" },
}
tinsert(package.files, sourcesfromdirs(source_root, sysdep_dirs[OS]));
end

View File

@ -311,8 +311,16 @@ int gnu_cpp()
if (!prj_is_kind("cxxtestgen"))
{
/* Include the automatically generated dependency lists */
io_print("-include $(OBJECTS:%%.o=%%.d)\n\n");
io_print("-include $(PCHS:%%.%s=%%.d)\n\n", pchExt);
/* But skip that if OBJECTS would have been empty - make doesn't like
* empty include lists */
if (*prj_get_files())
io_print("-include $(OBJECTS:%%.o=%%.d)\n\n");
/* Include the dependency list for the precompiled header. But skip
* it if we're not doing PCH's, to get rid of "no file name for
* -include" warnings from make */
if (prj_get_pch_source() != NULL)
io_print("-include $(PCHS:%%.%s=%%.d)\n\n", pchExt);
}
io_closefile();
@ -477,12 +485,17 @@ static const char* listCppTargets(const char* name)
strcat(input_dir, "/");
opts = "";
if (!os_is("windows"))
if (!os_is("windows") && !os_is("macosx"))
opts = "-dDONT_USE_UNDERLINE=1 ";
strcat(g_buffer, "nasm "); strcat(g_buffer, opts);
strcat(g_buffer, " -i"); strcat(g_buffer,input_dir );
strcat(g_buffer, " -f elf -o $@ $<\n");
strcat(g_buffer, " -f ");
if (os_is("macosx"))
strcat(g_buffer, "macho");
else
strcat(g_buffer, "elf");
strcat(g_buffer, " -o $@ $<\n");
strcat(g_buffer, "\t");
if (!g_verbose)

View File

@ -167,7 +167,7 @@ void write_be64(void* p, u64 x)
u64 movzx_le64(const u8* p, size_t size_bytes)
{
u64 number = 0;
for(uint i = 0; i < std::min(size_bytes, 8u); i++)
for(uint i = 0; i < std::min(size_bytes, (size_t)8u); i++)
number |= ((u64)p[i]) << (i*8);
return number;
@ -176,7 +176,7 @@ u64 movzx_le64(const u8* p, size_t size_bytes)
u64 movzx_be64(const u8* p, size_t size_bytes)
{
u64 number = 0;
for(uint i = 0; i < std::min(size_bytes, 8u); i++)
for(uint i = 0; i < std::min(size_bytes, (size_t)8u); i++)
{
number <<= 8;
number |= p[i];

View File

@ -408,7 +408,7 @@
// safe CRT functions: strcpy_s, fopen_s, etc.
// these are always available to users: if not provided by the CRT, we
// implement them ourselves. this flag is only used to skip our impl.
#if MSC_VERSION >= 1400 || GCC_VERSION
#if MSC_VERSION >= 1400
# define HAVE_SECURE_CRT 1
#else
# define HAVE_SECURE_CRT 0

View File

@ -6,7 +6,11 @@
#include <algorithm>
#include <cassert>
#ifdef __APPLE__
#include <OpenAL/al.h>
#else
#include <AL/al.h>
#endif
#include <vorbis/vorbisfile.h>
#include "ogghack.h" // HACK: must be included after al.h (bad interface)

View File

@ -222,7 +222,9 @@ int tprintf_s(tchar* buf, size_t max_chars, const tchar* fmt, ...)
return len;
}
#if OS_WIN || !defined(WSECURE_CRT)
// FIXME this doesn't work in the wchar_t version, for the platforms where it's
// supposed to do good, since wfopen is microsoft-specific.
errno_t tfopen_s(FILE** pfile, const tchar* filename, const tchar* mode)
{
*pfile = NULL;
@ -232,5 +234,6 @@ errno_t tfopen_s(FILE** pfile, const tchar* filename, const tchar* mode)
*pfile = file;
return 0;
}
#endif
#endif // #if !HAVE_SECURE_CRT

View File

@ -17,7 +17,7 @@
# include "lib/sysdep/ia32/ia32.h"
# include "lib/sysdep/ia32/ia32_memcpy.h"
#endif
#if OS_BSD
#if OS_BSD || OS_MACOSX
# include "lib/sysdep/unix/bsd.h"
#endif
#if OS_WIN
@ -249,6 +249,10 @@ LibError cpu_CallByEachCPU(CpuCallback cb, void* param)
{
#if OS_WIN
return wcpu_CallByEachCPU(cb, param);
#else
UNUSED2(cb);
UNUSED2(param);
return ERR::NOT_IMPLEMENTED;
#endif
}
@ -309,4 +313,4 @@ size_t cpu_MemorySize(CpuMemoryIndicators mem_type)
#else
return bsd_MemorySize(mem_type);
#endif
}
}

View File

@ -1,8 +1,8 @@
; set section attributes
section .data data align=32 use32
section .bss bss align=16 use32
section .text code align=64 use32
; section .data data align=32 use32
; section .bss bss align=16 use32
; section .text code align=64 use32
; activate .text (needs to be separate because __SECT__ will otherwise
; complain that the above definition is redeclaring attributes)
section .text

View File

@ -1,15 +1,17 @@
#include "precompiled.h"
#include "bsd.h"
#if OS_BSD
#if OS_BSD || OS_MACOSX
#include <sys/sysctl.h>
static int SysctlFromMemType(CpuMemoryIndicators mem_type)
{
switch(mem_type)
{
case MEM_TOTAL:
case CPU_MEM_TOTAL:
return HW_PHYSMEM;
case MEM_AVAILABLE:
case CPU_MEM_AVAILABLE:
return HW_USERMEM;
}
UNREACHABLE;
@ -19,7 +21,8 @@ size_t bsd_MemorySize(CpuMemoryIndicators mem_type)
{
size_t memory_size = 0;
size_t len = sizeof(memory_size);
const int mib[2] = { CTL_HW, SysctlFromMemType(mem_type) };
// Argh, the API doesn't seem to be const-correct
/*const*/ int mib[2] = { CTL_HW, SysctlFromMemType(mem_type) };
sysctl(mib, 2, &memory_size, &len, 0, 0);
return memory_size;
}

View File

@ -5,8 +5,28 @@
#include "lib/res/file/file.h"
#include "lib/sysdep/sysdep.h"
#include "lib/sysdep/dir_watch.h"
#include "ps/CLogger.h"
#if !OS_LINUX
LibError dir_add_watch(const char * const n_full_path, intptr_t* const watch)
{
return INFO::OK;
}
LibError dir_cancel_watch(const intptr_t watch)
{
return INFO::OK;
}
LibError dir_get_changed_file(char *)
{
return INFO::OK;
}
#else
#include <fam.h>
static FAMConnection fc;
@ -73,7 +93,7 @@ LibError dir_cancel_watch(const intptr_t watch)
return INFO::OK;
}
int dir_get_changed_file(char* fn)
LibError dir_get_changed_file(char* fn)
{
if(initialized == -1)
return ERR::FAIL; // NOWARN
@ -99,3 +119,4 @@ int dir_get_changed_file(char* fn)
// just nothing new; try again later
return ERR::AGAIN; // NOWARN
}
#endif

View File

@ -2,6 +2,10 @@
#include "ucpu.h"
#if OS_MACOSX
#include <sys/sysctl.h>
#endif
int ucpu_IsThrottlingPossible()
{
return -1; // don't know
@ -9,11 +13,21 @@ int ucpu_IsThrottlingPossible()
int ucpu_NumPackages()
{
#if OS_MACOSX
int mib[]={CTL_HW, HW_NCPU};
int ncpus;
size_t len = sizeof(ncpus);
if (sysctl(mib, 2, &ncpus, &len, NULL, 0) == -1)
return -1; // don't know
else
return ncpus;
#else
long res = sysconf(_SC_NPROCESSORS_CONF);
if (res == -1)
return 1;
else
return (int)res;
#endif
}
double ucpu_ClockFrequency()

View File

@ -1,77 +1,44 @@
/* udbg.cpp
This file contains debug helpers that are common for all unix systems. See
udbg_bfd.cpp for the linux-specific stuff (Using BFD and backtrace() for symbol
lookups and backtraces)
*/
#include "precompiled.h"
#include "lib/timer.h"
#include "lib/sysdep/sysdep.h"
#include "lib/debug.h"
#include <stdarg.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <cassert>
#include <bfd.h>
#include <cxxabi.h>
#ifndef bfd_get_section_size
#define bfd_get_section_size bfd_get_section_size_before_reloc
#endif
#if OS_LINUX
#define GNU_SOURCE
#include <dlfcn.h>
#include <execinfo.h>
#endif
#if OS_LINUX
# define DEBUGGER_WAIT 3
# define DEBUGGER_CMD "gdb"
# define DEBUGGER_ARG_FORMAT "--pid=%d"
# define DEBUGGER_BREAK_AFTER_WAIT 0
#else
# error "port"
#endif
#define PROFILE_RESOLVE_SYMBOL 0
// Hard-coded - yuck :P
// These should only be used as fallbacks
#if defined(TESTING)
#define EXE_NAME "pyrogenesis_test"
#elif defined(NDEBUG)
#define EXE_NAME "pyrogenesis"
#else
#define EXE_NAME "pyrogenesis_dbg"
#endif
struct symbol_file_context
#if !OS_LINUX
// udbg stubs implementation. This is what's used on non-linux, so far.
void* debug_get_nth_caller(uint UNUSED(n), void *UNUSED(context))
{
asymbol **syms;
bfd *abfd;
};
symbol_file_context ps_dbg_context;
bool udbg_initialized=false;
return NULL;
}
struct symbol_lookup_context
LibError debug_dump_stack(wchar_t* UNUSED(buf), size_t UNUSED(max_chars), uint UNUSED(skip), void* UNUSED(context))
{
symbol_file_context *file_ctx;
return ERR::NOT_IMPLEMENTED;
}
bfd_vma address;
const char* symbol;
const char* filename;
uint line;
bool found;
};
LibError debug_resolve_symbol(void* UNUSED(ptr_of_interest), char* UNUSED(sym_name), char* UNUSED(file), int* UNUSED(line))
{
return ERR::NOT_IMPLEMENTED;
}
#endif
void unix_debug_break()
{
kill(getpid(), SIGTRAP);
}
#define DEBUGGER_WAIT 3
#define DEBUGGER_CMD "gdb"
#define DEBUGGER_ARG_FORMAT "--pid=%d"
#define DEBUGGER_BREAK_AFTER_WAIT 0
/*
Start the debugger and tell it to attach to the current process/thread
(called by display_error)
@ -105,329 +72,12 @@ void udbg_launch_debugger()
}
}
void* debug_get_nth_caller(uint n, void *UNUSED(context))
{
// bt[0] == debug_get_nth_caller
// bt[1] == caller of get_nth_caller
// bt[2] == 1:st caller (n==1)
void *bt[n+2];
int bt_size;
bt_size=backtrace(bt, n+2);
assert(bt_size >= (int)(n+2) && "Need at least n+2 frames in get_nth_caller");
return bt[n+1]; // n==1 => bt[2], and so forth
}
LibError debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* UNUSED(context))
{
++skip; // Skip ourselves too
// bt[0..skip] == skipped
// bt[skip..N_FRAMES+skip] == print
static const uint N_FRAMES = 16;
void *bt[skip+N_FRAMES];
int bt_size;
wchar_t *bufpos = buf;
wchar_t *bufend = buf + max_chars;
bt_size=backtrace(bt, ARRAY_SIZE(bt));
// did we get enough backtraced frames?
assert((bt_size >= (int)skip) && "Need at least <skip> frames in the backtrace");
// Assumed max length of a single print-out
static const uint MAX_OUT_CHARS=1024;
for (uint i=skip;(int)i<bt_size && bufpos+MAX_OUT_CHARS < bufend;i++)
{
char file[DBG_FILE_LEN];
char symbol[DBG_SYMBOL_LEN];
int line;
int len;
if (debug_resolve_symbol(bt[i], symbol, file, &line) == 0)
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x) %hs:%d %hs\n", bt[i], file, line, symbol);
else
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x)\n", bt[i]);
if (len < 0)
{
// MAX_OUT_CHARS exceeded, realistically this was caused by some
// mindbogglingly long symbol name... replace the end with an
// ellipsis and a newline
memcpy(&bufpos[MAX_OUT_CHARS-6], L"...\n", 5*sizeof(wchar_t));
len = MAX_OUT_CHARS;
}
bufpos += len;
}
return INFO::OK;
}
static int slurp_symtab(symbol_file_context *ctx)
{
bfd *abfd=ctx->abfd;
asymbol ***syms=&ctx->syms;
long symcount;
int size=0;
if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0)
{
printf("slurp_symtab(): Huh? Has no symbols...\n");
return -1;
}
size = bfd_get_symtab_upper_bound(abfd);
if (size < 0)
{
bfd_perror("symtab_upper_bound");
return -1;
}
*syms = (asymbol **)malloc(size);
if (!syms)
return -1;
symcount = bfd_canonicalize_symtab(abfd, *syms);
if (symcount == 0)
symcount = bfd_read_minisymbols (abfd, FALSE, (void **)syms, (uint *)&size);
if (symcount == 0)
symcount = bfd_read_minisymbols (abfd, TRUE /* dynamic */, (void **)syms, (uint *)&size);
if (symcount < 0)
{
bfd_perror("slurp_symtab");
return -1;
}
return 0;
}
static int read_symbols(const char *file_name, symbol_file_context *ctx)
{
char **matching=NULL;
ONCE(bfd_init());
ctx->abfd = bfd_openr (file_name, NULL);
if (ctx->abfd == NULL)
{
bfd_perror("udbg.cpp: bfd_openr");
return -1;
}
if (! bfd_check_format_matches (ctx->abfd, bfd_object, &matching))
{
if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
{
printf("Error reading symbols from %s: ambiguous format\n", file_name);
while (*matching)
printf("\tPotential matching format: %s\n", *matching++);
free(matching);
}
else
{
bfd_perror("bfd_check_format_matches");
}
return -1;
}
int res=slurp_symtab(ctx);
if (res == 0)
return res;
else
{
bfd_perror("udbg.cpp: slurp_symtab");
bfd_close(ctx->abfd);
return -1;
}
}
void udbg_init(void)
{
char n_path[PATH_MAX];
char *exename=n_path;
if (sys_get_executable_name(n_path, sizeof(n_path)) != INFO::OK)
{
debug_printf("sys_get_executable_name didn't work, using hard-coded guess %s.\n", EXE_NAME);
exename=EXE_NAME;
}
debug_printf("udbg_init: loading symbols from %s.\n", exename);
if (read_symbols(exename, &ps_dbg_context)==0)
udbg_initialized=true;
#if PROFILE_RESOLVE_SYMBOL
{
TIMER(udbg_init_benchmark)
char symbol[DBG_SYMBOL_LEN];
char file[DBG_FILE_LEN];
int line;
debug_resolve_symbol(debug_get_nth_caller(3), symbol, file, &line);
printf("%s (%s:%d)\n", symbol, file, line);
for (int i=0;i<1000000;i++)
{
debug_resolve_symbol(debug_get_nth_caller(1), symbol, file, &line);
}
}
#endif
}
static void find_address_in_section (bfd *abfd, asection *section, void *data)
{
symbol_lookup_context *ctx=(symbol_lookup_context *)data;
asymbol **syms=ctx->file_ctx->syms;
bfd_vma pc=ctx->address;
bfd_vma vma;
bfd_size_type size;
if (ctx->found) return;
if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0)
return;
vma = bfd_get_section_vma (abfd, section);
if (pc < vma)
return;
size = bfd_get_section_size (section);
if (pc >= vma + size)
return;
ctx->found = bfd_find_nearest_line (abfd, section, syms,
pc - vma, &ctx->filename, &ctx->symbol, &ctx->line);
}
// BFD functions perform allocs with real malloc - we need to free that data
#include "lib/nommgr.h"
void demangle_buf(char *buf, const char *symbol, size_t n)
{
int status=0;
char *alloc=NULL;
if (symbol == NULL || *symbol == '\0')
{
symbol = "??";
status = -1;
}
else
{
alloc=abi::__cxa_demangle(symbol, NULL, NULL, &status);
}
// status is 0 on success and a negative value on failure
if (status == 0)
symbol=alloc;
strncpy(buf, symbol, n);
buf[n-1]=0;
if (alloc)
free(alloc);
}
static LibError debug_resolve_symbol_dladdr(void *ptr, char* sym_name, char* file, int* line)
{
Dl_info syminfo;
int res=dladdr(ptr, &syminfo);
if (res == 0)
WARN_RETURN(ERR::FAIL);
if (sym_name)
{
if (syminfo.dli_sname)
demangle_buf(sym_name, syminfo.dli_sname, DBG_SYMBOL_LEN);
else
{
snprintf(sym_name, DBG_SYMBOL_LEN, "0x%08x", (uint)ptr);
sym_name[DBG_SYMBOL_LEN-1]=0;
}
}
if (file)
{
strncpy(file, syminfo.dli_fname, DBG_FILE_LEN);
file[DBG_FILE_LEN-1]=0;
}
if (line)
{
*line=0;
}
return INFO::OK;
}
LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line)
{
ONCE(udbg_init());
// We use our default context - in the future we could do something like
// mapping library -> file context to support more detailed reporting on
// external libraries
symbol_file_context *file_ctx=&ps_dbg_context;
bfd *abfd=file_ctx->abfd;
// Reset here if we fail later on
if (sym_name)
*sym_name=0;
if (file)
*file=0;
if (line)
*line=0;
if (!udbg_initialized)
return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line);
symbol_lookup_context ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.address=reinterpret_cast<bfd_vma>(ptr_of_interest);
ctx.file_ctx=file_ctx;
bfd_map_over_sections (abfd, find_address_in_section, &ctx);
// This will happen for addresses in external files. What one *could* do
// here is to figure out the originating library file and load that through
// BFD... but how often will external libraries have debugging info? really?
// At least attempt to find out the symbol name through dladdr.
if (!ctx.found)
return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line);
if (sym_name)
{
demangle_buf(sym_name, ctx.symbol, DBG_SYMBOL_LEN);
}
if (file)
{
if (ctx.filename != NULL)
{
const char *h;
h = strrchr (ctx.filename, '/');
if (h != NULL)
ctx.filename = h + 1;
strncpy(file, ctx.filename, DBG_FILE_LEN);
file[DBG_FILE_LEN]=0;
}
else
strcpy(file, "none");
}
if (line)
{
*line = ctx.line;
}
return INFO::OK;
}
void debug_puts(const char* text)
{
fputs(text, stdout);
fflush(stdout);
}
// TODO: Do these properly. (I don't know what I'm doing; I just
// know that these functions are required in order to compile...)

View File

@ -0,0 +1,382 @@
#include "precompiled.h"
// Only use BFD on linux (where it is supported)
#if OS_LINUX
#include "lib/timer.h"
#include "lib/sysdep/sysdep.h"
#include "lib/debug.h"
#include <stdarg.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <cassert>
#include <bfd.h>
#include <cxxabi.h>
#ifndef bfd_get_section_size
#define bfd_get_section_size bfd_get_section_size_before_reloc
#endif
// This is a no-op. But we *could* use the BFD stuff on other platforms too, if
// we saw the need for it
#if OS_LINUX
#define GNU_SOURCE
#include <dlfcn.h>
#include <execinfo.h>
#endif
// Hard-coded - yuck :P
// These should only be used as fallbacks
#if defined(TESTING)
#define EXE_NAME "pyrogenesis_test"
#elif defined(NDEBUG)
#define EXE_NAME "pyrogenesis"
#else
#define EXE_NAME "pyrogenesis_dbg"
#endif
#define PROFILE_RESOLVE_SYMBOL 0
struct symbol_file_context
{
asymbol **syms;
bfd *abfd;
};
symbol_file_context ps_dbg_context;
bool udbg_initialized=false;
struct symbol_lookup_context
{
symbol_file_context *file_ctx;
bfd_vma address;
const char* symbol;
const char* filename;
uint line;
bool found;
};
void* debug_get_nth_caller(uint n, void *UNUSED(context))
{
// bt[0] == debug_get_nth_caller
// bt[1] == caller of get_nth_caller
// bt[2] == 1:st caller (n==1)
void *bt[n+2];
int bt_size;
bt_size=backtrace(bt, n+2);
assert(bt_size >= (int)(n+2) && "Need at least n+2 frames in get_nth_caller");
return bt[n+1]; // n==1 => bt[2], and so forth
}
LibError debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* UNUSED(context))
{
++skip; // Skip ourselves too
// bt[0..skip] == skipped
// bt[skip..N_FRAMES+skip] == print
static const uint N_FRAMES = 16;
void *bt[skip+N_FRAMES];
int bt_size=0;
wchar_t *bufpos = buf;
wchar_t *bufend = buf + max_chars;
bt_size=backtrace(bt, ARRAY_SIZE(bt));
// did we get enough backtraced frames?
assert((bt_size >= (int)skip) && "Need at least <skip> frames in the backtrace");
// Assumed max length of a single print-out
static const uint MAX_OUT_CHARS=1024;
for (uint i=skip;(int)i<bt_size && bufpos+MAX_OUT_CHARS < bufend;i++)
{
char file[DBG_FILE_LEN];
char symbol[DBG_SYMBOL_LEN];
int line;
int len;
if (debug_resolve_symbol(bt[i], symbol, file, &line) == 0)
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x) %hs:%d %hs\n", bt[i], file, line, symbol);
else
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x)\n", bt[i]);
if (len < 0)
{
// MAX_OUT_CHARS exceeded, realistically this was caused by some
// mindbogglingly long symbol name... replace the end with an
// ellipsis and a newline
memcpy(&bufpos[MAX_OUT_CHARS-6], L"...\n", 5*sizeof(wchar_t));
len = MAX_OUT_CHARS;
}
bufpos += len;
}
return INFO::OK;
}
static int slurp_symtab(symbol_file_context *ctx)
{
bfd *abfd=ctx->abfd;
asymbol ***syms=&ctx->syms;
long symcount;
int size=0;
if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0)
{
printf("slurp_symtab(): Huh? Has no symbols...\n");
return -1;
}
size = bfd_get_symtab_upper_bound(abfd);
if (size < 0)
{
bfd_perror("symtab_upper_bound");
return -1;
}
*syms = (asymbol **)malloc(size);
if (!syms)
return -1;
symcount = bfd_canonicalize_symtab(abfd, *syms);
if (symcount == 0)
symcount = bfd_read_minisymbols (abfd, FALSE, (void **)syms, (uint *)&size);
if (symcount == 0)
symcount = bfd_read_minisymbols (abfd, TRUE /* dynamic */, (void **)syms, (uint *)&size);
if (symcount < 0)
{
bfd_perror("slurp_symtab");
return -1;
}
return 0;
}
static int read_symbols(const char *file_name, symbol_file_context *ctx)
{
char **matching=NULL;
ONCE(bfd_init());
ctx->abfd = bfd_openr (file_name, NULL);
if (ctx->abfd == NULL)
{
bfd_perror("udbg.cpp: bfd_openr");
return -1;
}
if (! bfd_check_format_matches (ctx->abfd, bfd_object, &matching))
{
if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
{
printf("Error reading symbols from %s: ambiguous format\n", file_name);
while (*matching)
printf("\tPotential matching format: %s\n", *matching++);
free(matching);
}
else
{
bfd_perror("bfd_check_format_matches");
}
return -1;
}
int res=slurp_symtab(ctx);
if (res == 0)
return res;
else
{
bfd_perror("udbg.cpp: slurp_symtab");
bfd_close(ctx->abfd);
return -1;
}
}
void udbg_bfd_init(void)
{
char n_path[PATH_MAX];
char *exename=n_path;
if (sys_get_executable_name(n_path, sizeof(n_path)) != INFO::OK)
{
debug_printf("sys_get_executable_name didn't work, using hard-coded guess %s.\n", EXE_NAME);
exename=EXE_NAME;
}
debug_printf("udbg_init: loading symbols from %s.\n", exename);
if (read_symbols(exename, &ps_dbg_context)==0)
udbg_initialized=true;
#if PROFILE_RESOLVE_SYMBOL
{
TIMER(udbg_init_benchmark)
char symbol[DBG_SYMBOL_LEN];
char file[DBG_FILE_LEN];
int line;
debug_resolve_symbol(debug_get_nth_caller(3), symbol, file, &line);
printf("%s (%s:%d)\n", symbol, file, line);
for (int i=0;i<1000000;i++)
{
debug_resolve_symbol(debug_get_nth_caller(1), symbol, file, &line);
}
}
#endif
}
static void find_address_in_section (bfd *abfd, asection *section, void *data)
{
symbol_lookup_context *ctx=(symbol_lookup_context *)data;
asymbol **syms=ctx->file_ctx->syms;
bfd_vma pc=ctx->address;
bfd_vma vma;
bfd_size_type size;
if (ctx->found) return;
if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0)
return;
vma = bfd_get_section_vma (abfd, section);
if (pc < vma)
return;
size = bfd_get_section_size (section);
if (pc >= vma + size)
return;
ctx->found = bfd_find_nearest_line (abfd, section, syms,
pc - vma, &ctx->filename, &ctx->symbol, &ctx->line);
}
// BFD functions perform allocs with real malloc - we need to free that data
#include "lib/nommgr.h"
void demangle_buf(char *buf, const char *symbol, size_t n)
{
int status=0;
char *alloc=NULL;
if (symbol == NULL || *symbol == '\0')
{
symbol = "??";
status = -1;
}
else
{
alloc=abi::__cxa_demangle(symbol, NULL, NULL, &status);
}
// status is 0 on success and a negative value on failure
if (status == 0)
symbol=alloc;
strncpy(buf, symbol, n);
buf[n-1]=0;
if (alloc)
free(alloc);
}
static LibError debug_resolve_symbol_dladdr(void *ptr, char* sym_name, char* file, int* line)
{
Dl_info syminfo;
int res=dladdr(ptr, &syminfo);
if (res == 0)
WARN_RETURN(ERR::FAIL);
if (sym_name)
{
if (syminfo.dli_sname)
demangle_buf(sym_name, syminfo.dli_sname, DBG_SYMBOL_LEN);
else
{
snprintf(sym_name, DBG_SYMBOL_LEN, "0x%08x", (uint)ptr);
sym_name[DBG_SYMBOL_LEN-1]=0;
}
}
if (file)
{
strncpy(file, syminfo.dli_fname, DBG_FILE_LEN);
file[DBG_FILE_LEN-1]=0;
}
if (line)
{
*line=0;
}
return INFO::OK;
}
LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line)
{
ONCE(udbg_init());
// We use our default context - in the future we could do something like
// mapping library -> file context to support more detailed reporting on
// external libraries
symbol_file_context *file_ctx=&ps_dbg_context;
bfd *abfd=file_ctx->abfd;
// Reset here if we fail later on
if (sym_name)
*sym_name=0;
if (file)
*file=0;
if (line)
*line=0;
if (!udbg_initialized)
return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line);
symbol_lookup_context ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.address=reinterpret_cast<bfd_vma>(ptr_of_interest);
ctx.file_ctx=file_ctx;
bfd_map_over_sections (abfd, find_address_in_section, &ctx);
// This will happen for addresses in external files. What one *could* do
// here is to figure out the originating library file and load that through
// BFD... but how often will external libraries have debugging info? really?
// At least attempt to find out the symbol name through dladdr.
if (!ctx.found)
return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line);
if (sym_name)
{
demangle_buf(sym_name, ctx.symbol, DBG_SYMBOL_LEN);
}
if (file)
{
if (ctx.filename != NULL)
{
const char *h;
h = strrchr (ctx.filename, '/');
if (h != NULL)
ctx.filename = h + 1;
strncpy(file, ctx.filename, DBG_FILE_LEN);
file[DBG_FILE_LEN]=0;
}
else
strcpy(file, "none");
}
if (line)
{
*line = ctx.line;
}
return INFO::OK;
}
#endif

View File

@ -4,7 +4,7 @@
#include <stdio.h>
#include <wchar.h>
#include "lib/sdl.h"
#include "lib/external_libraries/sdl.h"
#include "lib/sysdep/sysdep.h"
#include "udbg.h"