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