1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <zircon/compiler.h> 6#include <zircon/status.h> 7#include <zircon/syscalls.h> 8#include <zircon/syscalls/object.h> 9#include <zircon/types.h> 10#include <pretty/sizes.h> 11#include <task-utils/get.h> 12#include <task-utils/walker.h> 13 14#include <inttypes.h> 15#include <limits.h> 16#include <stdio.h> 17#include <stdlib.h> 18#include <string.h> 19 20// Reads the zx_info_maps_t entries for the process. 21// Caller is responsible for the |out_maps| pointer. 22zx_status_t get_maps(zx_koid_t koid, zx_handle_t process, 23 zx_info_maps_t** out_maps, size_t* out_count, 24 size_t* out_avail) { 25 size_t count = 4096; // Should be more than enough. 26 zx_info_maps_t* maps = NULL; 27 int pass = 3; 28 while (true) { 29 maps = (zx_info_maps_t*)realloc(maps, count * sizeof(zx_info_maps_t)); 30 31 size_t actual; 32 size_t avail; 33 zx_status_t s = zx_object_get_info(process, ZX_INFO_PROCESS_MAPS, 34 maps, count * sizeof(zx_info_maps_t), 35 &actual, &avail); 36 if (s != ZX_OK) { 37 fprintf(stderr, 38 "ERROR: couldn't get maps for process with koid %" PRIu64 39 ": %s (%d)\n", 40 koid, zx_status_get_string(s), s); 41 free(maps); 42 return s; 43 } 44 if (actual < avail && pass-- > 0) { 45 count = (avail * 10) / 9; 46 continue; 47 } 48 *out_maps = maps; 49 *out_count = actual; 50 *out_avail = avail; 51 return ZX_OK; 52 } 53} 54 55void print_ptr(zx_vaddr_t addr) { 56 if (addr <= UINT32_MAX) { 57 printf("________%08" PRIx32, (uint32_t)addr); 58 } else { 59 printf("%016" PRIx64, addr); 60 } 61} 62 63void print_range(zx_vaddr_t addr, size_t size) { 64 print_ptr(addr); 65 printf("-"); 66 print_ptr(addr + size); 67} 68 69void print_mmu_flags(unsigned int mmu_flags) { 70 if (mmu_flags & ZX_VM_PERM_READ) { 71 printf("r"); 72 } else { 73 printf("-"); 74 } 75 if (mmu_flags & ZX_VM_PERM_WRITE) { 76 printf("w"); 77 } else { 78 printf("-"); 79 } 80 if (mmu_flags & ZX_VM_PERM_EXECUTE) { 81 printf("x"); 82 } else { 83 printf("-"); 84 } 85} 86 87// Pretty-prints the contents of |maps| to stdout. 88zx_status_t print_maps(zx_info_maps_t* maps, size_t count, size_t avail) { 89 size_t max_depth = 2; 90 for (size_t i = 0; i < count; i++) { 91 zx_info_maps_t* e = maps + i; 92 if (e->depth > max_depth) { 93 max_depth = e->depth; 94 } 95 } 96 97 char size_str[MAX_FORMAT_SIZE_LEN]; 98 for (size_t i = 0; i < count; i++) { 99 zx_info_maps_t* e = maps + i; 100 char tc = 0; 101 switch (e->type) { 102 case ZX_INFO_MAPS_TYPE_ASPACE: 103 tc = 'A'; 104 break; 105 case ZX_INFO_MAPS_TYPE_VMAR: 106 tc = 'R'; 107 break; 108 case ZX_INFO_MAPS_TYPE_MAPPING: 109 tc = 'M'; 110 break; 111 default: 112 break; 113 } 114 if (tc == 0) { 115 continue; 116 } 117 118 // Print the type character, indented to show its place in the tree. 119 if (e->depth < 2) { 120 // This is the aspace or root vmar. 121 // They'll always exist and always be the parents of everything. 122 printf("/%c%*s", tc, (int)(max_depth - 3), ""); 123 } else { 124 printf("%*s%c%*s", (int)(e->depth - 2), "", 125 tc, (int)(max_depth - e->depth), ""); 126 } 127 128 printf(" "); 129 print_range(e->base, e->size); 130 131 int size_width; 132 if (e->type == ZX_INFO_MAPS_TYPE_MAPPING) { 133 printf(" "); 134 print_mmu_flags(e->u.mapping.mmu_flags); 135 size_width = 5; 136 } else { 137 size_width = 9; 138 } 139 140 format_size(size_str, sizeof(size_str), e->size); 141 printf(" %*s:sz", size_width, size_str); 142 if (e->type == ZX_INFO_MAPS_TYPE_MAPPING) { 143 const zx_info_maps_mapping_t* u = &e->u.mapping; 144 format_size(size_str, sizeof(size_str), 145 u->committed_pages * PAGE_SIZE); 146 printf(" %4s:res", size_str); 147 printf(" %5" PRIu64 ":vmo", u->vmo_koid); 148 } else { 149 printf("%19s", ""); 150 } 151 152 printf(" '%s'\n", e->name); 153 } 154 if (avail > count) { 155 printf("[%zd entries truncated]\n", avail - count); 156 } 157 return ZX_OK; 158} 159 160void try_help(char** argv) { 161 const char* c = argv[1]; 162 while (*c == '-') { 163 c++; 164 } 165 if (strcmp(c, "help") != 0) { 166 return; 167 } 168 169 printf("Usage: %s <process-koid>\n", argv[0]); 170 printf("\n"); 171 printf("Dumps a process's memory maps to stdout.\n"); 172 printf("\n"); 173 printf("First column:\n"); 174 printf(" \"/A\" -- Process address space\n"); 175 printf(" \"/R\" -- Root VMAR\n"); 176 printf(" \"R\" -- VMAR (R for Region)\n"); 177 printf(" \"M\" -- Mapping\n"); 178 printf("\n"); 179 printf(" Indentation indicates parent/child relationship.\n"); 180 exit(0); 181} 182 183__NO_RETURN void usage(const char* argv0) { 184 fprintf(stderr, "Usage: %s <process-koid>|help\n", argv0); 185 exit(1); 186} 187 188int main(int argc, char** argv) { 189 if (argc != 2) { 190 usage(argv[0]); 191 } 192 try_help(argv); 193 char* end; 194 zx_koid_t koid = strtoull(argv[1], &end, 0); 195 if (argv[1][0] == '\0' || *end != '\0') { 196 fprintf(stderr, "ERROR: \"%s\" is not a number\n", argv[1]); 197 usage(argv[0]); 198 } 199 200 zx_handle_t process; 201 zx_obj_type_t type; 202 zx_status_t s = get_task_by_koid(koid, &type, &process); 203 if (s == ZX_OK && type != ZX_OBJ_TYPE_PROCESS) { 204 zx_handle_close(process); 205 s = ZX_ERR_WRONG_TYPE; 206 } 207 if (s != ZX_OK) { 208 fprintf(stderr, 209 "ERROR: couldn't find process with koid %" PRIu64 ": %s (%d)\n", 210 koid, zx_status_get_string(s), s); 211 usage(argv[0]); 212 } 213 214 zx_info_maps_t* maps; 215 size_t count; 216 size_t avail; 217 s = get_maps(koid, process, &maps, &count, &avail); 218 zx_handle_close(process); 219 if (s != ZX_OK) { 220 return 1; 221 } 222 s = print_maps(maps, count, avail); 223 free(maps); 224 return s == ZX_OK ? 0 : 1; 225} 226