1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package pprof
6
7import (
8	"os"
9	"unsafe"
10)
11
12func isExecutable(protection int32) bool {
13	return (protection&_VM_PROT_EXECUTE) != 0 && (protection&_VM_PROT_READ) != 0
14}
15
16// machVMInfo uses the mach_vm_region region system call to add mapping entries
17// for the text region of the running process.
18func machVMInfo(addMapping func(lo, hi, offset uint64, file, buildID string)) bool {
19	added := false
20	var addr uint64 = 0x1
21	for {
22		var memRegionSize uint64
23		var info machVMRegionBasicInfoData
24		// Get the first address and page size.
25		kr := mach_vm_region(
26			&addr,
27			&memRegionSize,
28			unsafe.Pointer(&info))
29		if kr != 0 {
30			if kr == _MACH_SEND_INVALID_DEST {
31				// No more memory regions.
32				return true
33			}
34			return added // return true if at least one mapping was added
35		}
36		if isExecutable(info.Protection) {
37			// NOTE: the meaning/value of Offset is unclear. However,
38			// this likely doesn't matter as the text segment's file
39			// offset is usually 0.
40			addMapping(addr,
41				addr+memRegionSize,
42				read64(&info.Offset),
43				regionFilename(addr),
44				"")
45			added = true
46		}
47		addr += memRegionSize
48	}
49}
50
51func read64(p *[8]byte) uint64 {
52	// all supported darwin platforms are little endian
53	return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56
54}
55
56func regionFilename(address uint64) string {
57	buf := make([]byte, _MAXPATHLEN)
58	r := proc_regionfilename(
59		os.Getpid(),
60		address,
61		unsafe.SliceData(buf),
62		int64(cap(buf)))
63	if r == 0 {
64		return ""
65	}
66	return string(buf[:r])
67}
68
69// mach_vm_region and proc_regionfilename are implemented by
70// the runtime package (runtime/sys_darwin.go).
71//
72//go:noescape
73func mach_vm_region(address, region_size *uint64, info unsafe.Pointer) int32
74
75//go:noescape
76func proc_regionfilename(pid int, address uint64, buf *byte, buflen int64) int32
77