1
+ /*
2
+ * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
3
+ * Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates.
4
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
+ *
6
+ * This code is free software; you can redistribute it and/or modify it
7
+ * under the terms of the GNU General Public License version 2 only, as
8
+ * published by the Free Software Foundation.
9
+ *
10
+ * This code is distributed in the hope that it will be useful, but WITHOUT
11
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
+ * version 2 for more details (a copy is included in the LICENSE file that
14
+ * accompanied this code).
15
+ *
16
+ * You should have received a copy of the GNU General Public License version
17
+ * 2 along with this work; if not, write to the Free Software Foundation,
18
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
+ *
20
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
+ * or visit www.oracle.com if you need additional information or have any
22
+ * questions.
23
+ *
24
+ */
25
+
26
+ #if defined(__APPLE__)
27
+
28
+ #include " precompiled.hpp"
29
+
30
+ #include " nmt/memMapPrinter.hpp"
31
+ #include " runtime/os.hpp"
32
+ #include " utilities/align.hpp"
33
+ #include " utilities/globalDefinitions.hpp"
34
+ #include " utilities/powerOfTwo.hpp"
35
+
36
+ #include < limits.h>
37
+ #include < stdio.h>
38
+ #include < stdlib.h>
39
+ #include < libproc.h>
40
+ #include < unistd.h>
41
+
42
+ #include < mach/vm_inherit.h>
43
+ #include < mach/vm_prot.h>
44
+ #include < mach/mach_vm.h>
45
+
46
+ // maximum number of mapping records returned
47
+ static const int MAX_REGIONS_RETURNED = 1000000 ;
48
+
49
+ // ::mmap() on MacOS is a layer on top of Mach system calls, and will allocate in 128MB chunks.
50
+ // This code will coalesce a series of identical 128GB chunks (maybe followed by one smaller chunk
51
+ // with identical flags) into one.
52
+ // Unfortunately, two or more identically allocated contiguous sections will appear as one, if the
53
+ // first section is size 128MB. vmmap(1) has the same issue.
54
+ static const int MACOS_PARTIAL_ALLOCATION_SIZE = 128 * M;
55
+
56
+ class MappingInfo {
57
+ proc_regioninfo _rinfo;
58
+ public:
59
+ const char * _address;
60
+ size_t _size;
61
+ stringStream _share_buffer;
62
+ stringStream _type_buffer;
63
+ stringStream _protect_buffer;
64
+ stringStream _file_name;
65
+ const char * _tag_text;
66
+
67
+ MappingInfo () : _address(nullptr ), _size(0 ), _tag_text(nullptr ) {}
68
+
69
+ void reset () {
70
+ _share_buffer.reset ();
71
+ _protect_buffer.reset ();
72
+ _type_buffer.reset ();
73
+ _file_name.reset ();
74
+ _tag_text = nullptr ;
75
+ }
76
+
77
+ bool canCombine (const proc_regionwithpathinfo& mem_info) {
78
+ const proc_regioninfo& n = mem_info.prp_prinfo ;
79
+ bool cc = _rinfo.pri_size == MACOS_PARTIAL_ALLOCATION_SIZE
80
+ && n.pri_address == (_rinfo.pri_address + _size)
81
+ && n.pri_protection == _rinfo.pri_protection
82
+ && n.pri_max_protection == _rinfo.pri_max_protection
83
+ && n.pri_user_tag == _rinfo.pri_user_tag
84
+ && n.pri_share_mode == _rinfo.pri_share_mode
85
+ && n.pri_offset == 0 ;
86
+ return cc;
87
+ }
88
+
89
+ void combineWithFollowing (const proc_regionwithpathinfo& mem_info) {
90
+ _size += mem_info.prp_prinfo .pri_size ;
91
+ }
92
+
93
+ void process (const proc_regionwithpathinfo& mem_info) {
94
+ reset ();
95
+
96
+ _rinfo = mem_info.prp_prinfo ;
97
+
98
+ _address = (const char *) _rinfo.pri_address ;
99
+ _size = _rinfo.pri_size ;
100
+
101
+ if (mem_info.prp_vip .vip_path [0 ] != ' \0 ' ) {
102
+ _file_name.print_raw (mem_info.prp_vip .vip_path );
103
+ }
104
+ // proc_regionfilename() seems to give bad results, so we don't try to use it here.
105
+
106
+ char prot[4 ];
107
+ char maxprot[4 ];
108
+ rwbits (_rinfo.pri_protection , prot);
109
+ rwbits (_rinfo.pri_max_protection , maxprot);
110
+ _protect_buffer.print (" %s/%s" , prot, maxprot);
111
+
112
+ get_share_mode (_share_buffer, _rinfo);
113
+ _tag_text = tagToStr (_rinfo.pri_user_tag );
114
+ }
115
+
116
+ static void get_share_mode (outputStream& out, const proc_regioninfo& rinfo) {
117
+ static const char * share_strings[] = {
118
+ " cow" , " pvt" , " ---" , " shr" , " tsh" , " p/a" , " s/a" , " lpg"
119
+ };
120
+ assert (SM_COW == 1 && SM_LARGE_PAGE == (sizeof (share_strings)/sizeof (share_strings[0 ])), " share_mode contants are out of range" ); // the +1 offset is intentional; see below
121
+ const bool valid_share_mode = rinfo.pri_share_mode >= SM_COW && rinfo.pri_share_mode <= SM_LARGE_PAGE;
122
+ if (valid_share_mode) {
123
+ int share_mode = rinfo.pri_share_mode ;
124
+ out.print_raw (share_strings[share_mode - 1 ]);
125
+ } else {
126
+ out.print_cr (" invalid pri_share_mode (%d)" , rinfo.pri_share_mode );
127
+ assert (valid_share_mode, " invalid pri_share_mode (%d)" , rinfo.pri_share_mode );
128
+ }
129
+ }
130
+
131
+ #define X1 (TAG, DESCR ) X2(TAG, DESCR)
132
+ #define X2 (TAG, DESCRIPTION ) case VM_MEMORY_ ## TAG: return # DESCRIPTION;
133
+ static const char * tagToStr (uint32_t user_tag) {
134
+ switch (user_tag) {
135
+ case 0 :
136
+ return 0 ;
137
+ X1 (MALLOC, malloc);
138
+ X1 (MALLOC_SMALL, malloc_small);
139
+ X1 (MALLOC_LARGE, malloc_large);
140
+ X1 (MALLOC_HUGE, malloc_huge);
141
+ X1 (SBRK, sbrk );
142
+ X1 (REALLOC, realloc);
143
+ X1 (MALLOC_TINY, malloc_tiny);
144
+ X1 (MALLOC_LARGE_REUSABLE, malloc_large_reusable);
145
+ X1 (MALLOC_LARGE_REUSED, malloc_lage_reused);
146
+ X1 (ANALYSIS_TOOL, analysis_tool);
147
+ X1 (MALLOC_NANO, malloc_nano);
148
+ X1 (MALLOC_MEDIUM, malloc_medium);
149
+ X1 (MALLOC_PROB_GUARD, malloc_prob_guard);
150
+ X1 (MACH_MSG, malloc_msg);
151
+ X1 (IOKIT, IOKit);
152
+ X1 (STACK, stack);
153
+ X1 (GUARD, guard);
154
+ X1 (SHARED_PMAP, shared_pmap);
155
+ X1 (DYLIB, dylib);
156
+ X1 (UNSHARED_PMAP, unshared_pmap);
157
+ X2 (APPKIT, AppKit);
158
+ X2 (FOUNDATION, Foundation);
159
+ X2 (COREGRAPHICS, CoreGraphics);
160
+ X2 (CORESERVICES, CoreServices); // is also VM_MEMORY_CARBON
161
+ X2 (JAVA, Java);
162
+ X2 (COREDATA, CoreData);
163
+ X1 (COREDATA_OBJECTIDS, CodeData_objectids);
164
+ X1 (ATS, ats);
165
+ X1 (DYLD, dyld);
166
+ X1 (DYLD_MALLOC, dyld_malloc);
167
+ X1 (SQLITE, sqlite);
168
+ X1 (JAVASCRIPT_CORE, javascript_core);
169
+ X1 (JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR, javascript_jit_executable_allocator);
170
+ X1 (JAVASCRIPT_JIT_REGISTER_FILE, javascript_jit_register_file);
171
+ X1 (OPENCL, OpenCL);
172
+ X2 (COREIMAGE, CoreImage);
173
+ X2 (IMAGEIO, ImageIO);
174
+ X2 (COREPROFILE, CoreProfile);
175
+ X1 (APPLICATION_SPECIFIC_1, application_specific_1);
176
+ X1 (APPLICATION_SPECIFIC_16, application_specific_16);
177
+ X1 (OS_ALLOC_ONCE, os_alloc_once);
178
+ X1 (GENEALOGY, genealogy);
179
+ default :
180
+ static char buffer[30 ];
181
+ snprintf (buffer, sizeof (buffer), " user_tag=0x%x(%d)" , user_tag, user_tag);
182
+ return buffer;
183
+ }
184
+ }
185
+
186
+ static void rwbits (int rw, char bits[4 ]) {
187
+ bits[0 ] = rw & VM_PROT_READ ? ' r' : ' -' ;
188
+ bits[1 ] = rw & VM_PROT_WRITE ? ' w' : ' -' ;
189
+ bits[2 ] = rw & VM_PROT_EXECUTE ? ' x' : ' -' ;
190
+ bits[3 ] = 0 ;
191
+ }
192
+ };
193
+
194
+ class ProcSmapsSummary {
195
+ unsigned _num_mappings;
196
+ size_t _private;
197
+ size_t _committed; // combined committed size
198
+ size_t _reserved; // reserved but not committed
199
+ size_t _shared; // combined shared size
200
+ size_t _swapped_out; // combined amount of swapped-out memory
201
+ public:
202
+ ProcSmapsSummary () : _num_mappings(0 ), _private(0 ),
203
+ _committed (0 ), _shared(0 ), _swapped_out(0 ) {}
204
+
205
+ void add_mapping (const proc_regioninfo& region_info) {
206
+ _num_mappings++;
207
+
208
+ bool is_private = region_info.pri_share_mode == SM_PRIVATE
209
+ || region_info.pri_share_mode == SM_PRIVATE_ALIASED;
210
+ bool is_shared = region_info.pri_share_mode == SM_SHARED
211
+ || region_info.pri_share_mode == SM_SHARED_ALIASED
212
+ || region_info.pri_share_mode == SM_TRUESHARED
213
+ || region_info.pri_share_mode == SM_COW;
214
+ bool is_committed = region_info.pri_share_mode == SM_EMPTY
215
+ && region_info.pri_max_protection == VM_PROT_ALL
216
+ && ((region_info.pri_protection & VM_PROT_DEFAULT) == VM_PROT_DEFAULT);
217
+ bool is_reserved = region_info.pri_share_mode == SM_EMPTY
218
+ && region_info.pri_max_protection == VM_PROT_ALL
219
+ && region_info.pri_protection == VM_PROT_NONE;
220
+
221
+ _private += is_private ? region_info.pri_size : 0 ;
222
+ _shared += is_shared ? region_info.pri_size : 0 ;
223
+ _swapped_out += region_info.pri_pages_swapped_out ;
224
+ _committed += is_committed ? region_info.pri_size : 0 ;
225
+ _reserved += is_reserved ? region_info.pri_size : 0 ;
226
+ }
227
+
228
+ void print_on (const MappingPrintSession& session) const {
229
+ outputStream* st = session.out ();
230
+
231
+ st->print_cr (" Number of mappings: %u" , _num_mappings);
232
+
233
+ task_vm_info vm_info;
234
+ mach_msg_type_number_t num_out = TASK_VM_INFO_COUNT;
235
+ kern_return_t err = task_info (mach_task_self (), TASK_VM_INFO, (task_info_t )(&vm_info), &num_out);
236
+ if (err == KERN_SUCCESS) {
237
+ st->print_cr (" vsize: %llu (%llu%s)" , vm_info.virtual_size , PROPERFMTARGS (vm_info.virtual_size ));
238
+ st->print_cr (" rss: %llu (%llu%s)" , vm_info.resident_size , PROPERFMTARGS (vm_info.resident_size ));
239
+ st->print_cr (" peak rss: %llu (%llu%s)" , vm_info.resident_size_peak , PROPERFMTARGS (vm_info.resident_size_peak ));
240
+ st->print_cr (" page size: %d (%ld%s)" , vm_info.page_size , PROPERFMTARGS ((size_t )vm_info.page_size ));
241
+ } else {
242
+ st->print_cr (" error getting vm_info %d" , err);
243
+ }
244
+ st->print_cr (" reserved: %zu (" PROPERFMT " )" , _reserved, PROPERFMTARGS (_reserved));
245
+ st->print_cr (" committed: %zu (" PROPERFMT " )" , _committed, PROPERFMTARGS (_committed));
246
+ st->print_cr (" private: %zu (" PROPERFMT " )" , _private, PROPERFMTARGS (_private));
247
+ st->print_cr (" shared: %zu (" PROPERFMT " )" , _shared, PROPERFMTARGS (_shared));
248
+ st->print_cr (" swapped out: %zu (" PROPERFMT " )" , _swapped_out * vm_info.page_size , PROPERFMTARGS (_swapped_out * vm_info.page_size ));
249
+ }
250
+ };
251
+
252
+ class ProcSmapsPrinter {
253
+ const MappingPrintSession& _session;
254
+ public:
255
+ ProcSmapsPrinter (const MappingPrintSession& session) :
256
+ _session (session)
257
+ {}
258
+
259
+ void print_single_mapping (const proc_regioninfo& region_info, const MappingInfo& mapping_info) const {
260
+ outputStream* st = _session.out ();
261
+ #define INDENT_BY (n ) \
262
+ if (st->fill_to (n) == 0 ) { \
263
+ st->print (" " ); \
264
+ }
265
+ st->print (" %#014.12llx-%#014.12llx" , (uint64_t )(mapping_info._address ), (uint64_t )(mapping_info._address + mapping_info._size ));
266
+ INDENT_BY (38 );
267
+ st->print (" %12ld" , mapping_info._size );
268
+ INDENT_BY (51 );
269
+ st->print (" %s" , mapping_info._protect_buffer .base ());
270
+ INDENT_BY (59 );
271
+ st->print (" %s" , mapping_info._share_buffer .base ());
272
+ st->print (" %s" , mapping_info._type_buffer .base ());
273
+ INDENT_BY (64 );
274
+ st->print (" %#11llx" , region_info.pri_offset );
275
+ INDENT_BY (77 );
276
+ if (_session.print_nmt_info_for_region ((const void *)mapping_info._address , (const void *)(mapping_info._address + mapping_info._size ))) {
277
+ st->print (" " );
278
+ } else {
279
+ const char * tag = mapping_info._tag_text ;
280
+ if (tag != nullptr ) {
281
+ st->print (" [%s] " , tag);
282
+ }
283
+ }
284
+
285
+ st->print_raw (mapping_info._file_name .base ());
286
+ st->cr ();
287
+
288
+ #undef INDENT_BY
289
+ }
290
+
291
+ void print_legend () const {
292
+ outputStream* st = _session.out ();
293
+ st->print_cr (" from, to, vsize: address range and size" );
294
+ st->print_cr (" prot: protection:" );
295
+ st->print_cr (" rwx: read / write / execute" );
296
+ st->print_cr (" share: share mode:" );
297
+ st->print_cr (" cow: copy on write" );
298
+ st->print_cr (" pvt: private" );
299
+ st->print_cr (" shr: shared" );
300
+ st->print_cr (" tsh: true shared" );
301
+ st->print_cr (" p/a: private aliased" );
302
+ st->print_cr (" s/a: shared aliased" );
303
+ st->print_cr (" lpg: large page" );
304
+ st->print_cr (" offset: offset from start of allocation block" );
305
+ st->print_cr (" vminfo: VM information (requires NMT)" );
306
+ st->print_cr (" file: file mapped, if mapping is not anonymous" );
307
+ {
308
+ streamIndentor si (st, 16 );
309
+ _session.print_nmt_flag_legend ();
310
+ }
311
+ st->print_cr (" file: file mapped, if mapping is not anonymous" );
312
+ }
313
+
314
+ void print_header () const {
315
+ outputStream* st = _session.out ();
316
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
317
+ // 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
318
+ // 0x000102890000-0x000102898000 32768 r--/r-- cow 0xc000 /Users/simont/dev/openjdk/jdk/build/macos-aarch64-fastdebug-shenandoah/images/jdk/bin/java
319
+ st->print_cr (" from to vsize prot share offset vminfo/file" );
320
+ st->print_cr (" ==================================================================================================" );
321
+ }
322
+ };
323
+
324
+ static bool is_interesting (const proc_regionwithpathinfo& info) {
325
+ return info.prp_prinfo .pri_share_mode != SM_EMPTY
326
+ || info.prp_prinfo .pri_user_tag != 0
327
+ || info.prp_vip .vip_path [0 ] != ' \0 '
328
+ || info.prp_prinfo .pri_protection != 0
329
+ || info.prp_prinfo .pri_max_protection != 0 ;
330
+ }
331
+
332
+ void MemMapPrinter::pd_print_all_mappings (const MappingPrintSession& session) {
333
+
334
+ ProcSmapsPrinter printer (session);
335
+ ProcSmapsSummary summary;
336
+ outputStream* const st = session.out ();
337
+ const pid_t pid = getpid ();
338
+
339
+ printer.print_legend ();
340
+ st->cr ();
341
+ printer.print_header ();
342
+
343
+ proc_regionwithpathinfo region_info_with_path;
344
+ MappingInfo mapping_info;
345
+ uint64_t address = 0 ;
346
+ int region_count = 0 ;
347
+ while (true ) {
348
+ if (++region_count > MAX_REGIONS_RETURNED) {
349
+ st->print_cr (" limit of %d regions reached (results inaccurate)" , region_count);
350
+ break ;
351
+ }
352
+ ::bzero (®ion_info_with_path, sizeof (region_info_with_path));
353
+ int retval = proc_pidinfo (pid, PROC_PIDREGIONPATHINFO, (uint64_t )address, ®ion_info_with_path, sizeof (region_info_with_path));
354
+ if (retval <= 0 ) {
355
+ break ;
356
+ } else if (retval < (int )sizeof (region_info_with_path)) {
357
+ st->print_cr (" proc_pidinfo() returned %d" , retval);
358
+ assert (false , " proc_pidinfo() returned %d" , retval);
359
+ }
360
+ proc_regioninfo& region_info = region_info_with_path.prp_prinfo ;
361
+ if (is_interesting (region_info_with_path)) {
362
+ if (mapping_info.canCombine (region_info_with_path)) {
363
+ mapping_info.combineWithFollowing (region_info_with_path);
364
+ } else {
365
+ // print previous mapping info
366
+ // avoid printing the empty info at the start
367
+ if (mapping_info._size != 0 ) {
368
+ printer.print_single_mapping (region_info, mapping_info);
369
+ }
370
+ summary.add_mapping (region_info);
371
+ mapping_info.process (region_info_with_path);
372
+ }
373
+ }
374
+ assert (region_info.pri_size > 0 , " size of region is 0" );
375
+ address = region_info.pri_address + region_info.pri_size ;
376
+ }
377
+ printer.print_single_mapping (region_info_with_path.prp_prinfo , mapping_info);
378
+ summary.add_mapping (region_info_with_path.prp_prinfo );
379
+ st->cr ();
380
+ summary.print_on (session);
381
+ st->cr ();
382
+ }
383
+ #endif // __APPLE__
0 commit comments