From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 43084 invoked by alias); 15 Mar 2015 19:52:07 -0000 Mailing-List: contact cygwin-developers-help@cygwin.com; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-developers-owner@cygwin.com Mail-Followup-To: cygwin-developers@cygwin.com Received: (qmail 43074 invoked by uid 89); 15 Mar 2015 19:52:07 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=4.5 required=5.0 tests=AWL,BAYES_00,FREEMAIL_FROM,KAM_COUK,KAM_FROM_URIBL_PCCC,KAM_STOCKGEN,SPF_PASS autolearn=no version=3.3.2 X-HELO: out.ipsmtp4nec.opaltelecom.net Received: from out.ipsmtp4nec.opaltelecom.net (HELO out.ipsmtp4nec.opaltelecom.net) (62.24.202.76) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (CAMELLIA256-SHA encrypted) ESMTPS; Sun, 15 Mar 2015 19:52:04 +0000 X-SMTPAUTH: drstacey@tiscali.co.uk X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A2CWAQB64gVV/yvURk8NToNYWoMMwQQKh1gBAQEBAQGFNgRRPRYLAgsDAgECATcBIAgBAYg4qmpwmjIMAR+PMIMMDC8SgTMFjjGDcYEvVIdPkkCCJRyBUG+BAoFBAQEB X-IPAS-Result: A2CWAQB64gVV/yvURk8NToNYWoMMwQQKh1gBAQEBAQGFNgRRPRYLAgsDAgECATcBIAgBAYg4qmpwmjIMAR+PMIMMDC8SgTMFjjGDcYEvVIdPkkCCJRyBUG+BAoFBAQEB Received: from 79-70-212-43.dynamic.dsl.as9105.com (HELO [192.168.1.2]) ([79.70.212.43]) by out.ipsmtp4nec.opaltelecom.net with ESMTP; 15 Mar 2015 19:51:59 +0000 Message-ID: <5505E2DA.6090806@tiscali.co.uk> Date: Sun, 15 Mar 2015 19:52:00 -0000 From: David Stacey User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.5.0 MIME-Version: 1.0 To: cygwin-developers@cygwin.com Subject: backtrace(3) in Cygwin Content-Type: multipart/mixed; boundary="------------090804060804030302060905" X-IsSubscribed: yes X-SW-Source: 2015-03/txt/msg00005.txt.bz2 This is a multi-part message in MIME format. --------------090804060804030302060905 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-length: 598 Following on from my thread on the main list [1], I have made an initial attempt at implementing backtrace(3) and backtrace_symbols(3). This still needs a little work - at the moment, the paths in the strings will be DOS-style rather than POSIX. However, I wanted to submit an early version to make sure you were happy with my approach before I spend more time on it. I've also documented (in the code) the issues surrounding backtrace_symbols_fd(3) on Cygwin. This is my first real submission to the programme - be kind :-) Dave. [1] - http://cygwin.com/ml/cygwin/2015-03/msg00198.html --------------090804060804030302060905 Content-Type: text/x-csrc; name="exec_info.c" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="exec_info.c" Content-length: 7786 #include #include #include #include /* This implementation of backtrace(3) and backtrace_symbols(3) comes with a * number of limitations: * * - The maxium number of stack frames is limited by the implementation of * CaptureStackBackTrace() in Windows XP, which can only retrieve a * maximum of 63 frames. * * - All of the DbgHelp functions in Windows are not thread safe. This * means that our implementation of the backtrace functions are also not * thread safe. * * - The frames returned by this implementation of backtrace(3) include * those those in the Windows kernel. This means that kernel32.dll and * ntdll.dll appear at the bottom end of the stack trace, and obviously * you wouldn't see these on Linux. */ /* backtrace() returns a backtrace for the calling program, in the array * pointed to by buffer. A backtrace is the series of currently active * function calls for the program. Each item in the array pointed to by * buffer is of type void *, and is the return address from the corresponding * stack frame. The size argument specifies the maximum number of addresses * that can be stored in buffer. If the backtrace is larger than size, then * the addresses corresponding to the size most recent function calls are * returned; to obtain the complete backtrace, make sure that buffer and size * are large enough. */ int backtrace(void **buffer, int size) { HANDLE process = GetCurrentProcess(); const int xp_max_frame_depth = 61; if (size > xp_max_frame_depth) size = xp_max_frame_depth; /* Ignore this function when getting the stack frames. */ SymInitialize(process, NULL, TRUE); return CaptureStackBackTrace(1, size, buffer, NULL); } /* Given the set of addresses returned by backtrace() in buffer, * backtrace_symbols() translates the addresses into an array of strings that * describe the addresses symbolically. The size argument specifies the * number of addresses in buffer. The symbolic representation of each address * consists of the function name (if this can be determined), a hexadecimal * offset into the function, and the actual return address (in hexadecimal). * The address of the array of string pointers is returned as the function * result of backtrace_symbols(). This array is malloc(3)ed by * backtrace_symbols(), and must be freed by the caller. (The strings pointed * to by the array of pointers need not and should not be freed.) */ char **backtrace_symbols(void *const *buffer, int size) { HANDLE process = GetCurrentProcess(); SYMBOL_INFO *symbol; IMAGEHLP_MODULE64 module_info; const int chars_needed_to_display_address = 16; const int additional_characters = 11; int chars_required = 0; int i; char **result; DWORD64 offset; char* frame_text; symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + (MAX_SYM_NAME + 1) * sizeof(char), 1); symbol->MaxNameLen = MAX_SYM_NAME; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); module_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); /* The glibc documentation doen't say whether the strings we're preparing * should contain decorated or undecorated symbol names; it simply states * that it should be a 'printable representation' of the frame. The man * page doesn't offer any guidance either. We'll use undecorated symbols, * as these are probably more helpful. */ SymSetOptions(SymGetOptions() | SYMOPT_UNDNAME); /* Compute the amount of memory required to hold the results. Each string * takes the following form: * * ./prog(myfunc+0x21) [0x8048894] * * So we need the module name and the symbol name (which will vary in * length), two addresses (which we assume are 64 bits and therefore * require 16 characters each), ten other characters (brackets, spaces, * etc.) and a terminating NULL. * * We also need a string lookup table, which contains pointers to these * strings. The string lookup table comes first in memory, followed by * the strings themselves. */ chars_required = size * sizeof(char*); for (i = 0; i < size; ++i) { if ((SymFromAddr(process, (DWORD64)(buffer[i]), 0, symbol)) && (SymGetModuleInfo64(process, symbol->Address, &module_info))) { chars_required += strlen(symbol->Name) + strlen(module_info.LoadedImageName) + (2 * chars_needed_to_display_address) + additional_characters; } } /* Allocate enough memory to hold the strings and the string lookup * table. This memory buffer is returned once it has been populated, and * it is the responsibility of the caller to free(3) the memory. */ result = (char**) malloc(chars_required); /* Now populate the string lookup table and the strings with the text * describing a frame. The pointer 'frame_text' is within the buffer we * have just allocated and points to the start of the next string to * write. */ if (result) { frame_text = (char*) (result + size); for (i = 0; i < size; ++i) { result[i] = frame_text; if ((SymFromAddr(process, (DWORD64)(buffer[i]), &offset, symbol)) && (SymGetModuleInfo64(process, symbol->Address, &module_info))) { frame_text += 1 + sprintf(frame_text, "%s(%s+0x%lx) [0x%lx]", module_info.LoadedImageName, symbol->Name, (unsigned long)offset, (unsigned long)buffer[i]); } else frame_text += 1 + sprintf(frame_text, "[0x%lx]", (unsigned long)buffer[i]); } assert(frame_text < (char*)result + chars_required); } free(symbol); return result; } /* backtrace_symbols_fd() takes the same buffer and size arguments as * backtrace_symbols(), but instead of returning an array of strings to the * caller, it writes the strings, one per line, to the file descriptor fd. * backtrace_symbols_fd() does not call malloc(3), and so can be employed in * situations where the latter function might fail. */ void backtrace_symbols_fd(void *const *buffer, int size, int fd) { /* A Cygwin implementation of backtrace_symbols_fd(3) is going to be * somewhat problematic and will demand a compromise at some point along * the way. The problem is how to allocate memory for the SYMBOL_INFO * structure, given that we're not supposed to use heap memory. Windows * defines MAX_SYM_NAME as 2000 characters, and clearly we shouldn't go * trying to allocate that much memory on the stack. * * Then there's the issue of how we actually get the data out to the file * descriptor supplied. If Cygwin supports dprintf(3) then that's all * well and good - but if not we'll have to use write(2), and that * involves allocating another buffer to hold the text we want to write * out - and that means a second copy of our long symbol name on the * stack. * * Clearly a compromise is needed. Here are some options: * * - Instead of MAX_SYM_NAME, use a much, much smaller value (say * 256). Then we could allocate all the memory we need on the stack * and still display the majority of symbol names. * * - Allocate heap memory anyway, irrespective of what the man page * says. In environments where Cygwin is run (i.e. the Windows * desktop), heap is probably plentiful, so does this matter? * * - Do not provide backtrace_symbols_fd(3) at all. * * For the moment, I'll take the latter of those options. */ } --------------090804060804030302060905--