diff options
Diffstat (limited to 'winsup/utils/dumper.cc')
-rw-r--r-- | winsup/utils/dumper.cc | 754 |
1 files changed, 0 insertions, 754 deletions
diff --git a/winsup/utils/dumper.cc b/winsup/utils/dumper.cc deleted file mode 100644 index 6ccf2f1..0000000 --- a/winsup/utils/dumper.cc +++ /dev/null @@ -1,754 +0,0 @@ -/* dumper.cc - - Copyright 1999 Cygnus Solutions. - - Written by Egor Duda <deo@logos-m.ru> - -This file is part of Cygwin. - -This software is a copyrighted work licensed under the terms of the -Cygwin license. Please consult the file "CYGWIN_LICENSE" for -details. */ - -#include <bfd.h> -#include <elf/common.h> -#include <elf/external.h> -#include <sys/procfs.h> -#include <sys/cygwin.h> -#include <getopt.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <windows.h> - -#include "dumper.h" - -#define NOTE_NAME_SIZE 16 - -typedef struct _note_header -{ - Elf_External_Note elf_note_header; - char name [ NOTE_NAME_SIZE - 1 ]; /* external note contains first byte of data */ -} -#ifdef __GNUC__ - __attribute__ ((packed)) -#endif -note_header ; - -BOOL verbose = FALSE; - -int deb_printf ( const char* format, ... ) -{ - if ( !verbose ) return 0; - va_list va; - va_start ( va, format ); - int ret_val = vprintf ( format, va ); - va_end ( va ); - return ret_val; -} - -dumper::dumper ( DWORD pid, DWORD tid, const char* file_name ) -{ - this->file_name = strdup ( file_name ); - - this->pid = pid; - this->tid = tid; - core_bfd = NULL; - excl_list = new exclusion ( 20 ); - - list = last = NULL; - - status_section = NULL; - - memory_num = module_num = thread_num = 0; - - hProcess = OpenProcess ( PROCESS_ALL_ACCESS, - FALSE, /* no inheritance */ - pid ); - if ( !hProcess ) - { - fprintf ( stderr, "Failed to open process #%lu\n", pid ); - return; - } - - init_core_dump (); - - if ( ! sane () ) dumper_abort (); -} - -dumper::~dumper () -{ - close (); - free ( file_name ); -} - -void -dumper::dumper_abort () -{ - close (); - unlink ( file_name ); -} - -void -dumper::close () -{ - if ( core_bfd ) bfd_close ( core_bfd ); - if ( excl_list ) delete excl_list; - if ( hProcess ) CloseHandle ( hProcess ); - core_bfd = NULL; - hProcess = NULL; - excl_list = NULL; -} - -int -dumper::sane () -{ - if ( hProcess == NULL || core_bfd == NULL || excl_list == NULL ) return 0; - return 1; -} - -process_entity* -dumper::add_process_entity_to_list ( process_entity_type type ) -{ - if ( ! sane () ) return NULL; - - process_entity* new_entity = ( process_entity* ) malloc ( sizeof ( process_entity ) ); - if ( new_entity == NULL ) return NULL; - new_entity->next = NULL; - new_entity->section = NULL; - if ( last == NULL ) - list = new_entity; - else - last->next = new_entity; - last = new_entity; - return new_entity; -} - -int -dumper::add_thread ( DWORD tid, HANDLE hThread ) -{ - if ( ! sane () ) return 0 ; - - CONTEXT* pcontext; - - process_entity* new_entity = add_process_entity_to_list ( pr_ent_thread ); - if ( new_entity == NULL ) return 0; - new_entity->type = pr_ent_thread; - thread_num++; - - new_entity->u.thread.tid = tid; - new_entity->u.thread.hThread = hThread; - - pcontext = &( new_entity->u.thread.context ); - pcontext->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; - if ( ! GetThreadContext ( hThread, pcontext ) ) return 0; - - deb_printf ( "added thread %u\n", tid ); - return 1; -} - -int -dumper::add_mem_region ( LPBYTE base, DWORD size ) -{ - if ( ! sane () ) return 0; - - if ( base == NULL || size == 0 ) return 1; // just ignore empty regions - - process_entity* new_entity = add_process_entity_to_list ( pr_ent_memory ); - if ( new_entity == NULL ) return 0; - new_entity->type = pr_ent_memory; - memory_num++; - - new_entity->u.memory.base = base; - new_entity->u.memory.size = size; - - deb_printf ( "added memory region %08x-%08x\n", (DWORD)base, (DWORD)base + size ); - return 1; -} - -/* - * split_add_mem_region scans list of regions to be excluded from dumping process - * (excl_list) and removes all "excluded" parts from given region - */ -int -dumper::split_add_mem_region ( LPBYTE base, DWORD size ) -{ - if ( ! sane () ) return 0; - - if ( base == NULL || size == 0 ) return 1; // just ignore empty regions - - LPBYTE last_base = base; - - for ( process_mem_region* p = excl_list->region; - p < excl_list->region + excl_list->last; - p++ ) - { - if ( p->base >= base + size || p->base + p->size <= base ) continue; - - if ( p->base <= base ) - { - last_base = p->base + p->size; - continue ; - } - - add_mem_region ( last_base, p->base - last_base ); - last_base = p->base + p->size; - } - - if ( last_base < base + size ) - add_mem_region ( last_base, base + size - last_base ); - - return 1; -} - -int -dumper::add_module ( LPVOID base_address ) -{ - if ( ! sane () ) return 0; - - char* module_name = psapi_get_module_name ( hProcess, (DWORD) base_address ); - if ( module_name == NULL ) return 1; - - process_entity* new_entity = add_process_entity_to_list ( pr_ent_module ); - if ( new_entity == NULL ) return 0; - new_entity->type = pr_ent_module; - module_num++; - - new_entity->u.module.base_address = base_address; - new_entity->u.module.name = module_name; - - parse_pe ( module_name, excl_list ); - - deb_printf ( "added module %08x %s\n", base_address, module_name ); - return 1; -} - -#define PAGE_BUFFER_SIZE 4096 - -int -dumper::collect_memory_sections () -{ - if ( ! sane () ) return 0; - - LPBYTE current_page_address; - LPBYTE last_base = (LPBYTE) 0xFFFFFFFF; - DWORD last_size = 0; - DWORD done; - - char mem_buf [ PAGE_BUFFER_SIZE ]; - - MEMORY_BASIC_INFORMATION mbi; - - if ( hProcess == NULL ) return 0; - - for ( current_page_address = 0; current_page_address < (LPBYTE) 0xFFFF0000; ) - { - if ( ! VirtualQueryEx ( hProcess, current_page_address, &mbi, sizeof ( mbi ) ) ) - break ; - - int skip_region_p = 0; - - if ( mbi.Protect & ( PAGE_NOACCESS | PAGE_GUARD ) || - mbi.State != MEM_COMMIT ) skip_region_p = 1; - - if ( ! skip_region_p ) - { - /* just to make sure that later we'll be able to read it. - According to MS docs either region is all-readable or - all-nonreadable */ - if ( ! ReadProcessMemory ( hProcess, current_page_address, mem_buf, sizeof ( mem_buf ), &done ) ) - { - const char* pt[10] ; - pt[0] = ( mbi.Protect & PAGE_READONLY ) ? "RO " : ""; - pt[1] = ( mbi.Protect & PAGE_READWRITE ) ? "RW " : ""; - pt[2] = ( mbi.Protect & PAGE_WRITECOPY ) ? "WC " : ""; - pt[3] = ( mbi.Protect & PAGE_EXECUTE ) ? "EX " : ""; - pt[4] = ( mbi.Protect & PAGE_EXECUTE_READ ) ? "EXRO " : ""; - pt[5] = ( mbi.Protect & PAGE_EXECUTE_READWRITE ) ? "EXRW " : ""; - pt[6] = ( mbi.Protect & PAGE_EXECUTE_WRITECOPY ) ? "EXWC " : ""; - pt[7] = ( mbi.Protect & PAGE_GUARD ) ? "GRD " : ""; - pt[8] = ( mbi.Protect & PAGE_NOACCESS ) ? "NA " : ""; - pt[9] = ( mbi.Protect & PAGE_NOCACHE ) ? "NC " : ""; - char buf [ 10*6 ] ; - buf [ 0 ] = '\0'; - for ( int i = 0 ; i < 10 ; i++ ) strcat ( buf, pt[i] ); - - deb_printf ( "warning: failed to read memory at %08x-%08x. protect = %s\n", - (DWORD)current_page_address, - (DWORD)current_page_address + mbi.RegionSize, - buf ); - skip_region_p = 1; - } - } - - if ( ! skip_region_p ) - { - if ( last_base + last_size == current_page_address ) - last_size += mbi.RegionSize; - else - { - split_add_mem_region ( last_base, last_size ); - last_base = (LPBYTE) mbi.BaseAddress; - last_size = mbi.RegionSize; - } - } - else - { - split_add_mem_region ( last_base, last_size ); - last_base = NULL; - last_size = 0; - } - - current_page_address += mbi.RegionSize; - } - - /* dump last sections, if any */ - split_add_mem_region ( last_base, last_size ); - return 1; -}; - -int -dumper::dump_memory_region ( asection* to, process_mem_region* memory ) -{ - if ( ! sane () ) return 0; - - DWORD size = memory->size; - DWORD todo; - DWORD done; - LPBYTE pos = memory->base; - DWORD sect_pos = 0; - - if ( to == NULL || memory == NULL ) return 0; - - char mem_buf [ PAGE_BUFFER_SIZE ]; - - while ( size > 0 ) - { - todo = min ( size, PAGE_BUFFER_SIZE ); - if ( ! ReadProcessMemory ( hProcess, pos, mem_buf, todo, &done ) ) - { - deb_printf ( "Error reading process memory at %x(%x) %u\n", pos, todo, GetLastError () ); - return 0; - } - size -= done; - pos += done; - if ( ! bfd_set_section_contents ( core_bfd, to, mem_buf, sect_pos, done ) ) - { - bfd_perror ( "writing memory region to bfd" ); - dumper_abort (); - return 0; - } ; - sect_pos += done; - } - return 1; -} - -int -dumper::dump_thread ( asection* to, process_thread* thread ) -{ - if ( ! sane () ) return 0; - - if ( to == NULL || thread == NULL ) return 0; - - win32_pstatus thread_pstatus; - - note_header header; - bfd_putl32 ( NOTE_NAME_SIZE, header.elf_note_header.namesz ); - bfd_putl32 ( sizeof ( thread_pstatus ), header.elf_note_header.descsz ); - bfd_putl32 ( NT_WIN32PSTATUS, header.elf_note_header.type ); - strncpy ( (char*) & header.elf_note_header.name, "win32thread", NOTE_NAME_SIZE ); - - thread_pstatus.data_type = NOTE_INFO_THREAD; - thread_pstatus.data.thread_info.tid = thread->tid; - - if ( tid == 0 ) - { - /* this is a special case. we don't know, which thread - was active when exception occured, so let's blame - the first one */ - thread_pstatus.data.thread_info.is_active_thread = TRUE ; - tid = (DWORD) -1 ; - } - else if ( tid > 0 && thread->tid == tid ) - thread_pstatus.data.thread_info.is_active_thread = TRUE; - else - thread_pstatus.data.thread_info.is_active_thread = FALSE; - - memcpy ( &(thread_pstatus.data.thread_info.thread_context), - &(thread->context), - sizeof ( thread->context ) ); - - if ( ! bfd_set_section_contents ( core_bfd, to, &header, - 0, - sizeof ( header ) ) || - ! bfd_set_section_contents ( core_bfd, to, &thread_pstatus, - sizeof ( header ), - sizeof ( thread_pstatus ) ) ) - { - bfd_perror ( "writing thread info to bfd" ); - dumper_abort (); - return 0; - } ; - return 1; -} - -int -dumper::dump_module ( asection* to, process_module* module ) -{ - if ( ! sane () ) return 0; - - if ( to == NULL || module == NULL ) return 0; - - struct win32_pstatus* module_pstatus_ptr; - - int note_length = sizeof ( struct win32_pstatus ) + strlen ( module->name ); - - char* buf = (char*) malloc ( note_length ); - - if ( ! buf ) - { - fprintf ( stderr, "Error alloating memory. Dumping aborted.\n" ); - goto out; - } ; - - module_pstatus_ptr = (struct win32_pstatus*) buf; - - note_header header; - bfd_putl32 ( NOTE_NAME_SIZE, header.elf_note_header.namesz ); - bfd_putl32 ( note_length, header.elf_note_header.descsz ); - bfd_putl32 ( NT_WIN32PSTATUS, header.elf_note_header.type ); - strncpy ( (char*) & header.elf_note_header.name, "win32module", NOTE_NAME_SIZE ); - - module_pstatus_ptr->data_type = NOTE_INFO_MODULE; - module_pstatus_ptr->data.module_info.base_address = module->base_address; - module_pstatus_ptr->data.module_info.module_name_size = strlen ( module->name ) + 1; - strcpy ( module_pstatus_ptr->data.module_info.module_name, module->name ); - - if ( ! bfd_set_section_contents ( core_bfd, to, &header, - 0, - sizeof ( header ) ) || - ! bfd_set_section_contents ( core_bfd, to, module_pstatus_ptr, - sizeof ( header ), - note_length ) ) - { - bfd_perror ( "writing module info to bfd" ); - goto out; - }; - return 1; - -out: - if ( buf ) free ( buf ); - dumper_abort (); - return 0; - -} - -int -dumper::collect_process_information () -{ - if ( ! sane () ) return 0; - - if ( ! DebugActiveProcess ( pid ) ) - { - fprintf ( stderr, "Cannot attach to process #%lu", pid ); - return 0; - } - - char event_name [ sizeof ( "cygwin_error_start_event" ) + 20 ]; - sprintf ( event_name, "cygwin_error_start_event%16lx", pid ); - HANDLE sync_with_debugee = OpenEvent ( EVENT_MODIFY_STATE, FALSE, event_name ); - - DEBUG_EVENT current_event; - - while (1) - { - if ( ! WaitForDebugEvent ( ¤t_event, 20000 ) ) return 0; - - switch (current_event.dwDebugEventCode) - { - case CREATE_THREAD_DEBUG_EVENT: - - if ( ! add_thread ( current_event.dwThreadId, - current_event.u.CreateThread.hThread ) ) - goto failed; - - break; - - case CREATE_PROCESS_DEBUG_EVENT: - - if ( ! add_module ( current_event.u.CreateProcessInfo.lpBaseOfImage ) || - ! add_thread ( current_event.dwThreadId, - current_event.u.CreateProcessInfo.hThread ) ) - goto failed; - - break; - - case EXIT_PROCESS_DEBUG_EVENT: - - deb_printf ( "debugee quits" ); - ContinueDebugEvent ( current_event.dwProcessId, - current_event.dwThreadId, - DBG_CONTINUE ); - - return 1; - - break; - - case LOAD_DLL_DEBUG_EVENT: - - if ( ! add_module ( current_event.u.LoadDll.lpBaseOfDll ) ) - goto failed; - - break; - - case EXCEPTION_DEBUG_EVENT: - - collect_memory_sections (); - - /* got all info. time to dump */ - - if ( ! prepare_core_dump () ) - { - fprintf ( stderr, "Failed to prepare core dump\n" ); - goto failed; - }; - - if ( ! write_core_dump () ) - { - fprintf ( stderr, "Failed to write core dump\n" ); - goto failed; - }; - - /* signal a debugee that we've finished */ - if ( sync_with_debugee ) SetEvent ( sync_with_debugee ); - - break; - - default: - - break; - - } - - ContinueDebugEvent ( current_event.dwProcessId, - current_event.dwThreadId, - DBG_CONTINUE ); - } -failed: - /* set debugee free */ - if ( sync_with_debugee ) SetEvent ( sync_with_debugee ); - - return 0; -} - -int -dumper::init_core_dump () -{ - bfd_init (); - - core_bfd = bfd_openw ( file_name, "elf32-i386" ); - if ( core_bfd == NULL ) - { - bfd_perror ( "opening bfd" ); - goto failed; - } - - if ( ! bfd_set_format ( core_bfd, bfd_core ) ) - { - bfd_perror ( "setting bfd format" ); - goto failed; - } - - return 1; - -failed: - dumper_abort (); - return 0; - -} - -int -dumper::prepare_core_dump () -{ - if ( ! sane () ) return 0; - - int sect_no = 0; - char sect_name [ 50 ]; - - flagword sect_flags; - DWORD sect_size; - bfd_vma sect_vma; - - asection* new_section; - - for ( process_entity* p = list; p != NULL; p = p->next ) - { - sect_no++; - - switch ( p->type ) - { - case pr_ent_memory: - sprintf ( sect_name, ".mem/%u", sect_no ); - sect_flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD; - sect_size = p->u.memory.size; - sect_vma = (bfd_vma)(p->u.memory.base); - - break; - - case pr_ent_thread: - sprintf ( sect_name, ".note/%u", sect_no ); - sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; - sect_size = sizeof ( note_header ) + sizeof ( struct win32_pstatus ); - sect_vma = 0; - break; - - case pr_ent_module: - sprintf ( sect_name, ".note/%u", sect_no ); - sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; - sect_size = sizeof ( note_header ) + sizeof ( struct win32_pstatus ) + - (bfd_size_type)( strlen (p->u.module.name) ); - sect_vma = 0; - break; - - default: - continue; - } - - if ( p->type == pr_ent_module && status_section != NULL ) - { - if ( ! bfd_set_section_size ( core_bfd, - status_section, - status_section->_raw_size + sect_size ) ) - { - bfd_perror ( "resizing status section" ); - goto failed; - }; - continue; - } - - deb_printf ( "creating section (type%u) %s(%u), flags=%08x\n", - p->type, sect_name, sect_size, sect_flags ); - - char* buf = strdup ( sect_name ); - new_section = bfd_make_section ( core_bfd, buf ); - - if ( new_section == NULL || - ! bfd_set_section_flags ( core_bfd, new_section, sect_flags ) || - ! bfd_set_section_size ( core_bfd, new_section, sect_size ) ) - { - bfd_perror ( "creating section" ); - goto failed; - }; - - new_section->vma = sect_vma; - new_section->output_section = new_section; - new_section->output_offset = 0; - p->section = new_section; - } - - return 1; - -failed: - dumper_abort (); - return 0; -} - -int -dumper::write_core_dump () -{ - if ( ! sane () ) return 0; - - for ( process_entity* p = list; p != NULL; p = p->next ) - { - if ( p->section == NULL ) continue; - - deb_printf ( "writing section type=%u base=%08x size=%08x flags=%08x\n", - p->type, - p->section->vma, - p->section->_raw_size, - p->section->flags ); - - switch ( p->type ) - { - case pr_ent_memory: - dump_memory_region ( p->section, &(p->u.memory) ); - break; - - case pr_ent_thread: - dump_thread ( p->section, &(p->u.thread) ); - break; - - case pr_ent_module: - dump_module ( p->section, &(p->u.module) ); - break; - - default: - continue; - - } - } - return 1; -} - -static void -usage () -{ - fprintf ( stderr, "Usage: dumper [-v] [-c filename] pid\n" ); - fprintf ( stderr, "-c filename -- dump core to filename.core\n" ); - fprintf ( stderr, "-d -- print some debugging info while dumping\n" ); - fprintf ( stderr, "pid -- win32-pid of process to dump\n" ); -} - -int -main( int argc, char** argv ) -{ - int opt; - char* p = ""; - DWORD pid; - - while ((opt = getopt (argc, argv, "dc:")) != EOF) - switch (opt) - { - case 'd': - verbose = TRUE; - break; - case 'c': - char win32_name [ MAX_PATH ]; - cygwin_conv_to_win32_path ( optarg, win32_name ); - if ( ( p = strrchr ( win32_name, '\\' ) ) ) - p++; - else - p = win32_name; - break; - } - - char* core_file = (char*) malloc ( strlen ( p ) + sizeof ( ".core" ) ); - if ( ! core_file ) - { - fprintf ( stderr, "error allocating memory\n" ); - return -1; - } - sprintf ( core_file, "%s.core", p ); - - if ( argv && *(argv+optind) ) - pid = atoi ( *(argv+optind) ); - else - { - usage (); - return -1; - } - - DWORD tid = 0; - - if ( verbose ) - printf ( "dumping process #%lu to %s\n", pid, core_file ); - - dumper d ( pid, tid, core_file ); - if ( ! d.sane () ) - return -1; - d.collect_process_information (); - free ( core_file ); - - return 0; -}; |