| /**************************************************************************** |
| * sched/misc/coredump.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <sys/stat.h> |
| #include <sys/param.h> |
| |
| #include <syslog.h> |
| #include <debug.h> |
| |
| #include <nuttx/coredump.h> |
| #include <nuttx/elf.h> |
| #include <nuttx/nuttx.h> |
| #include <nuttx/sched.h> |
| |
| #include "sched/sched.h" |
| #include "coredump.h" |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #ifdef PAGESIZE |
| # define ELF_PAGESIZE PAGESIZE |
| #else |
| # define ELF_PAGESIZE 1024 |
| #endif |
| |
| #if defined(CONFIG_BOARD_COREDUMP_BLKDEV) || \ |
| defined(CONFIG_BOARD_COREDUMP_MTDDEV) |
| # define CONFIG_BOARD_COREDUMP_DEV |
| #endif |
| |
| #define PROGRAM_ALIGNMENT 64 |
| |
| /* Architecture can overwrite the default XCPTCONTEXT alignment */ |
| |
| #ifndef XCPTCONTEXT_ALIGN |
| # define XCPTCONTEXT_ALIGN 16 |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* This struct provides a description of the dump information of |
| * memory regions. |
| */ |
| |
| struct elf_dumpinfo_s |
| { |
| FAR const struct memory_region_s *regions; |
| FAR struct lib_outstream_s *stream; |
| pid_t pid; |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static uint8_t g_running_regs[XCPTCONTEXT_SIZE] |
| aligned_data(XCPTCONTEXT_ALIGN); |
| |
| #ifdef CONFIG_BOARD_COREDUMP_COMPRESSION |
| static struct lib_lzfoutstream_s g_lzfstream; |
| #endif |
| |
| #ifdef CONFIG_BOARD_COREDUMP_SYSLOG |
| static struct lib_syslogstream_s g_syslogstream; |
| # ifdef CONFIG_BOARD_COREDUMP_BASE64STREAM |
| static struct lib_base64outstream_s g_base64stream; |
| # else |
| static struct lib_hexdumpstream_s g_hexstream; |
| # endif |
| #endif |
| |
| #ifdef CONFIG_BOARD_COREDUMP_BLKDEV |
| static struct lib_blkoutstream_s g_devstream; |
| #elif defined(CONFIG_BOARD_COREDUMP_MTDDEV) |
| static struct lib_mtdoutstream_s g_devstream; |
| #endif |
| |
| #ifdef CONFIG_BOARD_MEMORY_RANGE |
| static struct memory_region_s g_memory_region[] = |
| { |
| CONFIG_BOARD_MEMORY_RANGE |
| }; |
| #endif |
| static const struct memory_region_s *g_regions; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: elf_flush |
| * |
| * Description: |
| * Flush the out stream |
| * |
| ****************************************************************************/ |
| |
| static int elf_flush(FAR struct elf_dumpinfo_s *cinfo) |
| { |
| return lib_stream_flush(cinfo->stream); |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit |
| * |
| * Description: |
| * Send the dump data to binfmt_outstream_s |
| * |
| ****************************************************************************/ |
| |
| static int elf_emit(FAR struct elf_dumpinfo_s *cinfo, |
| FAR const void *buf, size_t len) |
| { |
| FAR const uint8_t *ptr = buf; |
| size_t total = len; |
| int ret = 0; |
| |
| while (total > 0) |
| { |
| ret = lib_stream_puts(cinfo->stream, ptr, total); |
| if (ret < 0) |
| { |
| break; |
| } |
| |
| total -= ret; |
| ptr += ret; |
| } |
| |
| return ret < 0 ? ret : len - total; |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_align |
| * |
| * Description: |
| * Align the filled data according to the current offset |
| * |
| ****************************************************************************/ |
| |
| static int elf_emit_align(FAR struct elf_dumpinfo_s *cinfo) |
| { |
| off_t align = ALIGN_UP(cinfo->stream->nput, |
| ELF_PAGESIZE) - cinfo->stream->nput; |
| unsigned char null[256]; |
| off_t total = align; |
| off_t ret = 0; |
| |
| memset(null, 0, sizeof(null)); |
| |
| while (total > 0) |
| { |
| ret = elf_emit(cinfo, null, total > sizeof(null) ? |
| sizeof(null) : total); |
| if (ret <= 0) |
| { |
| break; |
| } |
| |
| total -= ret; |
| } |
| |
| return ret < 0 ? ret : align; |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_hdr |
| * |
| * Description: |
| * Fill the elf header |
| * |
| ****************************************************************************/ |
| |
| static int elf_emit_hdr(FAR struct elf_dumpinfo_s *cinfo, |
| int segs) |
| { |
| Elf_Ehdr ehdr; |
| |
| memset(&ehdr, 0, sizeof(ehdr)); |
| memcpy(ehdr.e_ident, ELFMAG, EI_MAGIC_SIZE); |
| |
| ehdr.e_ident[EI_CLASS] = ELF_CLASS; |
| ehdr.e_ident[EI_DATA] = ELF_DATA; |
| ehdr.e_ident[EI_VERSION] = EV_CURRENT; |
| ehdr.e_ident[EI_OSABI] = ELF_OSABI; |
| |
| ehdr.e_type = ET_CORE; |
| ehdr.e_machine = EM_ARCH; |
| ehdr.e_version = EV_CURRENT; |
| ehdr.e_phoff = sizeof(Elf_Ehdr); |
| ehdr.e_flags = EF_FLAG; |
| ehdr.e_ehsize = sizeof(Elf_Ehdr); |
| ehdr.e_phentsize = sizeof(Elf_Phdr); |
| ehdr.e_phnum = segs; |
| |
| return elf_emit(cinfo, &ehdr, sizeof(ehdr)); |
| } |
| |
| /**************************************************************************** |
| * Name: elf_get_ntcb |
| * |
| * Description: |
| * Calculate the note segment size |
| * |
| ****************************************************************************/ |
| |
| static int elf_get_ntcb(void) |
| { |
| int count = 0; |
| int i; |
| |
| for (i = 0; i < g_npidhash; i++) |
| { |
| if (g_pidhash[i] != NULL) |
| { |
| count++; |
| } |
| } |
| |
| return count; |
| } |
| |
| /**************************************************************************** |
| * Name: elf_get_note_size |
| * |
| * Description: |
| * Calculate the note segment size |
| * |
| ****************************************************************************/ |
| |
| static int elf_get_note_size(int stksegs) |
| { |
| int total; |
| |
| total = stksegs * (sizeof(Elf_Nhdr) + COREDUMP_INFONAME_SIZE + |
| sizeof(elf_prstatus_t)); |
| total += stksegs * (sizeof(Elf_Nhdr) + COREDUMP_INFONAME_SIZE + |
| sizeof(elf_prpsinfo_t)); |
| return total; |
| } |
| |
| static int elf_get_info_note_size(void) |
| { |
| return sizeof(Elf_Nhdr) + COREDUMP_INFONAME_SIZE + |
| sizeof(struct coredump_info_s); |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_tcb_note |
| * |
| * Description: |
| * Fill the note segment information from tcb |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_tcb_note(FAR struct elf_dumpinfo_s *cinfo, |
| FAR struct tcb_s *tcb) |
| { |
| char name[COREDUMP_INFONAME_SIZE]; |
| elf_prstatus_t status; |
| elf_prpsinfo_t info; |
| FAR uintptr_t *regs; |
| Elf_Nhdr nhdr; |
| int i; |
| |
| memset(&info, 0x0, sizeof(info)); |
| memset(&status, 0x0, sizeof(status)); |
| |
| /* Fill Process info */ |
| |
| nhdr.n_namesz = sizeof(name); |
| nhdr.n_descsz = sizeof(info); |
| nhdr.n_type = NT_PRPSINFO; |
| |
| elf_emit(cinfo, &nhdr, sizeof(nhdr)); |
| |
| strlcpy(name, tcb->name, sizeof(name)); |
| elf_emit(cinfo, name, sizeof(name)); |
| |
| info.pr_pid = tcb->pid; |
| strlcpy(info.pr_fname, tcb->name, sizeof(info.pr_fname)); |
| elf_emit(cinfo, &info, sizeof(info)); |
| |
| /* Fill Process status */ |
| |
| nhdr.n_descsz = sizeof(status); |
| nhdr.n_type = NT_PRSTATUS; |
| |
| elf_emit(cinfo, &nhdr, sizeof(nhdr)); |
| elf_emit(cinfo, name, sizeof(name)); |
| |
| status.pr_pid = tcb->pid; |
| |
| if (running_task() == tcb) |
| { |
| if (up_interrupt_context()) |
| { |
| regs = (FAR uintptr_t *)running_regs(); |
| } |
| else |
| { |
| up_saveusercontext(g_running_regs); |
| regs = (FAR uintptr_t *)g_running_regs; |
| } |
| } |
| else |
| { |
| regs = (FAR uintptr_t *)tcb->xcp.regs; |
| } |
| |
| if (regs != NULL) |
| { |
| for (i = 0; i < MIN(nitems(status.pr_regs), g_tcbinfo.regs_num); i++) |
| { |
| if (g_tcbinfo.reg_off.p[i] != UINT16_MAX) |
| { |
| status.pr_regs[i] = |
| *(uintptr_t *)((uint8_t *)regs + g_tcbinfo.reg_off.p[i]); |
| } |
| } |
| } |
| |
| elf_emit(cinfo, &status, sizeof(status)); |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_note |
| * |
| * Description: |
| * Fill the note segment information |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_note(FAR struct elf_dumpinfo_s *cinfo) |
| { |
| int i; |
| |
| if (cinfo->pid == INVALID_PROCESS_ID) |
| { |
| for (i = 0; i < g_npidhash; i++) |
| { |
| if (g_pidhash[i] != NULL) |
| { |
| elf_emit_tcb_note(cinfo, g_pidhash[i]); |
| } |
| } |
| } |
| else |
| { |
| elf_emit_tcb_note(cinfo, nxsched_get_tcb(cinfo->pid)); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_tcb_stack |
| * |
| * Description: |
| * Fill the task stack information from tcb |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_tcb_stack(FAR struct elf_dumpinfo_s *cinfo, |
| FAR struct tcb_s *tcb) |
| { |
| uintptr_t buf = 0; |
| uintptr_t sp; |
| size_t len; |
| |
| if (running_task() != tcb) |
| { |
| sp = up_getusrsp(tcb->xcp.regs); |
| |
| if (sp > (uintptr_t)tcb->stack_base_ptr && |
| sp < (uintptr_t)tcb->stack_base_ptr + tcb->adj_stack_size) |
| { |
| len = ((uintptr_t)tcb->stack_base_ptr + |
| tcb->adj_stack_size) - sp; |
| buf = sp; |
| } |
| #ifdef CONFIG_STACK_COLORATION |
| else |
| { |
| len = up_check_tcbstack(tcb, tcb->adj_stack_size); |
| buf = (uintptr_t)tcb->stack_base_ptr + |
| (tcb->adj_stack_size - len); |
| } |
| #endif |
| } |
| |
| if (buf == 0) |
| { |
| buf = (uintptr_t)tcb->stack_alloc_ptr; |
| len = tcb->adj_stack_size + |
| (tcb->stack_base_ptr - tcb->stack_alloc_ptr); |
| } |
| |
| sp = ALIGN_DOWN(buf, PROGRAM_ALIGNMENT); |
| len = ALIGN_UP(len + (buf - sp), PROGRAM_ALIGNMENT); |
| buf = sp; |
| |
| elf_emit(cinfo, (FAR void *)buf, len); |
| |
| /* Align to page */ |
| |
| elf_emit_align(cinfo); |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_stack |
| * |
| * Description: |
| * Fill the task stack information |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_stack(FAR struct elf_dumpinfo_s *cinfo) |
| { |
| int i; |
| |
| if (cinfo->pid == INVALID_PROCESS_ID) |
| { |
| for (i = 0; i < g_npidhash; i++) |
| { |
| if (g_pidhash[i] != NULL) |
| { |
| elf_emit_tcb_stack(cinfo, g_pidhash[i]); |
| } |
| } |
| } |
| else |
| { |
| elf_emit_tcb_stack(cinfo, nxsched_get_tcb(cinfo->pid)); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_memory |
| * |
| * Description: |
| * Fill the note segment information |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_memory(FAR struct elf_dumpinfo_s *cinfo, int memsegs) |
| { |
| int i; |
| |
| for (i = 0; i < memsegs; i++) |
| { |
| if (cinfo->regions[i].flags & PF_REGISTER) |
| { |
| FAR uintptr_t *start = (FAR uintptr_t *)cinfo->regions[i].start; |
| FAR uintptr_t *end = (FAR uintptr_t *)cinfo->regions[i].end; |
| uintptr_t buf[64]; |
| size_t offset = 0; |
| |
| while (start < end) |
| { |
| buf[offset++] = *start++; |
| |
| if (offset % (sizeof(buf) / sizeof(uintptr_t)) == 0) |
| { |
| elf_emit(cinfo, buf, sizeof(buf)); |
| offset = 0; |
| } |
| } |
| |
| if (offset != 0) |
| { |
| elf_emit(cinfo, buf, offset * sizeof(uintptr_t)); |
| } |
| } |
| else |
| { |
| elf_emit(cinfo, (FAR void *)cinfo->regions[i].start, |
| cinfo->regions[i].end - cinfo->regions[i].start); |
| } |
| |
| /* Align to page */ |
| |
| elf_emit_align(cinfo); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_info_note |
| * |
| * Description: |
| * Fill the core info note |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_info_note(FAR struct elf_dumpinfo_s *cinfo) |
| { |
| struct coredump_info_s info; |
| Elf_Nhdr nhdr; |
| char name[COREDUMP_INFONAME_SIZE]; |
| |
| memset(&info, 0x0, sizeof(info)); |
| memset(name, 0x0, sizeof(name)); |
| |
| nhdr.n_namesz = sizeof(name); |
| nhdr.n_descsz = sizeof(info); |
| nhdr.n_type = COREDUMP_MAGIC; |
| |
| elf_emit(cinfo, &nhdr, sizeof(nhdr)); |
| |
| strlcpy(name, "NuttX", sizeof(name)); |
| elf_emit(cinfo, name, sizeof(name)); |
| |
| info.size = cinfo->stream->nput + sizeof(info); |
| clock_gettime(CLOCK_REALTIME, &info.time); |
| uname(&info.name); |
| |
| elf_emit(cinfo, &info, sizeof(info)); |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_tcb_phdr |
| * |
| * Description: |
| * Fill the program segment header from tcb |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_tcb_phdr(FAR struct elf_dumpinfo_s *cinfo, |
| FAR struct tcb_s *tcb, |
| FAR Elf_Phdr *phdr, off_t *offset) |
| { |
| uintptr_t sp; |
| |
| phdr->p_vaddr = 0; |
| |
| if (running_task() != tcb) |
| { |
| sp = up_getusrsp(tcb->xcp.regs); |
| |
| if (sp > (uintptr_t)tcb->stack_base_ptr && |
| sp < (uintptr_t)tcb->stack_base_ptr + tcb->adj_stack_size) |
| { |
| phdr->p_filesz = ((uintptr_t)tcb->stack_base_ptr + |
| tcb->adj_stack_size) - sp; |
| phdr->p_vaddr = sp; |
| } |
| #ifdef CONFIG_STACK_COLORATION |
| else |
| { |
| phdr->p_filesz = up_check_tcbstack(tcb, tcb->adj_stack_size); |
| phdr->p_vaddr = (uintptr_t)tcb->stack_base_ptr + |
| (tcb->adj_stack_size - phdr->p_filesz); |
| } |
| #endif |
| } |
| |
| if (phdr->p_vaddr == 0) |
| { |
| phdr->p_vaddr = (uintptr_t)tcb->stack_alloc_ptr; |
| phdr->p_filesz = tcb->adj_stack_size + |
| (tcb->stack_base_ptr - tcb->stack_alloc_ptr); |
| } |
| |
| sp = ALIGN_DOWN(phdr->p_vaddr, PROGRAM_ALIGNMENT); |
| phdr->p_filesz = ALIGN_UP(phdr->p_filesz + |
| (phdr->p_vaddr - sp), PROGRAM_ALIGNMENT); |
| phdr->p_vaddr = sp; |
| |
| phdr->p_type = PT_LOAD; |
| phdr->p_offset = ALIGN_UP(*offset, ELF_PAGESIZE); |
| phdr->p_paddr = phdr->p_vaddr; |
| phdr->p_memsz = phdr->p_filesz; |
| phdr->p_flags = PF_X | PF_W | PF_R; |
| *offset += ALIGN_UP(phdr->p_memsz, ELF_PAGESIZE); |
| |
| elf_emit(cinfo, phdr, sizeof(*phdr)); |
| } |
| |
| /**************************************************************************** |
| * Name: elf_emit_phdr |
| * |
| * Description: |
| * Fill the program segment header |
| * |
| ****************************************************************************/ |
| |
| static void elf_emit_phdr(FAR struct elf_dumpinfo_s *cinfo, |
| int stksegs, int memsegs) |
| { |
| off_t offset = cinfo->stream->nput + |
| (stksegs + memsegs + 1 + 1) * sizeof(Elf_Phdr); |
| Elf_Phdr phdr; |
| int i; |
| |
| memset(&phdr, 0, sizeof(Elf_Phdr)); |
| |
| phdr.p_type = PT_NOTE; |
| phdr.p_offset = offset; |
| phdr.p_filesz = elf_get_note_size(stksegs); |
| offset += phdr.p_filesz; |
| |
| elf_emit(cinfo, &phdr, sizeof(phdr)); |
| |
| phdr.p_align = ELF_PAGESIZE; |
| if (cinfo->pid == INVALID_PROCESS_ID) |
| { |
| for (i = 0; i < g_npidhash; i++) |
| { |
| if (g_pidhash[i] != NULL) |
| { |
| elf_emit_tcb_phdr(cinfo, g_pidhash[i], &phdr, &offset); |
| } |
| } |
| } |
| else |
| { |
| elf_emit_tcb_phdr(cinfo, nxsched_get_tcb(cinfo->pid), |
| &phdr, &offset); |
| } |
| |
| /* Write program headers for segments dump */ |
| |
| for (i = 0; i < memsegs; i++) |
| { |
| phdr.p_type = PT_LOAD; |
| phdr.p_offset = ALIGN_UP(offset, ELF_PAGESIZE); |
| phdr.p_vaddr = cinfo->regions[i].start; |
| phdr.p_paddr = phdr.p_vaddr; |
| phdr.p_filesz = cinfo->regions[i].end - cinfo->regions[i].start; |
| phdr.p_memsz = phdr.p_filesz; |
| phdr.p_flags = cinfo->regions[i].flags; |
| offset += ALIGN_UP(phdr.p_memsz, ELF_PAGESIZE); |
| elf_emit(cinfo, &phdr, sizeof(phdr)); |
| } |
| |
| memset(&phdr, 0, sizeof(Elf_Phdr)); |
| phdr.p_type = PT_NOTE; |
| phdr.p_offset = ALIGN_UP(offset, ELF_PAGESIZE); |
| phdr.p_filesz = elf_get_info_note_size(); |
| offset += phdr.p_filesz; |
| |
| elf_emit(cinfo, &phdr, sizeof(phdr)); |
| } |
| |
| /**************************************************************************** |
| * Name: coredump_dump_syslog |
| * |
| * Description: |
| * Put coredump to block device. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_BOARD_COREDUMP_SYSLOG |
| static void coredump_dump_syslog(pid_t pid) |
| { |
| FAR void *stream; |
| FAR const char *streamname; |
| int logmask; |
| |
| logmask = setlogmask(LOG_UPTO(LOG_ALERT)); |
| |
| _alert("Start coredump:\n"); |
| |
| /* Initialize hex output stream */ |
| |
| lib_syslogstream(&g_syslogstream, LOG_EMERG); |
| stream = &g_syslogstream; |
| #ifdef CONFIG_BOARD_COREDUMP_BASE64STREAM |
| lib_base64outstream(&g_base64stream, stream); |
| stream = &g_base64stream; |
| streamname = "base64"; |
| #else |
| lib_hexdumpstream(&g_hexstream, stream); |
| stream = &g_hexstream; |
| streamname = "hex"; |
| #endif |
| |
| # ifdef CONFIG_BOARD_COREDUMP_COMPRESSION |
| |
| /* Initialize LZF compression stream */ |
| |
| lib_lzfoutstream(&g_lzfstream, stream); |
| stream = &g_lzfstream; |
| # endif |
| |
| /* Do core dump */ |
| |
| coredump(g_regions, stream, pid); |
| |
| # ifdef CONFIG_BOARD_COREDUMP_COMPRESSION |
| _alert("Finish coredump (Compression Enabled). %s formatted\n", |
| streamname); |
| # else |
| _alert("Finish coredump. %s formatted\n", streamname); |
| # endif |
| |
| setlogmask(logmask); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: coredump_dump_dev |
| * |
| * Description: |
| * Save coredump to storage device. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_BOARD_COREDUMP_DEV |
| static void coredump_dump_dev(pid_t pid) |
| { |
| FAR void *stream = &g_devstream; |
| int ret; |
| |
| if (g_devstream.inode == NULL) |
| { |
| _alert("Coredump device not found\n"); |
| return; |
| } |
| |
| #ifdef CONFIG_BOARD_COREDUMP_COMPRESSION |
| lib_lzfoutstream(&g_lzfstream, stream); |
| stream = &g_lzfstream; |
| #endif |
| |
| ret = coredump(g_regions, stream, pid); |
| if (ret < 0) |
| { |
| _alert("Coredump fail %d\n", ret); |
| return; |
| } |
| |
| _alert("Finish coredump, write %zu bytes to %s\n", |
| g_devstream.common.nput, CONFIG_BOARD_COREDUMP_DEVPATH); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: coredump_initialize_memory_region |
| * |
| * Description: |
| * initialize the memory region with board memory range specified in config |
| * |
| ****************************************************************************/ |
| |
| static int coredump_initialize_memory_region(void) |
| { |
| #ifdef CONFIG_BOARD_MEMORY_RANGE |
| if (g_regions == NULL) |
| { |
| g_regions = g_memory_region; |
| } |
| #endif |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: coredump_add_memory_region |
| * |
| * Description: |
| * Use coredump to dump the memory of the specified area. |
| * |
| ****************************************************************************/ |
| |
| int coredump_add_memory_region(FAR const void *ptr, size_t size, |
| uint32_t flags) |
| { |
| FAR struct memory_region_s *region; |
| size_t count = 1; /* 1 for end flag */ |
| int ret; |
| |
| ret = coredump_initialize_memory_region(); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| if (g_regions != NULL) |
| { |
| region = (FAR struct memory_region_s *)g_regions; |
| |
| while (region->start < region->end) |
| { |
| if ((uintptr_t)ptr >= region->start && |
| (uintptr_t)ptr + size < region->end) |
| { |
| /* Already watched */ |
| |
| return 0; |
| } |
| else if ((uintptr_t)ptr < region->start && |
| (uintptr_t)ptr + size >= region->end) |
| { |
| /* start out of region, end out of region */ |
| |
| region->start = (uintptr_t)ptr; |
| region->end = (uintptr_t)ptr + size; |
| return 0; |
| } |
| else if ((uintptr_t)ptr < region->start && |
| (uintptr_t)ptr + size >= region->start) |
| { |
| /* start out of region, end in region */ |
| |
| region->start = (uintptr_t)ptr; |
| return 0; |
| } |
| else if ((uintptr_t)ptr < region->end && |
| (uintptr_t)ptr + size >= region->end) |
| { |
| /* start in region, end out of region */ |
| |
| region->end = (uintptr_t)ptr + size; |
| return 0; |
| } |
| |
| count++; |
| region++; |
| } |
| |
| /* Need a new region */ |
| } |
| |
| region = lib_malloc(sizeof(struct memory_region_s) * (count + 1)); |
| if (region == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| memcpy(region, g_regions, sizeof(struct memory_region_s) * count); |
| |
| if (g_regions != NULL |
| #ifdef CONFIG_BOARD_MEMORY_RANGE |
| && g_regions != g_memory_region |
| #endif |
| ) |
| { |
| lib_free((FAR void *)g_regions); |
| } |
| |
| region[count - 1].start = (uintptr_t)ptr; |
| region[count - 1].end = (uintptr_t)ptr + size; |
| region[count - 1].flags = flags; |
| region[count].start = 0; |
| region[count].end = 0; |
| region[count].flags = 0; |
| |
| g_regions = region; |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: coredump_initialize |
| * |
| * Description: |
| * Initialize the coredump facility. Called once and only from |
| * nx_start_application. |
| * |
| ****************************************************************************/ |
| |
| int coredump_initialize(void) |
| { |
| int ret = 0; |
| |
| ret = coredump_initialize_memory_region(); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| #ifdef CONFIG_BOARD_COREDUMP_BLKDEV |
| ret = lib_blkoutstream_open(&g_devstream, |
| CONFIG_BOARD_COREDUMP_DEVPATH); |
| #elif defined(CONFIG_BOARD_COREDUMP_MTDDEV) |
| ret = lib_mtdoutstream_open(&g_devstream, |
| CONFIG_BOARD_COREDUMP_DEVPATH); |
| #endif |
| |
| #ifdef CONFIG_BOARD_COREDUMP_DEV |
| if (ret < 0) |
| { |
| _alert("%s Coredump device not found %d\n", |
| CONFIG_BOARD_COREDUMP_DEVPATH, ret); |
| } |
| #endif |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: coredump_dump |
| * |
| * Description: |
| * Do coredump of the task specified by pid. |
| * |
| * Input Parameters: |
| * pid - The task/thread ID of the thread to dump |
| * |
| ****************************************************************************/ |
| |
| void coredump_dump(pid_t pid) |
| { |
| #ifdef CONFIG_BOARD_COREDUMP_SYSLOG |
| coredump_dump_syslog(pid); |
| #endif |
| |
| #ifdef CONFIG_BOARD_COREDUMP_DEV |
| coredump_dump_dev(pid); |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: coredump |
| * |
| * Description: |
| * This function for generating core dump stream. |
| * |
| ****************************************************************************/ |
| |
| int coredump(FAR const struct memory_region_s *regions, |
| FAR struct lib_outstream_s *stream, |
| pid_t pid) |
| { |
| struct elf_dumpinfo_s cinfo; |
| irqstate_t flags; |
| int memsegs = 0; |
| int stksegs; |
| |
| cinfo.regions = regions; |
| cinfo.stream = stream; |
| cinfo.pid = pid; |
| |
| flags = enter_critical_section(); |
| |
| if (cinfo.pid != INVALID_PROCESS_ID) |
| { |
| if (nxsched_get_tcb(cinfo.pid) == NULL) |
| { |
| leave_critical_section(flags); |
| return -EINVAL; |
| } |
| |
| stksegs = 1; |
| } |
| else |
| { |
| stksegs = elf_get_ntcb(); |
| } |
| |
| /* Check the memory region */ |
| |
| if (cinfo.regions != NULL) |
| { |
| for (; cinfo.regions[memsegs].start < |
| cinfo.regions[memsegs].end; memsegs++); |
| } |
| |
| /* Fill notes section, with additional one for program header, |
| * and one for the core file info defined by NuttX. |
| */ |
| |
| elf_emit_hdr(&cinfo, stksegs + memsegs + 1 + 1); |
| |
| /* Fill all the program information about the process for the |
| * notes. This also sets up the file header. |
| */ |
| |
| elf_emit_phdr(&cinfo, stksegs, memsegs); |
| |
| /* Fill note information */ |
| |
| elf_emit_note(&cinfo); |
| |
| /* Align to page */ |
| |
| elf_emit_align(&cinfo); |
| |
| /* Dump stack */ |
| |
| elf_emit_stack(&cinfo); |
| |
| /* Dump memory segments */ |
| |
| if (memsegs > 0) |
| { |
| elf_emit_memory(&cinfo, memsegs); |
| } |
| |
| /* Emit core info note */ |
| |
| elf_emit_info_note(&cinfo); |
| |
| /* Flush the dump */ |
| |
| elf_flush(&cinfo); |
| |
| leave_critical_section(flags); |
| |
| return OK; |
| } |