blob: af82fc9f9c4d23aa4e3a1c6c1fe8261924c56056 [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 }
102 // If we can't get any infor about the size from the next region, just fill
103 // 1 in as the byte size
104 if (region_info->size == 0)
105 region_info->size = 1;
106
107 // Not readable, writeable or executable
108 region_info->permissions = 0;
109 }
110 return true;
Jason Molenda1f3966b2011-11-08 04:28:12 +0000111}
112
Han Ming Ong8594ae82012-11-27 19:21:03 +0000113// For integrated graphics chip, this makes the accounting info for 'wired' memory more like top.
Jason Molendabecd6392013-04-06 07:16:15 +0000114uint64_t
115MachVMMemory::GetStolenPages(task_t task)
Han Ming Ong8594ae82012-11-27 19:21:03 +0000116{
117 static uint64_t stolenPages = 0;
118 static bool calculated = false;
119 if (calculated) return stolenPages;
120
121 static int mib_reserved[CTL_MAXNAME];
122 static int mib_unusable[CTL_MAXNAME];
123 static int mib_other[CTL_MAXNAME];
124 static size_t mib_reserved_len = 0;
125 static size_t mib_unusable_len = 0;
126 static size_t mib_other_len = 0;
127 int r;
128
129 /* This can be used for testing: */
130 //tsamp->pages_stolen = (256 * 1024 * 1024ULL) / tsamp->pagesize;
131
132 if(0 == mib_reserved_len)
133 {
134 mib_reserved_len = CTL_MAXNAME;
135
136 r = sysctlnametomib("machdep.memmap.Reserved", mib_reserved,
137 &mib_reserved_len);
138
139 if(-1 == r)
140 {
141 mib_reserved_len = 0;
142 return 0;
143 }
144
145 mib_unusable_len = CTL_MAXNAME;
146
147 r = sysctlnametomib("machdep.memmap.Unusable", mib_unusable,
148 &mib_unusable_len);
149
150 if(-1 == r)
151 {
152 mib_reserved_len = 0;
153 return 0;
154 }
155
156
157 mib_other_len = CTL_MAXNAME;
158
159 r = sysctlnametomib("machdep.memmap.Other", mib_other,
160 &mib_other_len);
161
162 if(-1 == r)
163 {
164 mib_reserved_len = 0;
165 return 0;
166 }
167 }
168
169 if(mib_reserved_len > 0 && mib_unusable_len > 0 && mib_other_len > 0)
170 {
171 uint64_t reserved = 0, unusable = 0, other = 0;
172 size_t reserved_len;
173 size_t unusable_len;
174 size_t other_len;
175
176 reserved_len = sizeof(reserved);
177 unusable_len = sizeof(unusable);
178 other_len = sizeof(other);
179
180 /* These are all declared as QUAD/uint64_t sysctls in the kernel. */
181
182 if(-1 == sysctl(mib_reserved, mib_reserved_len, &reserved,
183 &reserved_len, NULL, 0))
184 {
185 return 0;
186 }
187
188 if(-1 == sysctl(mib_unusable, mib_unusable_len, &unusable,
189 &unusable_len, NULL, 0))
190 {
191 return 0;
192 }
193
194 if(-1 == sysctl(mib_other, mib_other_len, &other,
195 &other_len, NULL, 0))
196 {
197 return 0;
198 }
199
200 if(reserved_len == sizeof(reserved)
201 && unusable_len == sizeof(unusable)
202 && other_len == sizeof(other))
203 {
204 uint64_t stolen = reserved + unusable + other;
205 uint64_t mb128 = 128 * 1024 * 1024ULL;
206
207 if(stolen >= mb128)
208 {
209 stolen = (stolen & ~((128 * 1024 * 1024ULL) - 1)); // rounding down
Jason Molendafe806902013-05-04 00:39:52 +0000210 stolenPages = stolen / PageSize (task);
Han Ming Ong8594ae82012-11-27 19:21:03 +0000211 }
212 }
213 }
214
215 calculated = true;
216 return stolenPages;
217}
218
219static uint64_t GetPhysicalMemory()
220{
221 // This doesn't change often at all. No need to poll each time.
222 static uint64_t physical_memory = 0;
223 static bool calculated = false;
224 if (calculated) return physical_memory;
225
226 int mib[2];
227 mib[0] = CTL_HW;
228 mib[1] = HW_MEMSIZE;
229 size_t len = sizeof(physical_memory);
230 sysctl(mib, 2, &physical_memory, &len, NULL, 0);
231 return physical_memory;
232}
233
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000234// 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 +0000235void
236MachVMMemory::GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t &dirty_size)
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000237{
238 mach_vm_address_t address = 0;
239 mach_vm_size_t size;
240 kern_return_t err = 0;
241 unsigned nestingDepth = 0;
242 mach_vm_size_t pages_resident = 0;
243 mach_vm_size_t pages_dirtied = 0;
244
245 while (1)
246 {
Han Ming Ong6f7237d2013-03-25 20:44:40 +0000247 mach_msg_type_number_t count;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000248 struct vm_region_submap_info_64 info;
249
250 count = VM_REGION_SUBMAP_INFO_COUNT_64;
251 err = mach_vm_region_recurse(task, &address, &size, &nestingDepth, (vm_region_info_t)&info, &count);
252 if (err == KERN_INVALID_ADDRESS)
253 {
254 // It seems like this is a good break too.
255 break;
256 }
257 else if (err)
258 {
259 mach_error("vm_region",err);
260 break; // reached last region
261 }
262
263 bool should_count = true;
264 if (info.is_submap)
265 { // is it a submap?
266 nestingDepth++;
267 should_count = false;
268 }
269 else
270 {
271 // Don't count malloc stack logging data in the TOTAL VM usage lines.
272 if (info.user_tag == VM_MEMORY_ANALYSIS_TOOL)
273 should_count = false;
Han Ming Ong7b641e92013-03-15 23:19:44 +0000274
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000275 address = address+size;
276 }
277
278 if (should_count)
279 {
280 pages_resident += info.pages_resident;
281 pages_dirtied += info.pages_dirtied;
282 }
283 }
284
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000285 vm_size_t pagesize = PageSize (task);
Han Ming Ong6f7237d2013-03-25 20:44:40 +0000286 rsize = pages_resident * pagesize;
287 dirty_size = pages_dirtied * pagesize;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000288}
289
290// Test whether the virtual address is within the architecture's shared region.
291static bool InSharedRegion(mach_vm_address_t addr, cpu_type_t type)
292{
Jason Molendad251c9d2012-11-17 01:41:04 +0000293 mach_vm_address_t base = 0, size = 0;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000294
Jason Molendad251c9d2012-11-17 01:41:04 +0000295 switch(type) {
296 case CPU_TYPE_ARM:
297 base = SHARED_REGION_BASE_ARM;
298 size = SHARED_REGION_SIZE_ARM;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000299 break;
300
Jason Molendad251c9d2012-11-17 01:41:04 +0000301 case CPU_TYPE_X86_64:
302 base = SHARED_REGION_BASE_X86_64;
303 size = SHARED_REGION_SIZE_X86_64;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000304 break;
305
Jason Molendad251c9d2012-11-17 01:41:04 +0000306 case CPU_TYPE_I386:
307 base = SHARED_REGION_BASE_I386;
308 size = SHARED_REGION_SIZE_I386;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000309 break;
310
Jason Molendad251c9d2012-11-17 01:41:04 +0000311 default: {
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000312 // Log error abut unknown CPU type
313 break;
Jason Molendad251c9d2012-11-17 01:41:04 +0000314 }
315 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000316
317
Jason Molendad251c9d2012-11-17 01:41:04 +0000318 return(addr >= base && addr < (base + size));
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000319}
320
Jason Molendabecd6392013-04-06 07:16:15 +0000321void
322MachVMMemory::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 +0000323{
324 // Collecting some other info cheaply but not reporting for now.
325 mach_vm_size_t empty = 0;
Jason Molendad251c9d2012-11-17 01:41:04 +0000326 mach_vm_size_t fw_private = 0;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000327
Jason Molendad251c9d2012-11-17 01:41:04 +0000328 mach_vm_size_t aliased = 0;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000329 bool global_shared_text_data_mapped = false;
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000330 vm_size_t pagesize = PageSize (task);
Han Ming Ong6f7237d2013-03-25 20:44:40 +0000331
Jason Molendad251c9d2012-11-17 01:41:04 +0000332 for (mach_vm_address_t addr=0, size=0; ; addr += size)
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000333 {
Jason Molendad251c9d2012-11-17 01:41:04 +0000334 vm_region_top_info_data_t info;
335 mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT;
336 mach_port_t object_name;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000337
Jason Molendad251c9d2012-11-17 01:41:04 +0000338 kern_return_t kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name);
339 if (kr != KERN_SUCCESS) break;
340
341 if (InSharedRegion(addr, cputype))
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000342 {
Jason Molendad251c9d2012-11-17 01:41:04 +0000343 // Private Shared
344 fw_private += info.private_pages_resident * pagesize;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000345
Jason Molendad251c9d2012-11-17 01:41:04 +0000346 // 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.
347 if (global_shared_text_data_mapped == FALSE && info.share_mode == SM_EMPTY) {
Han Ming Ong6f7237d2013-03-25 20:44:40 +0000348 vm_region_basic_info_data_64_t b_info;
Jason Molendad251c9d2012-11-17 01:41:04 +0000349 mach_vm_address_t b_addr = addr;
350 mach_vm_size_t b_size = size;
351 count = VM_REGION_BASIC_INFO_COUNT_64;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000352
Jason Molendad251c9d2012-11-17 01:41:04 +0000353 kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name);
354 if (kr != KERN_SUCCESS) break;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000355
Jason Molendad251c9d2012-11-17 01:41:04 +0000356 if (b_info.reserved) {
357 global_shared_text_data_mapped = TRUE;
358 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000359 }
Jason Molendad251c9d2012-11-17 01:41:04 +0000360
361 // 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.
362 if (info.share_mode != SM_PRIVATE)
363 {
364 continue;
365 }
366 }
367
368 // Update counters according to the region type.
369 if (info.share_mode == SM_COW && info.ref_count == 1)
370 {
371 // Treat single reference SM_COW as SM_PRIVATE
372 info.share_mode = SM_PRIVATE;
373 }
374
375 switch (info.share_mode)
376 {
377 case SM_LARGE_PAGE:
378 // Treat SM_LARGE_PAGE the same as SM_PRIVATE
379 // since they are not shareable and are wired.
380 case SM_PRIVATE:
381 rprvt += info.private_pages_resident * pagesize;
382 rprvt += info.shared_pages_resident * pagesize;
383 vprvt += size;
384 break;
385
386 case SM_EMPTY:
387 empty += size;
388 break;
389
390 case SM_COW:
391 case SM_SHARED:
392 {
393 if (pid == 0)
394 {
395 // Treat kernel_task specially
396 if (info.share_mode == SM_COW)
397 {
398 rprvt += info.private_pages_resident * pagesize;
399 vprvt += size;
400 }
401 break;
402 }
403
404 if (info.share_mode == SM_COW)
405 {
406 rprvt += info.private_pages_resident * pagesize;
407 vprvt += info.private_pages_resident * pagesize;
408 }
409 break;
410 }
411 default:
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000412 // log that something is really bad.
Jason Molendad251c9d2012-11-17 01:41:04 +0000413 break;
414 }
415 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000416
Jason Molendad251c9d2012-11-17 01:41:04 +0000417 rprvt += aliased;
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000418}
419
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000420#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
421
422// cribbed from sysmond
423static uint64_t
424SumVMPurgeableInfo(const vm_purgeable_info_t info)
425{
426 uint64_t sum = 0;
427 int i;
428
429 for (i = 0; i < 8; i++)
430 {
431 sum += info->fifo_data[i].size;
432 }
433 sum += info->obsolete_data.size;
434 for (i = 0; i < 8; i++)
435 {
436 sum += info->lifo_data[i].size;
437 }
438
439 return sum;
440}
441
442#endif
443
444static void
445GetPurgeableAndAnonymous(task_t task, uint64_t &purgeable, uint64_t &anonymous)
446{
447#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
448
449 kern_return_t kr;
450 task_purgable_info_t purgeable_info;
451 uint64_t purgeable_sum = 0;
452 mach_msg_type_number_t info_count;
453 task_vm_info_data_t vm_info;
454
Han Ming Onge8632182013-05-22 23:00:47 +0000455 typedef kern_return_t (*task_purgable_info_type) (task_t, task_purgable_info_t *);
456 task_purgable_info_type task_purgable_info_ptr = NULL;
457 task_purgable_info_ptr = (task_purgable_info_type)dlsym(RTLD_NEXT, "task_purgable_info");
458 if (task_purgable_info_ptr != NULL)
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000459 {
Han Ming Onge8632182013-05-22 23:00:47 +0000460 kr = (*task_purgable_info_ptr)(task, &purgeable_info);
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000461 if (kr == KERN_SUCCESS) {
462 purgeable_sum = SumVMPurgeableInfo(&purgeable_info);
463 purgeable = purgeable_sum;
464 }
465 }
466
467 info_count = TASK_VM_INFO_COUNT;
468 kr = task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &info_count);
469 if (kr == KERN_SUCCESS)
470 {
471 if (purgeable_sum < vm_info.internal)
472 {
473 anonymous = vm_info.internal - purgeable_sum;
474 }
475 else
476 {
477 anonymous = 0;
478 }
479 }
Han Ming Onge8632182013-05-22 23:00:47 +0000480
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000481#endif
482}
483
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000484nub_bool_t
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000485MachVMMemory::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 +0000486{
Han Ming Ong8764fe72013-03-04 21:25:51 +0000487 if (scanType & eProfileHostMemory)
488 physical_memory = GetPhysicalMemory();
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000489
Han Ming Ong8764fe72013-03-04 21:25:51 +0000490 if (scanType & eProfileMemory)
491 {
492 static mach_port_t localHost = mach_host_self();
493 mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
494 host_statistics(localHost, HOST_VM_INFO, (host_info_t)&vm_stats, &count);
Jason Molendabecd6392013-04-06 07:16:15 +0000495 vm_stats.wire_count += GetStolenPages(task);
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000496
Han Ming Ong8764fe72013-03-04 21:25:51 +0000497 GetMemorySizes(task, cputype, pid, rprvt, vprvt);
498
499 rsize = ti.resident_size;
500 vsize = ti.virtual_size;
Han Ming Ong0c27cb72013-03-13 22:51:04 +0000501
502 if (scanType & eProfileMemoryDirtyPage)
503 {
504 // 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.
505 GetRegionSizes(task, rsize, dirty_size);
506 }
Han Ming Ongb153c2f2013-05-22 21:32:42 +0000507
508 if (scanType & eProfileMemoryAnonymous)
509 {
510 GetPurgeableAndAnonymous(task, purgeable, anonymous);
511 }
Han Ming Ong8764fe72013-03-04 21:25:51 +0000512 }
Han Ming Ongab3b8b22012-11-17 00:21:04 +0000513
514 return true;
515}
516
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000517nub_size_t
518MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count)
519{
520 if (data == NULL || data_count == 0)
521 return 0;
522
523 nub_size_t total_bytes_read = 0;
524 nub_addr_t curr_addr = address;
525 uint8_t *curr_data = (uint8_t*)data;
526 while (total_bytes_read < data_count)
527 {
Jason Molendabecd6392013-04-06 07:16:15 +0000528 mach_vm_size_t curr_size = MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_read);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000529 mach_msg_type_number_t curr_bytes_read = 0;
530 vm_offset_t vm_memory = NULL;
531 m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read);
Jim Ingham70358852011-12-09 19:48:22 +0000532
Jim Ingham329617a2012-03-09 21:09:42 +0000533 if (DNBLogCheckLogBit(LOG_MEMORY))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000534 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);
535
536 if (m_err.Success())
537 {
538 if (curr_bytes_read != curr_size)
539 {
540 if (DNBLogCheckLogBit(LOG_MEMORY))
541 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);
542 }
543 ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read);
544 ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read);
545 total_bytes_read += curr_bytes_read;
546 curr_addr += curr_bytes_read;
547 curr_data += curr_bytes_read;
548 }
549 else
550 {
551 break;
552 }
553 }
554 return total_bytes_read;
555}
556
557
558nub_size_t
559MachVMMemory::Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count)
560{
561 MachVMRegion vmRegion(task);
562
563 nub_size_t total_bytes_written = 0;
564 nub_addr_t curr_addr = address;
565 const uint8_t *curr_data = (const uint8_t*)data;
566
567
568 while (total_bytes_written < data_count)
569 {
570 if (vmRegion.GetRegionForAddress(curr_addr))
571 {
572 mach_vm_size_t curr_data_count = data_count - total_bytes_written;
573 mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
574 if (region_bytes_left == 0)
575 {
576 break;
577 }
578 if (curr_data_count > region_bytes_left)
579 curr_data_count = region_bytes_left;
580
581 if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE))
582 {
583 nub_size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count);
584 if (bytes_written <= 0)
585 {
586 // Error should have already be posted by WriteRegion...
587 break;
588 }
589 else
590 {
591 total_bytes_written += bytes_written;
592 curr_addr += bytes_written;
593 curr_data += bytes_written;
594 }
595 }
596 else
597 {
598 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));
599 break;
600 }
601 }
602 else
603 {
604 DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address);
605 break;
606 }
607 }
608
609 return total_bytes_written;
610}
611
612
613nub_size_t
614MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count)
615{
616 if (data == NULL || data_count == 0)
617 return 0;
618
619 nub_size_t total_bytes_written = 0;
620 nub_addr_t curr_addr = address;
621 const uint8_t *curr_data = (const uint8_t*)data;
622 while (total_bytes_written < data_count)
623 {
Jason Molendabecd6392013-04-06 07:16:15 +0000624 mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_written);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000625 m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count);
626 if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
627 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);
628
629#if !defined (__i386__) && !defined (__x86_64__)
630 vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
631
632 m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value);
633 if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
634 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);
635#endif
636
637 if (m_err.Success())
638 {
639 total_bytes_written += curr_data_count;
640 curr_addr += curr_data_count;
641 curr_data += curr_data_count;
642 }
643 else
644 {
645 break;
646 }
647 }
648 return total_bytes_written;
649}