Hi Rongwei, Thanks for the patch, please see the comments below.
On Tue, Sep 16, 2025 at 12:39 PM Rongwei Wang <[email protected]> wrote: > > From: Rongwei Wang <[email protected]> > > 'help -m' can show most of variables which > relates to memory layout, e.g. userspace_top, > page_offset, vmalloc_start_addr, etc. They > aren't a visual way to show a memory layout > for kernel space. > > This patch provides 'help -l' to show memory > layout, usage likes: > > crash> help -l > Gap Hole: 0xffffffffff7ff001 - 0xffffffffffffffff Size: 8.0 MB > Fixmap Area: 0xffffffffff578000 - 0xffffffffff7ff000 Size: 2.5 MB > Gap Hole: 0xffffffffff000001 - 0xffffffffff577fff Size: 5.5 MB > Module Area: 0xffffffffa0000000 - 0xffffffffff000000 Size: 1.5 GB > Gap Hole: 0xffffffff84826001 - 0xffffffff9fffffff Size: 439.9 > MB > Kernel Image: 0xffffffff81000000 - 0xffffffff84826000 Size: 56.1 MB > Gap Hole: 0xffffeb0000000000 - 0xffffffff80ffffff Size: 21.0 TB > Vmemmap Area: 0xffffea0000000000 - 0xffffeaffffffffff Size: 1.0 TB > Gap Hole: 0xffffe90000000000 - 0xffffe9ffffffffff Size: 1.0 TB > Vmalloc Area: 0xffffc90000000000 - 0xffffe8ffffffffff Size: 32.0 TB > Gap Hole: 0xffffc88000000000 - 0xffffc8ffffffffff Size: 512.0 > GB > Linear Mapping: 0xffff888000000000 - 0xffffc87fffffffff Size: 64.0 TB > Linear Gap: 0xffff800000000000 - 0xffff887fffffffff Size: 8.5 TB > Gap Hole: 0x0000800000000000 - 0xffff7fffffffffff Size: > 16776960.0 TB > User Space: 0x0000000000000000 - 0x00007fffffffffff Size: 128.0 > TB > The output looks better! > Signed-off-by: Rongwei Wang <[email protected]> > --- > defs.h | 14 +++- > help.c | 7 +- > memory.c | 11 +++ > x86_64.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 266 insertions(+), 2 deletions(-) > > diff --git a/defs.h b/defs.h > index 4fecb83..a3f00d5 100644 > --- a/defs.h > +++ b/defs.h > @@ -4100,12 +4100,23 @@ typedef signed int s32; > #define __PHYSICAL_MASK_SHIFT_5LEVEL 52 > #define __PHYSICAL_MASK_SHIFT (machdep->machspec->physical_mask_shift) > #define __PHYSICAL_MASK ((1UL << __PHYSICAL_MASK_SHIFT) - 1) > -#define __VIRTUAL_MASK_SHIFT 48 > +#define __VIRTUAL_MASK_SHIFT 47 > #define __VIRTUAL_MASK ((1UL << __VIRTUAL_MASK_SHIFT) - 1) > #define PAGE_SHIFT 12 > #define PAGE_SIZE (1UL << PAGE_SHIFT) > #define PHYSICAL_PAGE_MASK (~(PAGE_SIZE-1) & __PHYSICAL_MASK ) > > + > +#define KASAN_SHADOW_OFFSET 0xdffffc0000000000 > +#define KASAN_SHADOW_SCALE_SHIFT 3 > + > +#define KASAN_SHADOW_START (KASAN_SHADOW_OFFSET + \ > + ((-1UL << __VIRTUAL_MASK_SHIFT) >> \ > + KASAN_SHADOW_SCALE_SHIFT)) > +#define KASAN_SHADOW_END (KASAN_SHADOW_START + \ > + (1ULL << (__VIRTUAL_MASK_SHIFT - \ > + KASAN_SHADOW_SCALE_SHIFT))) These code are copied from kernel, which take 0xdffffc0000000000 as the default value. Please note the value is configurable as CONFIG_KASAN_SHADOW_OFFSET. If changed then the address calc is faulty. I suggest to dump the enum KASAN_SHADOW_START_NR from address_markers_idx, then dump address_markers[KASAN_SHADOW_START_NR] to get the KASAN_SHADOW_START. see: arch/x86/mm/dump_pagetables.c:pt_dump_init(). By this way, we can ensure the KASAN_SHADOW_START value is always correct. > + > #define _PAGE_BIT_NX 63 > #define _PAGE_PRESENT 0x001 > #define _PAGE_RW 0x002 > @@ -5911,6 +5922,7 @@ int phys_to_page(physaddr_t, ulong *); > int generic_get_kvaddr_ranges(struct vaddr_range *); > int l1_cache_size(void); > int dumpfile_memory(int); > +void dump_memory_layout(void); > #define DUMPFILE_MEM_USED (1) > #define DUMPFILE_FREE_MEM (2) > #define DUMPFILE_MEM_DUMP (3) > diff --git a/help.c b/help.c > index 5d61e0d..cd54744 100644 > --- a/help.c > +++ b/help.c > @@ -538,7 +538,7 @@ cmd_help(void) > oflag = 0; > > while ((c = getopt(argcnt, args, > - "efNDdmM:ngcaBbHhkKsvVoptTzLOr")) != EOF) { > + "efNDdmM:ngcaBbHhkKsvVoptTzLOrl")) != EOF) { > switch(c) > { > case 'e': > @@ -666,6 +666,7 @@ cmd_help(void) > fprintf(fp, " -v - vm_table\n"); > fprintf(fp, " -V - vm_table (verbose)\n"); > fprintf(fp, " -z - help options\n"); > + fprintf(fp, " -l - show memory layout\n"); > return; > > case 'L': > @@ -676,6 +677,10 @@ cmd_help(void) > dump_registers(); > return; > > + case 'l': > + dump_memory_layout(); > + return; > + > default: > argerrs++; > break; > diff --git a/memory.c b/memory.c > index 400d31a..55ed2f1 100644 > --- a/memory.c > +++ b/memory.c > @@ -17657,6 +17657,17 @@ dumpfile_memory(int cmd) > return retval; > } > > +#ifdef X86_64 > +extern void x86_64_dump_memory_layout(void); > +#endif > + > +void dump_memory_layout(void) > +{ > +#ifdef X86_64 > + x86_64_dump_memory_layout(); > +#endif How about: #if defined(X86_64) x86_64_dump_memory_layout(); #elif defined(arm64) ... #else fprintf(fp, "Not implemented for this arch"); #endif So the archs which not implemented can read an error message, rather than output nothing. > +} > + > /* > * Functions for sparse mem support > */ > diff --git a/x86_64.c b/x86_64.c > index d7da536..bfb53b4 100644 > --- a/x86_64.c > +++ b/x86_64.c > @@ -1151,6 +1151,242 @@ x86_64_dump_machdep_table(ulong arg) > fprintf(fp, "excpetion_functions_orig\n"); > } > > +#define MAX_LAYOUT 32 > +struct mem_segment { > + char name[64]; > + char desc[64]; > + unsigned long start; > + unsigned long end; > +}; > + > +struct mem_layout { > + int count; > + struct mem_segment segs[MAX_LAYOUT]; > +}; > + > +char* format_bytes(unsigned long bytes, char* buffer, int buffer_size) > +{ > + const char* units[] = {"B", "KB", "MB", "GB", "TB"}; > + int i = 0; > + double readable_size = (double)bytes; > + > + /* Handle the edge case of zero bytes */ > + if (bytes == 0) { > + snprintf(buffer, buffer_size, "0 B"); > + return buffer; > + } > + > + /* Handle negative values if necessary, though size is typically > non-negative */ > + if (bytes < 0) { > + snprintf(buffer, buffer_size, "Invalid size"); > + return buffer; > + } > + > + while (readable_size >= 1024 && i < (sizeof(units) / sizeof(units[0]) > - 1)) { > + readable_size /= 1024; > + i++; > + } > + > + memset(buffer, '\0', buffer_size); > + snprintf(buffer, buffer_size, "%.1f %s", readable_size, units[i]); > + > + return buffer; > +} > + > +int compare_segments(const void *a, const void *b) > +{ > + const struct mem_segment *seg_a = (const struct mem_segment *)a; > + const struct mem_segment *seg_b = (const struct mem_segment *)b; > + > + if (seg_a->start > seg_b->start) return -1; > + if (seg_a->start < seg_b->start) return 1; > + return 0; > +} > + > +void print_layout(struct mem_layout *layout) > +{ > + struct mem_segment *segs = layout->segs; > + struct mem_segment seg; > + int i; > + > + if (layout == NULL) > + return; > + > + for (i=0; i<layout->count; i++) { > + seg = segs[i]; > + > + fprintf(fp, "%s:\t 0x%016lx - 0x%016lx\t Size: %s\n", > seg.name, seg.start, > + seg.end, seg.desc); > + } > +} > + > +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) > +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) > +#define round_down(x, y) ((x) & ~__round_mask(x, y)) > + > +/* > + * memory layout: > + * > + * Gap Area: 0xffffffffff7ff001 - 0xffffffffffffffff Size: > 8.0 MB > + * Fixmap Area: 0xffffffffff578000 - 0xffffffffff7ff000 Size: > 2.5 MB > + * Gap Area: 0xffffffffff000001 - 0xffffffffff577fff Size: > 5.5 MB > + * Module Area: 0xffffffffa0000000 - 0xffffffffff000000 Size: > 1.5 GB > + * Gap Area: 0xffffffff84826001 - 0xffffffff9fffffff Size: > 439.9 MB > + * Kernel Image: 0xffffffff81000000 - 0xffffffff84826000 Size: > 56.1 MB > + * Gap Area: 0xffffeb0000000000 - 0xffffffff80ffffff Size: > 21.0 TB > + * Vmemmap Area: 0xffffea0000000000 - 0xffffeaffffffffff Size: > 1.0 TB > + * Gap Area: 0xffffe90000000000 - 0xffffe9ffffffffff Size: > 1.0 TB > + * Vmalloc Area: 0xffffc90000000000 - 0xffffe8ffffffffff Size: > 32.0 TB > + * Gap Area: 0xffffc88000000000 - 0xffffc8ffffffffff Size: > 512.0 GB > + * Linear Mapping: 0xffff888000000000 - 0xffffc87fffffffff Size: > 64.0 TB > + * Linear Gap: 0xffff800000000000 - 0xffff887fffffffff Size: > 8.5 TB > + * Gap Area: 0x0000800000000000 - 0xffff7fffffffffff > Size:16776960.0 TB > + * User Space: 0x0000000000000000 - 0x00007fffffffffff Size: > 128.0 TB > + * > + * kernel space: > + * _end, _text > + * vmemmap_end: ms->vmemmap_end > + * vmemmap_vaddr: ms->vmemmap_vaddr > + * vmalloc_end: ms->vmalloc_end > + * vmalloc_start_addr: ms->vmalloc_start_addr > + * page_offset_base: ms->page_offset > + * > + * user space: > + * userspace_top: ms->userspace_top > + * > + */ > +void x86_64_dump_memory_layout(void) > +{ > + struct mem_layout *layout = NULL; > + ulong text_start, text_end; > + struct machine_specific *ms = machdep->machspec; > + int i, next_idx; > + char size_buf[20]; > + long value = 0; > + > + layout = malloc(sizeof(struct mem_layout)); Please use GETBUF()/FREEBUF() rather than malloc/free in crash. They are crash's mem allocator. > + if (layout == NULL || layout->count == 0) { The layout memory is not initialised, the content can be any value, why check layout->count == 0 ? > + printf("Layout is empty, nothing to print.\n"); Please use fprintf(fp, "string"); instead. > + return; > + } > + > + /* Create a temporary copy to sort for printing, preserving the > original order. */ > + struct mem_segment *segments = layout->segs; > + if(!segments) { > + perror("Failed to allocate memory for sorting"); > + return; > + } I guess no need to check layout->segs == NULL, because layout->segs is within mem_layout and has been malloced previously, it will never be NULL. > + > + if (!symbol_exists("_text")) > + return; > + else > + text_start = symbol_value("_text"); > + > + if (!symbol_exists("_end")) > + return; > + else > + text_end = symbol_value("_end"); > + > + snprintf(segments[0].name, 64, "Kernel Image"); > + snprintf(segments[0].desc, 64, "%s", > + format_bytes(text_end - text_start + 1, size_buf, 20)); > + segments[0].start = text_start; > + segments[0].end = text_end; > + > + snprintf(segments[1].name, 64, "Vmemmap Area"); > + snprintf(segments[1].desc, 64, "%s", > + format_bytes(ms->vmemmap_end - ms->vmemmap_vaddr + 1, > size_buf, 20)); > + segments[1].start = (ulong)ms->vmemmap_vaddr; > + segments[1].end = (ulong)ms->vmemmap_end; > + > + snprintf(segments[2].name, 64, "Module Area"); > + snprintf(segments[2].desc, 64, "%s", > + format_bytes(ms->modules_end - ms->modules_vaddr + 1, > size_buf, 20)); > + segments[2].start = (ulong)ms->modules_vaddr; > + segments[2].end = (ulong)ms->modules_end; > + > + snprintf(segments[3].name, 64, "Vmalloc Area"); > + snprintf(segments[3].desc, 64, "%s", > + format_bytes(ms->vmalloc_end - ms->vmalloc_start_addr + 1, > size_buf, 20)); > + segments[3].start = (ulong)ms->vmalloc_start_addr; > + segments[3].end = (ulong)ms->vmalloc_end; > + > + snprintf(segments[4].name, 64, "Linear Mapping"); > + segments[4].start = (ulong)ms->page_offset; > + segments[4].end = (ulong)ms->page_offset + (1UL << > machdep->max_physmem_bits) - 1; > + snprintf(segments[4].desc, 64, "%s", > + format_bytes(1UL << machdep->max_physmem_bits, size_buf, > 20)); > + > + snprintf(segments[5].name, 64, "User Space"); > + snprintf(segments[5].desc, 64, "%s", > + format_bytes((ulong)ms->userspace_top, size_buf, 20)); > + segments[5].start = 0UL; > + segments[5].end = (ulong)ms->userspace_top - 1; > + > + snprintf(segments[6].name, 64, "Linear Gap"); > + segments[6].start = -1UL - (1UL << __VIRTUAL_MASK_SHIFT) + 1; > + segments[6].end = (ulong)ms->page_offset - 1; > + snprintf(segments[6].desc, 64, "%s", > + format_bytes(segments[6].end - segments[6].start + 1, > size_buf, 20)); > + > + layout->count = 7; > + if (kernel_symbol_exists("kasan_init")) { > + snprintf(segments[7].name, 64, "KASAN"); > + segments[7].start = KASAN_SHADOW_START; > + segments[7].end = KASAN_SHADOW_END; > + snprintf(segments[7].desc, 64, "%s", > + format_bytes(segments[7].end - segments[7].start + > 1, size_buf, 20)); > + layout->count++; > + } > + > + if (enumerator_value("__end_of_permanent_fixed_addresses", &value)) { > + unsigned fixaddr_size = 0; > + int idx = layout->count; > + > + fixaddr_size = value << PAGE_SHIFT; > + > + snprintf(segments[7].name, 64, "Fixmap Area"); > + segments[idx].end = round_up(VSYSCALL_START + PAGE_SIZE, 1 << > PMD_SHIFT) - PAGE_SIZE; > + segments[idx].start = segments[idx].end - fixaddr_size; > + > + snprintf(segments[idx].desc, 64, "%s", > + format_bytes(segments[idx].end - segments[idx].start > + 1, size_buf, 20)); > + layout->count++; > + } > + > + /* Sort segments from highest address to lowest. */ > + qsort(segments, layout->count, sizeof(struct mem_segment), > compare_segments); > + > + next_idx = layout->count; > + /* Insert gap area */ > + for (i=0; i<layout->count; i++) { > + unsigned long prev_start; > + unsigned long end = segments[i].end; > + > + if (i == 0) > + prev_start = -1UL; > + else > + prev_start = segments[i-1].start; > + > + if (prev_start == (end + 1)) > + continue; > + > + snprintf(segments[next_idx].name, 64, "Gap Hole"); > + segments[next_idx].start = end + 1; > + segments[next_idx].end = (i == 0) ? prev_start : prev_start - > 1; > + snprintf(segments[next_idx].desc, 64, "%s", > + format_bytes(segments[next_idx].end - > segments[next_idx].start + 1, size_buf, 20)); > + > + next_idx++; > + } > + > + layout->count = next_idx; > + qsort(segments, layout->count, sizeof(struct mem_segment), > compare_segments); > + > + print_layout(layout); > + free(layout); > +} > + > /* > * Gather the cpu_pda array info, updating any smp-related items that > * were possibly bypassed or improperly initialized in kernel_init(). > -- > 2.43.5 > -- Crash-utility mailing list -- [email protected] To unsubscribe send an email to [email protected] https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/ Contribution Guidelines: https://github.com/crash-utility/crash/wiki
