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.
get_maps(zx_koid_t koid,zx_handle_t process,zx_info_maps_t ** out_maps,size_t * out_count,size_t * out_avail)22 zx_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
print_ptr(zx_vaddr_t addr)55 void 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
print_range(zx_vaddr_t addr,size_t size)63 void print_range(zx_vaddr_t addr, size_t size) {
64 print_ptr(addr);
65 printf("-");
66 print_ptr(addr + size);
67 }
68
print_mmu_flags(unsigned int mmu_flags)69 void 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.
print_maps(zx_info_maps_t * maps,size_t count,size_t avail)88 zx_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
try_help(char ** argv)160 void 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
usage(const char * argv0)183 __NO_RETURN void usage(const char* argv0) {
184 fprintf(stderr, "Usage: %s <process-koid>|help\n", argv0);
185 exit(1);
186 }
187
main(int argc,char ** argv)188 int 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