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