blob: ea36ea286c04582c8465e2ba9f70689019b1c504 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Created by Greg Clayton on 6/26/07.
11//
12//===----------------------------------------------------------------------===//
13
14#include "MachVMMemory.h"
15#include "MachVMRegion.h"
16#include "DNBLog.h"
17#include <mach/mach_vm.h>
Han Ming Ongab3b8b22012-11-17 00:21:04 +000018#include <mach/shared_region.h>
Han Ming Ong8594ae82012-11-27 19:21:03 +000019#include <sys/sysctl.h>
Han Ming Ongb153c2f2013-05-22 21:32:42 +000020#include <dlfcn.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000021
22MachVMMemory::MachVMMemory() :
23 m_page_size (kInvalidPageSize),
24 m_err (0)
25{
26}
27
28MachVMMemory::~MachVMMemory()
29{
30}
31
32nub_size_t
Jason Molendabecd6392013-04-06 07:16:15 +000033MachVMMemory::PageSize(task_t task)
Chris Lattner30fdc8d2010-06-08 16:52:24 +000034{
35 if (m_page_size == kInvalidPageSize)
36 {
Jason Molendabecd6392013-04-06 07:16:15 +000037#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
38 if (task != TASK_NULL)
39 {
40 kern_return_t kr;
41 mach_msg_type_number_t info_count = TASK_VM_INFO_COUNT;
42 task_vm_info_data_t vm_info;
43 kr = task_info (task, TASK_VM_INFO, (task_info_t) &vm_info, &info_count);
44 if (kr == KERN_SUCCESS)
45 {
Jason Molendabef3f862013-04-06 07:28:38 +000046 DNBLogThreadedIf(LOG_TASK, "MachVMMemory::PageSize task_info returned page size of 0x%x", (int) vm_info.page_size);
Jason Molenda63742f12013-04-06 20:30:59 +000047 m_page_size = vm_info.page_size;
48 return m_page_size;
Jason Molendabecd6392013-04-06 07:16:15 +000049 }
Jason Molenda272ee612013-04-06 07:26:59 +000050 else
51 {
52 DNBLogThreadedIf(LOG_TASK, "MachVMMemory::PageSize task_info call failed to get page size, TASK_VM_INFO %d, TASK_VM_INFO_COUNT %d, kern return %d", TASK_VM_INFO, TASK_VM_INFO_COUNT, kr);
53 }
Jason Molendabecd6392013-04-06 07:16:15 +000054 }
55#endif
Chris Lattner30fdc8d2010-06-08 16:52:24 +000056 m_err = ::host_page_size( ::mach_host_self(), &m_page_size);
57 if (m_err.Fail())
58 m_page_size = 0;
59 }
60 return m_page_size;
61}
62
63nub_size_t
Jason Molendabecd6392013-04-06 07:16:15 +000064MachVMMemory::MaxBytesLeftInPage(task_t task, nub_addr_t addr, nub_size_t count)
Chris Lattner30fdc8d2010-06-08 16:52:24 +000065{
Jason Molendabecd6392013-04-06 07:16:15 +000066 const nub_size_t page_size = PageSize(task);
Chris Lattner30fdc8d2010-06-08 16:52:24 +000067 if (page_size > 0)
68 {
69 nub_size_t page_offset = (addr % page_size);
70 nub_size_t bytes_left_in_page = page_size - page_offset;
71 if (count > bytes_left_in_page)
72 count = bytes_left_in_page;
73 }
74 return count;
75}
76
Greg Claytonfc5dd292011-12-12 18:51:14 +000077nub_bool_t
Greg Clayton46fb5582011-11-18 07:03:08 +000078MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info)
Jason Molenda1f3966b2011-11-08 04:28:12 +000079{
80 MachVMRegion vmRegion(task);
81
Greg Clayton46fb5582011-11-18 07:03:08 +000082 if (vmRegion.GetRegionForAddress(address))
83 {
84 region_info->addr = vmRegion.StartAddress();
85 region_info->size = vmRegion.GetByteSize();
86 region_info->permissions = vmRegion.GetDNBPermissions();
Greg Clayton46fb5582011-11-18 07:03:08 +000087 }
Greg Claytonfc5dd292011-12-12 18:51:14 +000088 else
89 {
90 region_info->addr = address;
91 region_info->size = 0;
92 if (vmRegion.GetError().Success())
93 {
94 // vmRegion.GetRegionForAddress() return false, indicating that "address"
95 // wasn't in a valid region, but the "vmRegion" info was successfully
96 // read from the task which means the info describes the next valid
97 // region from which we can infer the size of this invalid region
98 mach_vm_address_t start_addr = vmRegion.StartAddress();
99 if (address < start_addr)
100 region_info->size = start_addr - address;
101 }
Greg Claytona2715cf2014-06-13 00:54:12 +0000102 // If we can't get any info about the size from the next region it means
103 // we asked about an address that was past all mappings, so the size
104 // of this region will take up all remaining address space.
Greg Claytonfc5dd292011-12-12 18:51:14 +0000105 if (region_info->size == 0)
Greg Claytona2715cf2014-06-13 00:54:12 +0000106 region_info->size = INVALID_NUB_ADDRESS - region_info->addr;
Greg Claytonfc5dd292011-12-12 18:51:14 +0000107
108 // Not readable, writeable or executable
109 region_info->permissions = 0;
110 }
111 return true;
Jason Molenda1f3966b2011-11-08 04:28:12 +0000112}
113
Han Ming Ong8594ae82012-11-27 19:21:03 +0000114// For integrated graphics chip, this makes the accounting info for 'wired' memory more like top.
Jason Molendabecd6392013-04-06 07:16:15 +0000115uint64_t
116MachVMMemory::GetStolenPages(task_t task)
Han Ming Ong8594ae82012-11-27 19:21:03 +0000117{
118 static uint64_t stolenPages = 0;
119 static bool calculated = false;
120 if (calculated) return stolenPages;
121
122 static int mib_reserved[CTL_MAXNAME];
123 static int mib_unusable[CTL_MAXNAME];
124 static int mib_other[CTL_MAXNAME];
125 static size_t mib_reserved_len = 0;
126 static size_t mib_unusable_len = 0;
127 static size_t mib_other_len = 0;
128 int r;
129
130 /* This can be used for testing: */
131 //tsamp->pages_stolen = (256 * 1024 * 1024ULL) / tsamp->pagesize;
132
133 if(0 == mib_reserved_len)
134 {
135 mib_reserved_len = CTL_MAXNAME;
136
137 r = sysctlnametomib("machdep.memmap.Reserved", mib_reserved,
138 &mib_reserved_len);
139
140 if(-1 == r)
141 {
142 mib_reserved_len = 0;
143 return 0;
144 }
145
146 mib_unusable_len = CTL_MAXNAME;
147
148 r = sysctlnametomib("machdep.memmap.Unusable", mib_unusable,
149 &mib_unusable_len);
150
151 if(-1 == r)
152 {
153 mib_reserved_len = 0;
154 return 0;
155 }
156
157
158 mib_other_len = CTL_MAXNAME;
159
160 r = sysctlnametomib("machdep.memmap.Other", mib_other,
161 &mib_other_len);
162
163 if(-1 == r)
164 {
165 mib_reserved_len = 0;
166 return 0;
167 }
168 }
169
170 if(mib_reserved_len > 0 && mib_unusable_len > 0 && mib_other_len > 0)
171 {
172 uint64_t reserved = 0, unusable = 0, other = 0;
173 size_t reserved_len;
174 size_t unusable_len;
175 size_t other_len;
176
177 reserved_len = sizeof(reserved);
178 unusable_len = sizeof(unusable);
179 other_len = sizeof(other);
180
181 /* These are all declared as QUAD/uint64_t sysctls in the kernel. */
182
Greg Claytonee2ed522015-03-09 19:45:23 +0000183 if (sysctl (mib_reserved,
184 static_cast<u_int>(mib_reserved_len),
185 &reserved,
186 &reserved_len,
187 NULL,
188 0))
Han Ming Ong8594ae82012-11-27 19:21:03 +0000189 {
190 return 0;
191 }
192
Greg Claytonee2ed522015-03-09 19:45:23 +0000193 if (sysctl (mib_unusable,
194 static_cast<u_int>(mib_unusable_len),
195 &unusable,
196 &unusable_len,
197 NULL,
198 0))
Han Ming Ong8594ae82012-11-27 19:21:03 +0000199 {
200 return 0;
201 }
202
Greg Claytonee2ed522015-03-09 19:45:23 +0000203 if (sysctl (mib_other,
204 static_cast<u_int>(mib_other_len),
205 &other,
206 &other_len,
207 NULL,
208 0))
Han Ming Ong8594ae82012-11-27 19:21:03 +0000209 {
210 return 0;
211 }
212
Greg Claytonee2ed522015-03-09 19:45:23 +0000213 if (reserved_len == sizeof(reserved) &&
214 unusable_len == sizeof(unusable) &&
215 other_len == sizeof(other))
Han Ming Ong8594ae82012-11-27 19:21:03 +0000216 {
217 uint64_t stolen = reserved + unusable + other;
218 uint64_t mb128 = 128 * 1024 * 1024ULL;
219
220 if(stolen >= mb128)
221 {
222 stolen = (stolen & ~((128 * 1024 * 1024ULL) - 1)); // rounding down
Jason Molendafe806902013-05-04 00:39:52 +0000223 stolenPages = stolen / PageSize (task);
Han Ming Ong8594ae82012-11-27 19:21:03 +0000224 }
225 }
226 }
227
228 calculated = true;
229 return stolenPages;
230}
231
232static uint64_t GetPhysicalMemory()
233{
234 // This doesn't change often at all. No need to poll each time.
235 static uint64_t physical_memory = 0;
236 static bool calculated = false;
237 if (calculated) return physical_memory;
238
239 int mib[2];
240 mib[0] = CTL_HW;
241 mib[1] = HW_MEMSIZE;
242 size_t len = sizeof(physical_memory);
243 sysctl(mib, 2, &physical_memory, &len, NULL, 0);
244 return physical_memory;
245}
246
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000247// rsize and dirty_size is not adjusted for dyld shared cache and multiple __LINKEDIT segment, as in vmmap. In practice, dirty_size doesn't differ much but rsize may. There is performance penalty for the adjustment. Right now, only use the dirty_size.
Jason Molendabecd6392013-04-06 07:16:15 +0000248void
249MachVMMemory::GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t &dirty_size)
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000250{
Han Ming Ongb4e1d4c2013-06-26 22:52:37 +0000251#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
252
253 task_vm_info_data_t vm_info;
254 mach_msg_type_number_t info_count;
255 kern_return_t kr;
256
257 info_count = TASK_VM_INFO_COUNT;
Han Ming Ongb4e1d4c2013-06-26 22:52:37 +0000258 kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count);
Han Ming Ongb4e1d4c2013-06-26 22:52:37 +0000259 if (kr == KERN_SUCCESS)
260 dirty_size = vm_info.internal;
Han Ming Ongb4e1d4c2013-06-26 22:52:37 +0000261#endif
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000262}
263
264// Test whether the virtual address is within the architecture's shared region.
265static bool InSharedRegion(mach_vm_address_t addr, cpu_type_t type)
266{
Jason Molendad251c9d2012-11-17 01:41:04 +0000267 mach_vm_address_t base = 0, size = 0;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000268
Jason Molendad251c9d2012-11-17 01:41:04 +0000269 switch(type) {
Jason Molendaa3329782014-03-29 18:54:20 +0000270#if defined (CPU_TYPE_ARM64) && defined (SHARED_REGION_BASE_ARM64)
271 case CPU_TYPE_ARM64:
272 base = SHARED_REGION_BASE_ARM64;
273 size = SHARED_REGION_SIZE_ARM64;
274 break;
275#endif
276
Jason Molendad251c9d2012-11-17 01:41:04 +0000277 case CPU_TYPE_ARM:
278 base = SHARED_REGION_BASE_ARM;
279 size = SHARED_REGION_SIZE_ARM;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000280 break;
Jason Molendaa3329782014-03-29 18:54:20 +0000281
Jason Molendad251c9d2012-11-17 01:41:04 +0000282 case CPU_TYPE_X86_64:
283 base = SHARED_REGION_BASE_X86_64;
284 size = SHARED_REGION_SIZE_X86_64;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000285 break;
286
Jason Molendad251c9d2012-11-17 01:41:04 +0000287 case CPU_TYPE_I386:
288 base = SHARED_REGION_BASE_I386;
289 size = SHARED_REGION_SIZE_I386;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000290 break;
291
Jason Molendad251c9d2012-11-17 01:41:04 +0000292 default: {
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000293 // Log error abut unknown CPU type
294 break;
Jason Molendad251c9d2012-11-17 01:41:04 +0000295 }
296 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000297
298
Jason Molendad251c9d2012-11-17 01:41:04 +0000299 return(addr >= base && addr < (base + size));
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000300}
301
Jason Molendabecd6392013-04-06 07:16:15 +0000302void
303MachVMMemory::GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, mach_vm_size_t &rprvt, mach_vm_size_t &vprvt)
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000304{
305 // Collecting some other info cheaply but not reporting for now.
306 mach_vm_size_t empty = 0;
Jason Molendad251c9d2012-11-17 01:41:04 +0000307 mach_vm_size_t fw_private = 0;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000308
Jason Molendad251c9d2012-11-17 01:41:04 +0000309 mach_vm_size_t aliased = 0;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000310 bool global_shared_text_data_mapped = false;
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000311 vm_size_t pagesize = PageSize (task);
Han Ming Ong6f7237d2013-03-25 20:44:40 +0000312
Jason Molendad251c9d2012-11-17 01:41:04 +0000313 for (mach_vm_address_t addr=0, size=0; ; addr += size)
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000314 {
Jason Molendad251c9d2012-11-17 01:41:04 +0000315 vm_region_top_info_data_t info;
316 mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT;
317 mach_port_t object_name;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000318
Jason Molendad251c9d2012-11-17 01:41:04 +0000319 kern_return_t kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name);
320 if (kr != KERN_SUCCESS) break;
321
322 if (InSharedRegion(addr, cputype))
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000323 {
Jason Molendad251c9d2012-11-17 01:41:04 +0000324 // Private Shared
325 fw_private += info.private_pages_resident * pagesize;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000326
Jason Molendad251c9d2012-11-17 01:41:04 +0000327 // Check if this process has the globally shared text and data regions mapped in. If so, set global_shared_text_data_mapped to TRUE and avoid checking again.
328 if (global_shared_text_data_mapped == FALSE && info.share_mode == SM_EMPTY) {
Han Ming Ong6f7237d2013-03-25 20:44:40 +0000329 vm_region_basic_info_data_64_t b_info;
Jason Molendad251c9d2012-11-17 01:41:04 +0000330 mach_vm_address_t b_addr = addr;
331 mach_vm_size_t b_size = size;
332 count = VM_REGION_BASIC_INFO_COUNT_64;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000333
Jason Molendad251c9d2012-11-17 01:41:04 +0000334 kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name);
335 if (kr != KERN_SUCCESS) break;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000336
Jason Molendad251c9d2012-11-17 01:41:04 +0000337 if (b_info.reserved) {
338 global_shared_text_data_mapped = TRUE;
339 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000340 }
Jason Molendad251c9d2012-11-17 01:41:04 +0000341
342 // Short circuit the loop if this isn't a shared private region, since that's the only region type we care about within the current address range.
343 if (info.share_mode != SM_PRIVATE)
344 {
345 continue;
346 }
347 }
348
349 // Update counters according to the region type.
350 if (info.share_mode == SM_COW && info.ref_count == 1)
351 {
352 // Treat single reference SM_COW as SM_PRIVATE
353 info.share_mode = SM_PRIVATE;
354 }
355
356 switch (info.share_mode)
357 {
358 case SM_LARGE_PAGE:
359 // Treat SM_LARGE_PAGE the same as SM_PRIVATE
360 // since they are not shareable and are wired.
361 case SM_PRIVATE:
362 rprvt += info.private_pages_resident * pagesize;
363 rprvt += info.shared_pages_resident * pagesize;
364 vprvt += size;
365 break;
366
367 case SM_EMPTY:
368 empty += size;
369 break;
370
371 case SM_COW:
372 case SM_SHARED:
373 {
374 if (pid == 0)
375 {
376 // Treat kernel_task specially
377 if (info.share_mode == SM_COW)
378 {
379 rprvt += info.private_pages_resident * pagesize;
380 vprvt += size;
381 }
382 break;
383 }
384
385 if (info.share_mode == SM_COW)
386 {
387 rprvt += info.private_pages_resident * pagesize;
388 vprvt += info.private_pages_resident * pagesize;
389 }
390 break;
391 }
392 default:
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000393 // log that something is really bad.
Jason Molendad251c9d2012-11-17 01:41:04 +0000394 break;
395 }
396 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000397
Jason Molendad251c9d2012-11-17 01:41:04 +0000398 rprvt += aliased;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000399}
400
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000401static void
402GetPurgeableAndAnonymous(task_t task, uint64_t &purgeable, uint64_t &anonymous)
403{
404#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
405
406 kern_return_t kr;
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000407 mach_msg_type_number_t info_count;
408 task_vm_info_data_t vm_info;
409
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000410 info_count = TASK_VM_INFO_COUNT;
Han Ming Ong6db14a92013-06-26 20:46:27 +0000411 kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count);
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000412 if (kr == KERN_SUCCESS)
413 {
Han Ming Ong6db14a92013-06-26 20:46:27 +0000414 purgeable = vm_info.purgeable_volatile_resident;
Han Ming Ongcd2d7972014-09-03 22:33:37 +0000415 anonymous = vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap;
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000416 }
Han Ming Ong6db14a92013-06-26 20:46:27 +0000417
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000418#endif
419}
420
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000421nub_bool_t
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000422MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous)
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000423{
Han Ming Ong8764fe72013-03-04 21:25:51 +0000424 if (scanType & eProfileHostMemory)
425 physical_memory = GetPhysicalMemory();
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000426
Han Ming Ong8764fe72013-03-04 21:25:51 +0000427 if (scanType & eProfileMemory)
428 {
429 static mach_port_t localHost = mach_host_self();
430 mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
431 host_statistics(localHost, HOST_VM_INFO, (host_info_t)&vm_stats, &count);
Jason Molendabecd6392013-04-06 07:16:15 +0000432 vm_stats.wire_count += GetStolenPages(task);
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000433
Han Ming Ong8764fe72013-03-04 21:25:51 +0000434 GetMemorySizes(task, cputype, pid, rprvt, vprvt);
435
436 rsize = ti.resident_size;
437 vsize = ti.virtual_size;
Han Ming Ong0c27cb72013-03-13 22:51:04 +0000438
439 if (scanType & eProfileMemoryDirtyPage)
440 {
441 // This uses vmmap strategy. We don't use the returned rsize for now. We prefer to match top's version since that's what we do for the rest of the metrics.
442 GetRegionSizes(task, rsize, dirty_size);
443 }
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000444
445 if (scanType & eProfileMemoryAnonymous)
446 {
447 GetPurgeableAndAnonymous(task, purgeable, anonymous);
448 }
Han Ming Ong8764fe72013-03-04 21:25:51 +0000449 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000450
451 return true;
452}
453
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000454nub_size_t
455MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count)
456{
457 if (data == NULL || data_count == 0)
458 return 0;
459
460 nub_size_t total_bytes_read = 0;
461 nub_addr_t curr_addr = address;
462 uint8_t *curr_data = (uint8_t*)data;
463 while (total_bytes_read < data_count)
464 {
Jason Molendabecd6392013-04-06 07:16:15 +0000465 mach_vm_size_t curr_size = MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_read);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000466 mach_msg_type_number_t curr_bytes_read = 0;
467 vm_offset_t vm_memory = NULL;
468 m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read);
Jim Ingham70358852011-12-09 19:48:22 +0000469
Jim Ingham329617a2012-03-09 21:09:42 +0000470 if (DNBLogCheckLogBit(LOG_MEMORY))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000471 m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i )", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read);
472
473 if (m_err.Success())
474 {
475 if (curr_bytes_read != curr_size)
476 {
477 if (DNBLogCheckLogBit(LOG_MEMORY))
478 m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i ) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size);
479 }
480 ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read);
481 ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read);
482 total_bytes_read += curr_bytes_read;
483 curr_addr += curr_bytes_read;
484 curr_data += curr_bytes_read;
485 }
486 else
487 {
488 break;
489 }
490 }
491 return total_bytes_read;
492}
493
494
495nub_size_t
496MachVMMemory::Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count)
497{
498 MachVMRegion vmRegion(task);
499
500 nub_size_t total_bytes_written = 0;
501 nub_addr_t curr_addr = address;
502 const uint8_t *curr_data = (const uint8_t*)data;
503
504
505 while (total_bytes_written < data_count)
506 {
507 if (vmRegion.GetRegionForAddress(curr_addr))
508 {
509 mach_vm_size_t curr_data_count = data_count - total_bytes_written;
510 mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
511 if (region_bytes_left == 0)
512 {
513 break;
514 }
515 if (curr_data_count > region_bytes_left)
516 curr_data_count = region_bytes_left;
517
518 if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE))
519 {
520 nub_size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count);
521 if (bytes_written <= 0)
522 {
523 // Error should have already be posted by WriteRegion...
524 break;
525 }
526 else
527 {
528 total_bytes_written += bytes_written;
529 curr_addr += bytes_written;
530 curr_data += bytes_written;
531 }
532 }
533 else
534 {
535 DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count));
536 break;
537 }
538 }
539 else
540 {
541 DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address);
542 break;
543 }
544 }
545
546 return total_bytes_written;
547}
548
549
550nub_size_t
551MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count)
552{
553 if (data == NULL || data_count == 0)
554 return 0;
555
556 nub_size_t total_bytes_written = 0;
557 nub_addr_t curr_addr = address;
558 const uint8_t *curr_data = (const uint8_t*)data;
559 while (total_bytes_written < data_count)
560 {
Greg Claytonee2ed522015-03-09 19:45:23 +0000561 mach_msg_type_number_t curr_data_count = static_cast<mach_msg_type_number_t>(MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_written));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000562 m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count);
563 if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
564 m_err.LogThreaded("::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count);
565
566#if !defined (__i386__) && !defined (__x86_64__)
567 vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
568
569 m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value);
570 if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
571 m_err.LogThreaded("::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count);
572#endif
573
574 if (m_err.Success())
575 {
576 total_bytes_written += curr_data_count;
577 curr_addr += curr_data_count;
578 curr_data += curr_data_count;
579 }
580 else
581 {
582 break;
583 }
584 }
585 return total_bytes_written;
586}