| /* dumper.cc |
| |
| Written by Egor Duda <deo@logos-m.ru> |
| |
| This file is part of Cygwin. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License (file COPYING.dumper) for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ |
| |
| #include <ansidecl.h> |
| #define PACKAGE |
| #include <bfd.h> |
| #include <elf/common.h> |
| #include <elf/external.h> |
| #include <sys/procfs.h> |
| #include <sys/cygwin.h> |
| #include <cygwin/version.h> |
| #include <getopt.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/param.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 #%u, error %ld\n", |
| (unsigned int) pid, (long) GetLastError ()); |
| 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; |
| } |
| |
| void |
| print_section_name (bfd* abfd, asection* sect, PTR obj) |
| { |
| deb_printf (" %s", bfd_get_section_name (abfd, sect)); |
| } |
| |
| void |
| dumper::print_core_section_list () |
| { |
| deb_printf ("current sections:"); |
| bfd_map_over_sections (core_bfd, &print_section_name, NULL); |
| deb_printf ("\n"); |
| } |
| |
| 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)) |
| { |
| deb_printf ("Failed to read thread context (tid=%x), error %ld\n", tid, GetLastError ()); |
| return 0; |
| } |
| |
| deb_printf ("added thread %u\n", tid); |
| return 1; |
| } |
| |
| int |
| dumper::add_mem_region (LPBYTE base, SIZE_T 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 %p-%p\n", base, 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, SIZE_T 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, 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 %p %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; |
| SIZE_T last_size = (SIZE_T) 0; |
| SIZE_T 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)) |
| { |
| DWORD err = GetLastError (); |
| 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 %p-%p (protect = %s), error %ld.\n", |
| current_page_address, |
| current_page_address + mbi.RegionSize, |
| buf, err); |
| 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; |
| |
| SIZE_T size = memory->size; |
| SIZE_T todo; |
| SIZE_T 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 ("Failed to read process memory at %x(%x), error %ld\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 () |
| { |
| int exception_level = 0; |
| |
| if (!sane ()) |
| return 0; |
| |
| if (!DebugActiveProcess (pid)) |
| { |
| fprintf (stderr, "Cannot attach to process #%u, error %ld", |
| (unsigned int) pid, (long) GetLastError ()); |
| return 0; |
| } |
| |
| char event_name[sizeof ("cygwin_error_start_event") + 20]; |
| sprintf (event_name, "cygwin_error_start_event%16x", (unsigned int) 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; |
| |
| deb_printf ("got debug event %d\n", current_event.dwDebugEventCode); |
| |
| 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: |
| |
| exception_level++; |
| if (exception_level == 2) |
| break; |
| else if (exception_level > 2) |
| return 0; |
| |
| 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; |
| } |
| |
| if (!bfd_set_arch_mach (core_bfd, bfd_arch_i386, 0)) |
| { |
| bfd_perror ("setting bfd architecture"); |
| 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; |
| SIZE_T sect_size; |
| bfd_vma sect_vma; |
| |
| asection *new_section; |
| |
| for (process_entity * p = list; p != NULL; p = p->next) |
| { |
| sect_no++; |
| |
| unsigned long phdr_type = PT_LOAD; |
| |
| 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); |
| phdr_type = PT_LOAD; |
| 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; |
| phdr_type = PT_NOTE; |
| 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; |
| phdr_type = PT_NOTE; |
| break; |
| |
| default: |
| continue; |
| } |
| |
| if (p->type == pr_ent_module && status_section != NULL) |
| { |
| if (!bfd_set_section_size (core_bfd, |
| status_section, |
| (bfd_get_section_size (status_section) |
| + 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); |
| |
| bfd_set_error (bfd_error_no_error); |
| char *buf = strdup (sect_name); |
| new_section = bfd_make_section (core_bfd, buf); |
| if (new_section == NULL) |
| { |
| if (bfd_get_error () == bfd_error_no_error) |
| fprintf (stderr, "error creating new section (%s), section already exists.\n", buf); |
| else |
| bfd_perror ("creating section"); |
| goto failed; |
| } |
| |
| if (!bfd_set_section_flags (core_bfd, new_section, sect_flags) || |
| !bfd_set_section_size (core_bfd, new_section, sect_size)) |
| { |
| bfd_perror ("setting section attributes"); |
| goto failed; |
| }; |
| |
| new_section->vma = sect_vma; |
| new_section->lma = 0; |
| new_section->output_section = new_section; |
| new_section->output_offset = 0; |
| p->section = new_section; |
| int section_count = 1; |
| |
| bfd_boolean filehdr = 0; |
| bfd_boolean phdrs = 0; |
| |
| bfd_vma at = 0; |
| bfd_boolean valid_at = 0; |
| |
| flagword flags = 0; |
| bfd_boolean valid_flags = 1; |
| |
| if (p->type == pr_ent_memory) |
| { |
| MEMORY_BASIC_INFORMATION mbi; |
| if (!VirtualQueryEx (hProcess, (LPVOID)sect_vma, &mbi, sizeof (mbi))) |
| { |
| bfd_perror ("getting mem region flags"); |
| goto failed; |
| } |
| |
| static const struct |
| { |
| DWORD protect; |
| flagword flags; |
| } mappings[] = |
| { |
| { PAGE_READONLY, PF_R }, |
| { PAGE_READWRITE, PF_R | PF_W }, |
| { PAGE_WRITECOPY, PF_W }, |
| { PAGE_EXECUTE, PF_X }, |
| { PAGE_EXECUTE_READ, PF_X | PF_R }, |
| { PAGE_EXECUTE_READWRITE, PF_X | PF_R | PF_W }, |
| { PAGE_EXECUTE_WRITECOPY, PF_X | PF_W } |
| }; |
| |
| for (size_t i = 0; |
| i < sizeof (mappings) / sizeof (mappings[0]); |
| i++) |
| if ((mbi.Protect & mappings[i].protect) != 0) |
| flags |= mappings[i].flags; |
| } |
| |
| if (!bfd_record_phdr (core_bfd, phdr_type, |
| valid_flags, flags, |
| valid_at, at, |
| filehdr, phdrs, |
| section_count, &new_section)) |
| { |
| bfd_perror ("recording program headers"); |
| goto failed; |
| } |
| } |
| 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=%p size=%p flags=%08x\n", |
| p->type, |
| p->section->vma, |
| bfd_get_section_size (p->section), |
| 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 (FILE *stream, int status) |
| { |
| fprintf (stream, "\ |
| Usage: %s [OPTION] FILENAME WIN32PID\n\ |
| \n\ |
| Dump core from WIN32PID to FILENAME.core\n\ |
| \n\ |
| -d, --verbose be verbose while dumping\n\ |
| -h, --help output help information and exit\n\ |
| -q, --quiet be quiet while dumping (default)\n\ |
| -V, --version output version information and exit\n\ |
| \n", program_invocation_short_name); |
| exit (status); |
| } |
| |
| struct option longopts[] = { |
| {"verbose", no_argument, NULL, 'd'}, |
| {"help", no_argument, NULL, 'h'}, |
| {"quiet", no_argument, NULL, 'q'}, |
| {"version", no_argument, 0, 'V'}, |
| {0, no_argument, NULL, 0} |
| }; |
| const char *opts = "dhqV"; |
| |
| static void |
| print_version () |
| { |
| printf ("dumper (cygwin) %d.%d.%d\n" |
| "Core Dumper for Cygwin\n" |
| "Copyright (C) 1999 - %s Cygwin Authors\n" |
| "This is free software; see the source for copying conditions. There is NO\n" |
| "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", |
| CYGWIN_VERSION_DLL_MAJOR / 1000, |
| CYGWIN_VERSION_DLL_MAJOR % 1000, |
| CYGWIN_VERSION_DLL_MINOR, |
| strrchr (__DATE__, ' ') + 1); |
| } |
| |
| int |
| main (int argc, char **argv) |
| { |
| int opt; |
| const char *p = ""; |
| DWORD pid; |
| |
| while ((opt = getopt_long (argc, argv, opts, longopts, NULL) ) != EOF) |
| switch (opt) |
| { |
| case 'd': |
| verbose = TRUE; |
| break; |
| case 'q': |
| verbose = FALSE; |
| break; |
| case 'h': |
| usage (stdout, 0); |
| case 'V': |
| print_version (); |
| exit (0); |
| default: |
| fprintf (stderr, "Try `%s --help' for more information.\n", |
| program_invocation_short_name); |
| exit (1); |
| } |
| |
| if (argv && *(argv + optind) && *(argv + optind +1)) |
| { |
| ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, |
| *(argv + optind), NULL, 0); |
| char *win32_name = (char *) alloca (len); |
| cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, *(argv + optind), |
| win32_name, len); |
| if ((p = strrchr (win32_name, '\\'))) |
| p++; |
| else |
| p = win32_name; |
| pid = strtoul (*(argv + optind + 1), NULL, 10); |
| } |
| else |
| { |
| usage (stderr, 1); |
| return -1; |
| } |
| |
| 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); |
| |
| DWORD tid = 0; |
| |
| if (verbose) |
| printf ("dumping process #%u to %s\n", (unsigned int) pid, core_file); |
| |
| dumper d (pid, tid, core_file); |
| if (!d.sane ()) |
| return -1; |
| d.collect_process_information (); |
| free (core_file); |
| |
| return 0; |
| }; |