├── .gitignore
├── README.md
├── adm
├── createuser
├── mksmartvm
└── rc.server
├── ios
├── simapp
├── simulator.gdb
└── splashsnoop.d
├── launchd
└── insecure.shell.plist
├── lib
├── binscale.py
└── dcov.pl
├── lldb-run.py
├── objc
├── dtrace_objc.h
├── notificationhook
│ ├── Makefile
│ ├── hook.lldb
│ ├── notification.provider.d
│ └── notificationhook.m
├── nsnotifications.d
└── test
│ └── ui.clj
├── sys
├── acceptsnoop.d
├── connectsnoop.d
├── dispatch_trace.d
├── execsnoop.real.d
├── exectime.d
├── machportdump.c
├── machtrace.d
├── portsnoop.d
├── sockets.d
└── waittrace.d
└── xnu
└── kdebug.gdb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.provider.h
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | darwinkit
2 | =========
3 |
4 | Darwin scripts toolbox: DTrace and GDB scripts, housekeeping.
5 | See [lxperf](https://github.com/proger/lxperf) for Linux.
6 |
7 | ## OSX DTrace gotchas
8 |
9 | * there is no `progenyof` -- `mach_trace.d` has to track its children manually
10 | * unordered events due to DTrace having per-cpu buffers and threads migrating between cores;
11 | * OSX has no core affinity API
12 | * no `-x temporal` too ([what is temporal](https://github.com/illumos/illumos-gate/commit/e5803b76927480e8f9b67b22201c484ccf4c2bcf))
13 | * workaround for `machtrace.d`:
14 |
15 | # column 2 should print the elapsed execution time
16 | dtrace -x evaltime=exec -Cs sys/machtrace.d -c ./a.out | sort -n -k2
17 |
18 | ## launchd stuff
19 |
20 | * [insecure.shell](launchd/insecure.shell.plist) -- helps you learn how screwed your launchd daemon/agent environment is
21 | * run client: `socat tcp-connect:localhost:12345 readline`
22 | * make sure you have `socat` installed at `/usr/local/bin`! (use homebrew)
23 |
--------------------------------------------------------------------------------
/adm/createuser:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | if [[ -z "$1" || -z "$2" ]]; then
3 | echo "Usage: $0 username homedir_path"
4 | exit 1
5 | fi
6 |
7 | set -x
8 |
9 | NAME=$1
10 | HOME=$2
11 | next_uid=$((`dscl . ls /Users uid | awk '{print $2}' | sort -nr | head -n1` + 1))
12 | next_gid=$((`dscl . ls /Groups gid | awk '{print $2}' | sort -nr | head -n1` + 1))
13 | echo "create user group"
14 | dscl . -create /Groups/${NAME}
15 | dscl . -create /Groups/${NAME} PrimaryGroupID $next_gid
16 | echo "create user"
17 | dscl . -create /Users/${NAME}
18 | dscl . -create /Users/${NAME} UserShell /bin/bash
19 | dscl . -create /Users/${NAME} RealName "${NAME}"
20 | dscl . -create /Users/${NAME} UniqueID $next_uid
21 | dscl . -create /Users/${NAME} PrimaryGroupID $next_gid
22 | dscl . -create /Users/${NAME} NFSHomeDirectory "${HOME}"
23 | echo "assign user to his group"
24 | dscl . -append /Groups/${NAME} GroupMembership ${NAME}
25 | dscl . -append /Groups/com.apple.access_ssh GroupMembership ${NAME}
26 | dscl . -append /Groups/com.apple.access_screensharing GroupMembership ${NAME}
27 | dscl . -append /Groups/staff GroupMembership ${NAME}
28 | dscl . -append /Groups/wheel GroupMembership ${NAME}
29 | echo "create user home directory"
30 | mkdir -p ${HOME}
31 | chown ${NAME}:${NAME} ${HOME}
32 |
33 | # vim: set sw=4 sts=4 et tw=80 :
34 |
--------------------------------------------------------------------------------
/adm/mksmartvm:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # origin: https://github.com/jperkin/scripts
4 | #
5 | # Configurables:
6 | #
7 | # - Disk size is in GB
8 | # - Memory size is in MB
9 | # - SSH port is the local forwarded port to the VM:22
10 | #
11 | disksize="32"
12 | memsize="1024"
13 | sshport="8322"
14 | vmname="SmartOS"
15 | dlsite="https://download.joyent.com/pub/iso"
16 |
17 | vboxdir=$(VBoxManage list systemproperties \
18 | | awk '/^Default.machine.folder/ { print $4 }')
19 |
20 | #
21 | # Download MD5 file and parse it for the latest ISO image and checksum
22 | #
23 | curl -o smartos-sums.txt ${dlsite}/md5sums.txt 2>/dev/null
24 | latest_md5=$(awk '/latest.iso/ { print $1 }' smartos-sums.txt)
25 | smartos_version=$(sed -ne "/^${latest_md5}/s/.*-\(.*\).iso/\1/p" \
26 | smartos-sums.txt)
27 | if [ -z "${smartos_version}" ]; then
28 | echo "ERROR: Couldn't determine latest version"
29 | exit 1
30 | fi
31 |
32 | #
33 | # Download the latest ISO image and verify
34 | #
35 | mkdir -p "${vboxdir}/${vmname}"
36 | if [ ! -f "${vboxdir}/${vmname}/smartos-${smartos_version}.iso" ]; then
37 | echo "Downloading ${dlsite}/smartos-${smartos_version}.iso"
38 | curl -o "${vboxdir}/${vmname}/smartos-${smartos_version}.iso" \
39 | ${dlsite}/smartos-${smartos_version}.iso
40 | #dl_md5=$(md5sum "${vboxdir}/${vmname}/smartos-${smartos_version}.iso" \
41 | # | awk '{ print $1 }')
42 | dl_md5=$(md5 -q "${vboxdir}/${vmname}/smartos-${smartos_version}.iso")
43 | if [ -z "${dl_md5}" ]; then
44 | echo "ERROR: Couldn't fetch ISO image"
45 | exit 1
46 | fi
47 | if [ "${latest_md5}" != "${dl_md5}" ]; then
48 | echo "ERROR: md5 checksums do not match"
49 | exit 1
50 | fi
51 | fi
52 |
53 | #
54 | # Create VirtualBox VM
55 | #
56 | echo "Creating/Updating Virtual Machine"
57 | VBoxManage showvminfo "${vmname}" >/dev/null 2>&1
58 | if [ $? -eq 0 ]; then
59 | # VM already exists, just update the ISO image
60 | VBoxManage storageattach "${vmname}" --storagectl "IDE Controller" \
61 | --port 1 --device 0 --type dvddrive \
62 | --medium "${vboxdir}/${vmname}/smartos-${smartos_version}.iso"
63 | else
64 | # Create the VM
65 | VBoxManage createvm --name "${vmname}" --ostype OpenSolaris_64 --register
66 | VBoxManage storagectl "${vmname}" --name "IDE Controller" --add ide
67 |
68 | # Attach the ISO image
69 | VBoxManage storageattach "${vmname}" --storagectl "IDE Controller" \
70 | --port 1 --device 0 --type dvddrive \
71 | --medium "${vboxdir}/${vmname}/smartos-${smartos_version}.iso"
72 |
73 | # Create and attach the zone disk
74 | VBoxManage createhd --filename "${vboxdir}/${vmname}/smartos-zones.vdi" \
75 | --size $(echo "${disksize}*1024" | bc)
76 | VBoxManage storageattach "${vmname}" --storagectl "IDE Controller" \
77 | --port 0 --device 0 --type hdd \
78 | --medium "${vboxdir}/${vmname}/smartos-zones.vdi"
79 |
80 | # Set misc settings
81 | VBoxManage modifyvm "${vmname}" --boot1 dvd --boot2 disk --boot3 none
82 | VBoxManage modifyvm "${vmname}" --memory ${memsize}
83 | VBoxManage modifyvm "${vmname}" --natpf1 "SSH,tcp,,${sshport},,22"
84 | fi
85 |
86 | #
87 | # Start it up
88 | #
89 | echo "Starting Virtual Machine"
90 | VirtualBox --startvm "${vmname}" &
91 |
--------------------------------------------------------------------------------
/adm/rc.server:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # rc.server: server-specific boot initializations
4 | #
5 | # Copyright 2006-2009 Apple Inc and Darkproger. All rights reserved.
6 | #
7 |
8 | set -x
9 | echo HOLY FUCKING SHIT THIS IS /etc/rc.server !
10 | /bin/ps axuww
11 | echo will start syslog early
12 | /usr/sbin/syslogd -D
13 | /bin/launchctl log level info
14 | echo will badaboom
15 | (cd /System/Library/LaunchDaemons; /bin/launchctl load *)
16 |
17 | #exec /bin/bash
18 |
19 | #
20 | # If you wish to override these or any other sysctl settings, copy
21 | # /etc/sysctl.conf.default to /etc/sysctl.conf and put your settings there;
22 | # it will be processed after this script.
23 | #
24 | # Refer to the man pages for launchctl(8) and sysctl(8) as well as
25 | # /etc/sysctl.conf.default for additional information.
26 | #
27 |
28 | PATH=/bin:/sbin:/usr/sbin:/usr/bin
29 | #
30 | # Set ipfw to configured state
31 | #
32 | if [ -f /etc/ipfilter/ipfw.conf.apple ]; then
33 | ipfw /etc/ipfilter/ipfw.conf.apple
34 | fi
35 | if [ -f /etc/ipfilter/ipfwstate-on ]; then
36 | sysctl -w net.inet.ip.fw.enable=1
37 | else
38 | sysctl -w net.inet.ip.fw.enable=0
39 | fi
40 |
41 | #
42 | # Set ip6fw to configured state
43 | #
44 | if [ -f /etc/ipfilter/ip6fw.conf.apple ]; then
45 | ip6fw /etc/ipfilter/ip6fw.conf.apple
46 | fi
47 | if [ -f /etc/ipfilter/ip6fwstate-on ]; then
48 | sysctl -w net.inet6.ip6.fw.enable=1
49 | else
50 | sysctl -w net.inet6.ip6.fw.enable=0
51 | fi
52 |
53 | # If we are not booted to tune our system as a server then just exit, perform no Tuning
54 | SERVER_INFO="/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/serverinfo";
55 | if [ -e $SERVER_INFO ]; then
56 | if ! $SERVER_INFO -q --perfmode; then
57 | exit
58 | fi
59 | fi
60 |
61 | #
62 | # Set TCP to ack every other packet. (RFC-compliant "compatibility" mode.)
63 | # This should increase server performance, especially when connected
64 | # to Windows clients.
65 | #
66 | sysctl -w net.inet.tcp.delayed_ack=2
67 |
68 | #
69 | # Scale kernel parameters based on memory configuration.
70 | #
71 | (( memsize=$(sysctl -n hw.memsize) ))
72 | (( memsize/=1073741824 ))
73 |
74 | /bin/echo "rc.server[" $$ "]: Tuning server for ${memsize} GB (rounded down)." >> "/private/var/log/system.log"
75 |
76 | # Almost all tunings that had been done here are now done in the kernel, which should now use the ServerInformation framework rather than srv=1
77 | #
78 | # If you have a bunch of HFS+ filesystems, as we expect today on servers, then maxnbuf
79 | # still needs to grow slightly. This is especially true for file servers.
80 | # set -x
81 | if (( ${memsize} >= 2 )); then
82 | # Increase the system-wide limit on the number of processes.
83 | # Server requires a fair sized listen queue default
84 | sysctl -w kern.ipc.somaxconn=2500
85 | if [ `uname -m` == i386 ]; then
86 | # Always tune maxproc on K32 kernel
87 | sysctl -w kern.maxproc=2500
88 | # Increase the maximum number of open network sockets.
89 | # The kernel automatically scales these by available RAM to increase disk I/O
90 | # performance, but server generally wants more than the defaults.
91 | if (( ${memsize} >= 16 )); then
92 | sysctl -w kern.maxnbuf=60000
93 | sysctl -w kern.maxvnodes=280000
94 | sysctl -w kern.maxfiles=280000
95 | elif (( ${memsize} >= 8 )); then
96 | sysctl -w kern.maxnbuf=42000
97 | sysctl -w kern.maxvnodes=150000
98 | sysctl -w kern.maxfiles=150000
99 | elif (( ${memsize} >= 4 )); then
100 | sysctl -w kern.maxnbuf=21000
101 | sysctl -w kern.maxvnodes=120000
102 | sysctl -w kern.maxfiles=120000
103 | else
104 | sysctl -w kern.maxvnodes=90000
105 | sysctl -w kern.maxfiles=90000
106 | fi
107 | fi
108 | if [ `uname -m` == x86_64 ]; then
109 | # Even on K64, we do want a higher maxnbuf
110 | if (( ${memsize} >= 16 )); then
111 | sysctl -w kern.maxnbuf=160000
112 | # For sizes 16GB or greater, the kernel automatically scales
113 | # tuning paramaters to appropriate values for a server based
114 | # on installed memory size. The following commands can be used
115 | # to read the values on the running system:
116 | # sysctl kern.maxproc
117 | # sysctl kern.maxvnodes
118 | # sysctl kern.maxfiles
119 | elif ((${memsize} >= 8)); then
120 | # For sizes less than 16GB the kernel maxproc default isn't high enough
121 | sysctl -w kern.maxproc=2500
122 | sysctl -w kern.maxnbuf=60000
123 | sysctl -w kern.maxvnodes=150000
124 | sysctl -w kern.maxfiles=150000
125 | elif (( ${memsize} >= 4 )); then
126 | sysctl -w kern.maxproc=2500
127 | sysctl -w kern.maxnbuf=21000
128 | sysctl -w kern.maxvnodes=120000
129 | sysctl -w kern.maxfiles=120000
130 | else
131 | sysctl -w kern.maxproc=1500
132 | sysctl -w kern.maxvnodes=90000
133 | sysctl -w kern.maxfiles=90000
134 | fi
135 | fi
136 | fi
137 |
138 | #
139 | # Increase the number of processes each user may launch.
140 | # This must be done AFTER setting maxproc, because sysctl fails if a new
141 | # value exceeds its limit (instead of pegging to max).
142 | # The launchctl(8) command is necessary to raise the resource limit for
143 | # launchd(8) and its children. See getrlimit(2) for information.
144 | #
145 | (( maxproc=$(sysctl -n kern.maxproc) ))
146 | (( bigbunch = ${maxproc}/4 ))
147 | (( maxprocperuid=(${maxproc}>2500)?${bigbunch}:(${maxproc}-100) ))
148 | sysctl -w kern.maxprocperuid=${maxprocperuid}
149 | #
150 | # XXX This requires the filesystem to be mounted read-write to communicate
151 | # with launchd correctly.
152 | launchctl limit maxproc ${maxprocperuid} ${maxproc}
153 |
154 | # re-read the sysctl.conf file, so any overrides get re-applied
155 | if [ -f /etc/sysctl.conf ]; then
156 | while read cmd ignore; do
157 | case ${cmd} in
158 | \#*|'') ;;
159 | *) sysctl -w "${cmd}" ;;
160 | esac
161 | done < /etc/sysctl.conf
162 | fi
163 | # Fix for issue 7022012
164 |
--------------------------------------------------------------------------------
/ios/simapp:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | app=$1
4 | binary=$2
5 |
6 | SDKVER=5.0
7 | REALHOME=$HOME
8 |
9 | exec env "TMPDIR=$REALHOME/Library/Application Support/iPhone Simulator/$SDKVER/Applications/$app/tmp" \
10 | "HOME=$REALHOME/Library/Application Support/iPhone Simulator/$SDKVER/Applications/$app" \
11 | "DYLD_FALLBACK_FRAMEWORK_PATH=/System/Library/Frameworks" \
12 | "DYLD_ROOT_PATH=/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator$SDKVER.sdk" \
13 | "IPHONE_SIMULATOR_ROOT=/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator$SDKVER.sdk" \
14 | "IPHONE_SIMULATOR_CLASS=N90" \
15 | "IPHONE_SIMULATOR_DEVICE=iPhone" \
16 | "MallocCorruptionAbort=0" \
17 | "IPHONE_SIMULATOR_MEMORY_WARNINGS=/tmp/iPhoneSimulatorMemoryWarning" \
18 | "COMMAND_MODE=unix2003" \
19 | "IPHONE_SHARED_RESOURCES_DIRECTORY=$REALHOME/Library/Application Support/iPhone Simulator/$SDKVER" \
20 | "IPHONE_SIMULATOR_VERSIONS=iPhone Simulator 272, iPhone OS $SDKVER (iPhone (Retina)/9A334)" \
21 | "CFFIXED_USER_HOME=$REALHOME/Library/Application Support/iPhone Simulator/$SDKVER/Applications/$app" \
22 | "DYLD_FALLBACK_LIBRARY_PATH=/usr/lib" \
23 | "__CF_USER_TEXT_ENCODING=0x1F5:0:0" \
24 | \
25 | "$REALHOME/Library/Application Support/iPhone Simulator/$SDKVER/Applications/$app/$binary.app/$binary"
26 |
--------------------------------------------------------------------------------
/ios/simulator.gdb:
--------------------------------------------------------------------------------
1 | set shlib-path-substitutions / /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/
2 | define penv
3 | set $env = (char **)environ
4 | set $i = 0
5 | while $env[$i] != 0
6 | p $env[$i]
7 | set $i = $i+1
8 | end
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/ios/splashsnoop.d:
--------------------------------------------------------------------------------
1 | #!/usr/sbin/dtrace -s
2 |
3 | syscall::open:entry
4 | /execname == "SpringBoard" && strstr(copyinstr(arg0), "Default@2x.png") != 0/
5 | {
6 | trace(copyinstr(arg0));
7 | ustack();
8 | stopped = pid;
9 | stop();
10 | }
11 |
12 | END /stopped != 0/ {
13 | pidresume(stopped);
14 | }
15 |
--------------------------------------------------------------------------------
/launchd/insecure.shell.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WorkingDirectory
6 | /
7 | Debug
8 |
9 | Label
10 | insecure.shell
11 | OnDemand
12 |
13 | KeepAlive
14 |
15 | ProgramArguments
16 |
17 | /usr/local/bin/socat
18 | tcp-listen:12345,reuseaddr,bind=127.0.0.1
19 | exec:/bin/zsh,stderr
20 |
21 | RunAtLoad
22 |
23 | UserName
24 | proger
25 |
26 | EnvironmentVariables
27 |
28 | JAVA_HOME
29 | /Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home
30 | PATH
31 | /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/Server.app/Contents/ServerRoot/usr/bin:/Applications/Server.app/Contents/ServerRoot/usr/sbin:/tank/proger/local/bin:/tank/proger/local/ruby/bin
32 | GEM_HOME
33 | /tank/proger/local/ruby
34 | LC_CTYPE
35 | en_US.UTF-8
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/lib/binscale.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from random import choice
3 |
4 | COL_NORMAL = '\033[0m'
5 | COL_RED = '\033[1;31m'
6 | COL_GREEN = '\033[1;32m'
7 | COL_YELLOW = '\033[1;33m'
8 | COL_BLUE = '\033[1;34m'
9 | COL_WHITE = '\033[1;37m'
10 | COL_CYAN = '\033[1;36m'
11 | COL_GREY = '\033[1;30m'
12 | cols = [COL_RED, COL_GREEN, COL_YELLOW, COL_BLUE, COL_WHITE, COL_CYAN]
13 |
14 | def color():
15 | c = cols.pop()
16 | cols.insert(0, c)
17 | return c
18 |
19 | def isbit(ch):
20 | return (ch in ['1', '0'])
21 |
22 | BIT_SEPARATOR = ' '
23 |
24 | class bits():
25 | '''
26 | Class, which represents an int value in a bit string.
27 | '''
28 | def __init__(self, n, nbits=8, descr=''):
29 | self.bit = (n, nbits)
30 | self.descr = descr
31 |
32 | def nbits(self):
33 | return self.bit[1]
34 |
35 | def val(self):
36 | return self.bit[0]
37 |
38 | def sign(self):
39 | '''
40 | returns value description
41 | '''
42 | return self.sign
43 |
44 | def ruler(self):
45 | '''
46 | returns a ruler for bit string representation
47 | '''
48 | return '|' + ''.join(['-' for i in range((self.nbits() - 1) * len(BIT_SEPARATOR) + self.nbits())]) + '|'
49 |
50 | def __str__(self):
51 | '''
52 | returns bit string representation, each bit is separated by BIT_SEPARATOR,
53 | string is prefixed and suffixed by one space.
54 | '''
55 | return ' ' + BIT_SEPARATOR.join([str((self.val() >> n) & 1) for n in range(self.nbits())][::-1]) + ' '
56 |
57 | def __repr__(self):
58 | return str(self.bit)
59 |
60 | class bitfield():
61 | BIT_OFFSET_MARK = 4 # mark offset every OFFSET_MARK bits
62 | MAX_OFFSET_LEN = 2 # max number of digits in offset
63 |
64 | ints = [] # Bitfields list, must be bit() instance
65 | width = 16 # Bits per string
66 | endianness = 'l' # only little endian for now, TODO: add support for more
67 |
68 | def __init__(self, *args, **kwargs):
69 | self.ints = list()
70 |
71 | for arg in list(args):
72 | if arg.__class__ == int:
73 | self.ints.append(bits(arg))
74 |
75 | elif arg.__class__ == tuple and len(arg) == 2:
76 | self.ints.append(bits(arg[0], arg[1]))
77 |
78 | elif arg.__class__ == bits:
79 | self.ints.append(arg)
80 |
81 | if kwargs.get('reverse', None) != None:
82 | self.reverse()
83 |
84 | if kwargs.get('width', None) != None:
85 | self.width = kwargs.get('width', 16)
86 |
87 | def reverse(self):
88 | self.ints = self.ints[::-1]
89 |
90 | def bitstr(self):
91 | res = '\n'
92 |
93 | # create bit offset marks for every BIT_OFFSET_MARK bits
94 | bit_offset_marks = []
95 | # for every bit string
96 | for n in range(self.nbits_total(), 0, -self.width): # little endian order
97 | marks = ' '
98 | # for every mark
99 | for i in range(0, self.width, self.BIT_OFFSET_MARK):
100 | # add offsets
101 | for sp in range(0, len(BIT_SEPARATOR) * (self.BIT_OFFSET_MARK - 1) + self.BIT_OFFSET_MARK):
102 | marks += ' '
103 |
104 | nmark = n - i - self.BIT_OFFSET_MARK
105 | if nmark < 0: break
106 | mark = str(nmark)
107 | while len(mark) < self.MAX_OFFSET_LEN: mark = '0' + mark
108 | marks += mark
109 |
110 | bit_offset_marks.append(marks)
111 |
112 | # pass one: concatenate components
113 | s = ''.join([str(i) for i in self.ints])
114 |
115 | # pass two: leave only `width` bits on one line,
116 | # add space for offset marks
117 | npbits = 0
118 | ns = ''
119 | cbitcnt = 0
120 | intidx = 0
121 | ccol = color()
122 | for ch in s:
123 | if isbit(ch):
124 | if cbitcnt >= (self.ints[intidx].nbits() - 1):
125 | cbitcnt = 0
126 | intidx += 1
127 | ccol = color()
128 | else:
129 | ns += ccol
130 | cbitcnt += 1
131 |
132 | if npbits and (npbits % self.width) == 0:
133 | ns += '\n ' + ccol
134 | npbits += 1
135 | ns += ch
136 | else:
137 | ns += ch
138 |
139 | bitlist = ns.splitlines()
140 |
141 | for i in range(len(bitlist)):
142 | res += COL_GREY + bit_offset_marks[i] + '\n'
143 | res += bitlist[i] + '\n'
144 |
145 | return res
146 |
147 | def __repr__(self):
148 | return self.bitstr()
149 |
150 | def __str__(self):
151 | return self.bitstr()
152 |
153 | def nbits_total(self):
154 | n = 0
155 | for i in self.ints: n += i.nbits()
156 | return n
157 |
158 | @classmethod
159 | def byte(cls, bit):
160 | return bit / 8
161 |
--------------------------------------------------------------------------------
/lib/dcov.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use Term::ANSIColor qw(:constants);
3 |
4 | $command = $ARGV[0];
5 | $args = join ' ', @ARGV;
6 |
7 | `rm -f dtrace.out`;
8 | print `dtrace -q -n 'pid\$target::main: {printf("%s\\n",probename)}' -c "$args" -o dtrace.out`;
9 | @matches = `cat dtrace.out`;
10 |
11 | @fundump = `otool -tvV $command -p _main`;
12 |
13 | $base = undef;
14 |
15 | foreach $line (@fundump) {
16 | @parts = split /\s/, $line;
17 | next if $#parts < 2;
18 |
19 |
20 | $addr = hex $parts[0];
21 | $base = $addr if $base == undef;
22 |
23 | $match = 0;
24 | foreach (@matches) {
25 | next if /^$/;
26 | next if /(entry|return)/;
27 | $match = 1 if ($addr - $base) == hex;
28 | } continue {
29 | last if $match;
30 | }
31 |
32 | print GREEN if $match;
33 | print $line;
34 | print RESET;
35 | }
36 |
--------------------------------------------------------------------------------
/lldb-run.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # lldb-run: because lldb -s is broken
4 |
5 | import sys
6 | sys.path.insert(0, '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
7 |
8 | import os
9 | from itertools import ifilter
10 | import jinja2
11 |
12 | pid = int(sys.argv[1])
13 |
14 | import lldb
15 |
16 | dbg = lldb.SBDebugger.Create()
17 | dbg.SetAsync(False)
18 |
19 | target = dbg.CreateTarget(None)
20 |
21 | ci = dbg.GetCommandInterpreter()
22 | cro = lldb.SBCommandReturnObject()
23 |
24 | if pid > 0:
25 | listener = lldb.SBListener()
26 | errp = lldb.SBError()
27 | process = target.AttachToProcessWithID(listener, pid, errp)
28 |
29 | sys.stderr.write('attach: {}\n'.format(errp))
30 |
31 | for command in ifilter(None, (l.strip() for l in sys.stdin.readlines())):
32 | comm = str(jinja2.Template(command).render(**os.environ))
33 |
34 | if pid > 0:
35 | ci.HandleCommand(comm, cro)
36 | sys.stderr.write('{}: <{}>\n'.format(comm, cro))
37 | else:
38 | sys.stderr.write('{}\n'.format(comm))
39 |
40 | sys.exit(0)
41 |
--------------------------------------------------------------------------------
/objc/dtrace_objc.h:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * XXX: this works only with compile-time CFStrings
4 | * hitting other types of CFStrings will produce user faults
5 | */
6 | #define cfstring(p) copyinstr(*(user_addr_t *)copyin(p + 2*sizeof(user_addr_t), sizeof(user_addr_t)))
7 |
--------------------------------------------------------------------------------
/objc/notificationhook/Makefile:
--------------------------------------------------------------------------------
1 | HOOK= notificationhook
2 | PROVIDER= notification.provider
3 |
4 | $(HOOK).dylib: $(HOOK).m $(PROVIDER).h
5 | clang -framework Foundation -shared -flat_namespace $< -o $@
6 |
7 | $(PROVIDER).h: $(PROVIDER).d
8 | dtrace -h -s $<
9 |
10 | clean:
11 | rm -f $(HOOK).dylib $(PROVIDER).h
12 |
13 | apply:
14 | @test "$(PID)" -ge 1 2>/dev/null || (echo error: specify PID=; false)
15 | cat hook.lldb | python ../../lldb-run.py $(PID)
16 | sudo dtrace -m $(HOOK).dylib -l
17 |
18 | .PHONY: clean
19 |
--------------------------------------------------------------------------------
/objc/notificationhook/hook.lldb:
--------------------------------------------------------------------------------
1 | expr (void*)dlopen("{{ PWD }}/notificationhook.dylib", 0x2|0x8)
2 | expr (void)notificationhook_do()
3 |
--------------------------------------------------------------------------------
/objc/notificationhook/notification.provider.d:
--------------------------------------------------------------------------------
1 | provider NSNotificationCenter {
2 | probe notification__post(const char *);
3 | };
4 |
--------------------------------------------------------------------------------
/objc/notificationhook/notificationhook.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #include "notification.provider.h"
5 |
6 | static IMP original_NSNotificationCenter$$postNotificationName$_object$_;
7 |
8 | void
9 | NSNotificationCenter$$postNotificationName$_object$_(id self, SEL _cmd, NSString *name, id object)
10 | {
11 | if (NSNOTIFICATIONCENTER_NOTIFICATION_POST_ENABLED()) {
12 | NSNOTIFICATIONCENTER_NOTIFICATION_POST([[name description] UTF8String]);
13 | }
14 |
15 | original_NSNotificationCenter$$postNotificationName$_object$_(self, _cmd, name, object);
16 | }
17 |
18 | void
19 | notificationhook_do(void)
20 | {
21 | Class class = objc_getClass("NSNotificationCenter");
22 | SEL selector = sel_getUid("postNotificationName:object:");
23 |
24 | Method method = class_getInstanceMethod(class, selector);
25 | original_NSNotificationCenter$$postNotificationName$_object$_ = method_getImplementation(method);
26 | method_setImplementation(method, (IMP)NSNotificationCenter$$postNotificationName$_object$_);
27 | }
28 |
--------------------------------------------------------------------------------
/objc/nsnotifications.d:
--------------------------------------------------------------------------------
1 | #!/usr/sbin/dtrace I. -Cs
2 |
3 | #include "dtrace_objc.h"
4 |
5 | #if 1
6 | objc$target::*postNotification*:entry
7 | {
8 | @[cfstring(arg2)] = count();
9 | }
10 |
11 | tick-1s
12 | {
13 | printa(@);
14 | trunc(@);
15 | }
16 |
17 | #endif
18 |
19 | #if 0
20 | objc$target::*addObserver?selector?name?*:entry
21 | {
22 | printf("%d [%s %s] %s\n", tid, probemod, probefunc, cfstring(arg4));
23 | ustack();
24 | }
25 |
26 | objc$target::*postNotificationName?*:entry
27 | {
28 | printf("%d [%s %s] %s\n", tid, probemod, probefunc, cfstring(arg2));
29 | ustack();
30 | }
31 |
32 | objc$target:__CFNotification:*initWithName?*:entry
33 | {
34 | printf("%d [%s %s] %s\n", tid, probemod, probefunc, cfstring(arg2));
35 | ustack();
36 | }
37 | #endif
38 |
--------------------------------------------------------------------------------
/objc/test/ui.clj:
--------------------------------------------------------------------------------
1 | (import '[java.awt Component])
2 | (import '[javax.swing JFrame JButton JOptionPane JTextArea BoxLayout])
3 | (import '[java.awt.event ActionListener])
4 |
5 | (let [frame (doto (JFrame. "Hello Swing")
6 | (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE))
7 | layout (BoxLayout. (.getContentPane frame) BoxLayout/Y_AXIS)
8 | textfield (doto (JTextArea. (slurp "ui.clj"))
9 | (.setEditable false))
10 | button (doto (JButton. "Click Me")
11 | (.setAlignmentX Component/CENTER_ALIGNMENT)
12 | (.addActionListener
13 | (reify ActionListener
14 | (actionPerformed [this evt]
15 | (JOptionPane/showMessageDialog
16 | nil
17 | (str
18 | "Hello from Clojure. "
19 | "Button " (.getActionCommand evt) " clicked."))))))]
20 |
21 | (doto (.getContentPane frame)
22 | (.setLayout layout)
23 | (.add textfield)
24 | (.add button))
25 |
26 | (doto frame
27 | .pack
28 | (.setLocationRelativeTo nil)
29 | (.setVisible true)))
30 |
--------------------------------------------------------------------------------
/sys/acceptsnoop.d:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env dtrace -s
2 |
3 | typedef struct sockaddr_in {
4 | short sin_family;
5 | unsigned short sin_port;
6 | unsigned char a0;
7 | unsigned char a1;
8 | unsigned char a2;
9 | unsigned char a3;
10 | } sockaddr_in_t;
11 |
12 | BEGIN
13 | {
14 | afd["", 0] = 0;
15 | }
16 |
17 | syscall::accept*:entry
18 | {
19 | self->sinaddr = arg1;
20 | }
21 |
22 | syscall::accept*:return
23 | {
24 | this->sin = (struct sockaddr_in *)copyin(self->sinaddr, sizeof(struct sockaddr_in));
25 | printf("%s:%d -- %d.%d.%d.%d:%hu", execname, arg1,
26 | this->sin->a0,
27 | this->sin->a1,
28 | this->sin->a2,
29 | this->sin->a3,
30 | this->sin->sin_port);
31 | afd[execname, arg1] = 1;
32 | }
33 |
34 | syscall::close:entry
35 | /afd[execname, arg0] == 1/
36 | {
37 | printf("%s:%d", execname, arg0);
38 | afd[execname, arg0] = 0;
39 | }
40 |
--------------------------------------------------------------------------------
/sys/connectsnoop.d:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env dtrace -Cs
2 |
3 | /* tested on darwin */
4 |
5 | #define AF_INET 2
6 |
7 | typedef struct sockaddr_in {
8 | short sin_family;
9 | unsigned short sin_port;
10 | unsigned char a0;
11 | unsigned char a1;
12 | unsigned char a2;
13 | unsigned char a3;
14 | } sockaddr_in_t;
15 |
16 | syscall::connect:entry
17 | #ifdef EXECNAME
18 | /execname == EXECNAME/
19 | #endif
20 | {
21 | this->sin = (struct sockaddr_in *)copyin(arg1, sizeof(struct sockaddr_in));
22 | this->fd = arg0;
23 | trace("connect");
24 | }
25 |
26 | syscall::connect:return
27 | /this->sin/
28 | {
29 | printf("%s:%d -- %d.%d.%d.%d:%hu (ret=%d)", execname, this->fd,
30 | this->sin->a0,
31 | this->sin->a1,
32 | this->sin->a2,
33 | this->sin->a3,
34 | this->sin->sin_port,
35 | arg1);
36 | }
37 |
--------------------------------------------------------------------------------
/sys/dispatch_trace.d:
--------------------------------------------------------------------------------
1 | #!/usr/sbin/dtrace -Z -s
2 |
3 | /*
4 | * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
5 | * Copyright (c) 2013 Vladimir Kirillov
6 | *
7 | * @APPLE_APACHE_LICENSE_HEADER_START@
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | *
21 | * @APPLE_APACHE_LICENSE_HEADER_END@
22 | */
23 |
24 | /*
25 | * Usage: dispatch_dtrace.d -p [pid]
26 | * traced process must have been executed with
27 | * DYLD_IMAGE_SUFFIX=_profile or DYLD_IMAGE_SUFFIX=_debug
28 | *
29 | * _OR_
30 | *
31 | * mv -v /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/system/libdispatch.dylib{,.backup}
32 | * cp -v /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/system/{introspection/,}libdispatch.dylib
33 | */
34 |
35 | #pragma D option quiet
36 | #pragma D option bufsize=16m
37 |
38 | BEGIN {
39 | printf("%-8s %-3s %-8s %-35s%-15s%-?s %-43s%-?s %-14s%-?s %s\n",
40 | "Time us", "CPU", "Thread", "Function", "Probe", "Queue", "Label",
41 | "Item", "Kind", "Context", "Symbol");
42 | }
43 |
44 | dispatch$target:::queue-push,
45 | dispatch$target:::queue-push,
46 | dispatch$target:::queue-pop,
47 | dispatch$target:::queue-pop,
48 | dispatch$target:::callout-entry,
49 | dispatch$target:::callout-entry,
50 | dispatch$target:::callout-return,
51 | dispatch$target:::callout-return /!start/ {
52 | start = walltimestamp;
53 | }
54 |
55 | /* probe queue-push/-pop(dispatch_queue_t queue, const char *label,
56 | * dispatch_object_t item, const char *kind,
57 | * dispatch_function_t function, void *context)
58 | */
59 | dispatch$target:::queue-push,
60 | dispatch$target:::queue-push,
61 | dispatch$target:::queue-pop,
62 | dispatch$target:::queue-pop {
63 | printf("%-8d %-3d 0x%08p %-35s%-15s0x%0?p %-43s0x%0?p %-14s0x%0?p",
64 | (walltimestamp-start)/1000, cpu, tid, probefunc, probename, arg0,
65 | copyinstr(arg1, 42), arg2, copyinstr(arg3, 13), arg5);
66 | usym(arg4);
67 | printf("\n");
68 | }
69 |
70 | /* probe callout-entry/-return(dispatch_queue_t queue, const char *label,
71 | * dispatch_function_t function, void *context)
72 | */
73 | dispatch$target:::callout-entry,
74 | dispatch$target:::callout-entry,
75 | dispatch$target:::callout-return,
76 | dispatch$target:::callout-return {
77 | printf("%-8d %-3d 0x%08p %-35s%-15s0x%0?p %-43s%-?s %-14s0x%0?p",
78 | (walltimestamp-start)/1000, cpu, tid, probefunc, probename, arg0,
79 | copyinstr(arg1, 42), "", "", arg3);
80 | usym(arg2);
81 | printf("\n");
82 | }
83 |
--------------------------------------------------------------------------------
/sys/execsnoop.real.d:
--------------------------------------------------------------------------------
1 | #!/usr/sbin/dtrace -Cs
2 | #pragma D option quiet
3 |
4 | fbt::__mac_execve:entry, fbt::posix_spawn:entry
5 | {
6 | self->want_malloc = 1;
7 | }
8 |
9 | /*
10 | * First _MALLOC call inside execve/posix_spawn allocates memory
11 | * for struct image_params, which will later be used to store
12 | * pointers to copied in argv vector.
13 | *
14 | * We can't get this pointer from any of exec_* functions because
15 | * they are static and not exposed to fbt in the vanilla kernel.
16 | */
17 |
18 | fbt::_MALLOC:return
19 | /self->want_malloc == 1/
20 | {
21 | self->imgp = (struct image_params *)arg1;
22 | self->want_malloc = 0;
23 | }
24 |
25 | /*
26 | * At this point we know that the ip_startargv and friends are
27 | * filled in.
28 | */
29 |
30 | proc:::exec-success
31 | {
32 | this->arglen = self->imgp->ip_endargv - self->imgp->ip_startargv;
33 | this->arg = self->imgp->ip_startargv;
34 | printf("[%d->%d] ", ppid, pid);
35 | }
36 |
37 | #define ITER() \
38 | proc:::exec-success \
39 | /this->arglen > 0/ \
40 | { \
41 | printf("%s ", stringof(this->arg)); \
42 | this->arglen -= strlen(stringof(this->arg)) + 1; \
43 | this->arg += strlen(stringof(this->arg)) + 1; \
44 | }
45 |
46 | ITER()
47 | ITER()
48 | ITER()
49 | ITER()
50 | ITER()
51 | ITER()
52 | ITER()
53 | ITER()
54 | ITER()
55 | ITER()
56 | ITER()
57 |
58 | proc:::exec-success
59 | {
60 | printf("\n");
61 | }
62 |
--------------------------------------------------------------------------------
/sys/exectime.d:
--------------------------------------------------------------------------------
1 | proc:::create
2 | {
3 | procs[pid] = timestamp;
4 | printf("%s pid %d ppid %d", execname, pid, ppid);
5 | }
6 |
7 | proc:::exec-success
8 | {
9 | printf("%s pid %d ppid %d creation: %d us", execname, pid, ppid, (timestamp - procs[ppid]) / 1000);
10 | }
11 |
12 | proc:::exit
13 | {
14 | printf("%s pid %d ppid %d lifetime: %d us", execname, pid, ppid, (timestamp - procs[ppid]) / 1000);
15 | }
16 |
--------------------------------------------------------------------------------
/sys/machportdump.c:
--------------------------------------------------------------------------------
1 | /*
2 | File: MachPortDump.c
3 |
4 | Contains: A program to dump the Mach ports for a process.
5 |
6 | Written by: DTS
7 |
8 | Copyright: Copyright (c) 2004 by Apple Computer, Inc., All Rights Reserved.
9 |
10 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
11 | ("Apple") in consideration of your agreement to the following terms, and your
12 | use, installation, modification or redistribution of this Apple software
13 | constitutes acceptance of these terms. If you do not agree with these terms,
14 | please do not use, install, modify or redistribute this Apple software.
15 |
16 | In consideration of your agreement to abide by the following terms, and subject
17 | to these terms, Apple grants you a personal, non-exclusive license, under Apple's
18 | copyrights in this original Apple software (the "Apple Software"), to use,
19 | reproduce, modify and redistribute the Apple Software, with or without
20 | modifications, in source and/or binary forms; provided that if you redistribute
21 | the Apple Software in its entirety and without modifications, you must retain
22 | this notice and the following text and disclaimers in all such redistributions of
23 | the Apple Software. Neither the name, trademarks, service marks or logos of
24 | Apple Computer, Inc. may be used to endorse or promote products derived from the
25 | Apple Software without specific prior written permission from Apple. Except as
26 | expressly stated in this notice, no other rights or licenses, express or implied,
27 | are granted by Apple herein, including but not limited to any patent rights that
28 | may be infringed by your derivative works or by other works in which the Apple
29 | Software may be incorporated.
30 |
31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
32 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
33 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
35 | COMBINATION WITH YOUR PRODUCTS.
36 |
37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
38 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
39 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
41 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
42 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
43 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 |
45 | Change History (most recent first):
46 |
47 | $Log: MachPortDump.c,v $
48 | Revision 1.1 2004/11/01 14:47:10 eskimo1
49 | Initial revision
50 |
51 |
52 | */
53 |
54 | /////////////////////////////////////////////////////////////////
55 |
56 | #include
57 | #include
58 | #include
59 | #include
60 | #include
61 | #include
62 | #include
63 |
64 | #include
65 |
66 | #include
67 |
68 | /////////////////////////////////////////////////////////////////
69 | #pragma mark ***** Compatibility Note
70 |
71 | // Apple strongly recommends that developers avoid using Mach APIs
72 | // directly. Mach APIs represent the lowest-level interface to
73 | // the kernel, and thus they are the most likely to change (or
74 | // become unsupportable) as the kernel evolves.
75 | //
76 | // Apple strongly recommends that developers use high-level wrappers
77 | // around Mach APIs where possible. For example, rather than use
78 | // Mach messages directly, you could use CFMessagePort. You should
79 | // only use Mach APIs if there is no higher-level alternatively.
80 | //
81 | // This sample uses Mach APIs directly, and thus seems to contravene
82 | // the above recommendations. However, this is justified by the last
83 | // sentence of the previous paragraph: the job this sample does,
84 | // displaying information about a Mach task's port right name space,
85 | // is only possible via the use of Mach APIs.
86 | //
87 | // It might make sense for you to copy the techniques used by
88 | // MachPortDump into your application, to help detect port right
89 | // leaks and so on. However, I strongly recommend that you include
90 | // this code only in your debug build.
91 |
92 | /////////////////////////////////////////////////////////////////
93 | #pragma mark ***** The Code
94 |
95 | static const char *gProgramName;
96 |
97 | static void PrintPortSetMembers(mach_port_t taskSendRight, mach_port_name_t portSetName)
98 | // For a given Mach port set within a given task, print the members
99 | // of the port set.
100 | {
101 | kern_return_t err;
102 | kern_return_t junk;
103 | mach_port_name_array_t memberNames;
104 | mach_msg_type_number_t memberNamesCount;
105 | mach_msg_type_number_t memberIndex;
106 |
107 | memberNames = NULL;
108 |
109 | // Get an array of members.
110 |
111 | err = mach_port_get_set_status(
112 | taskSendRight,
113 | portSetName,
114 | &memberNames,
115 | &memberNamesCount
116 | );
117 |
118 | // Iterate over the array, printing each one. Note that we print 6 members to
119 | // a line and we start every line except the second with enough spaces to
120 | // account for the information that we print that's common to each type
121 | // of output.
122 |
123 | if (err == KERN_SUCCESS) {
124 | fprintf(stdout, " ");
125 | for (memberIndex = 0; memberIndex < memberNamesCount; memberIndex++) {
126 | if ( (memberIndex != 0) && (memberIndex % 6) == 0) {
127 | // 6 columns of (8 characters plus space)
128 | // plus DNR column (3 chars) plus space
129 | fprintf(stdout, "\n%*s ", (6 * (8 + 1)) + 3 + 1, "");
130 | }
131 | fprintf(stdout, "%#8x ", memberNames[memberIndex]);
132 | }
133 | } else {
134 | fprintf(stdout, "??? ");
135 | }
136 |
137 | // Clean up.
138 |
139 | if (memberNames != NULL) {
140 | junk = vm_deallocate(mach_task_self(), (vm_address_t) memberNames, memberNamesCount * sizeof(*memberNames));
141 | assert(junk == KERN_SUCCESS);
142 | }
143 | }
144 |
145 | static void PrintPortReceiveStatus(mach_port_t taskSendRight, mach_port_name_t receiveRight)
146 | // Print information about the Mach receive right in the specified
147 | // task.
148 | {
149 | kern_return_t err;
150 | mach_port_status_t status;
151 | mach_msg_type_number_t statusCount;
152 |
153 | // Get information about the the right.
154 |
155 | statusCount = MACH_PORT_RECEIVE_STATUS_COUNT;
156 | err = mach_port_get_attributes(
157 | taskSendRight,
158 | receiveRight,
159 | MACH_PORT_RECEIVE_STATUS,
160 | (mach_port_info_t) &status,
161 | &statusCount
162 | );
163 | assert( (err != KERN_SUCCESS) || (statusCount == MACH_PORT_RECEIVE_STATUS_COUNT) );
164 |
165 | // Print it, as a group of flags followed by 6 columns of numbers,
166 | // which are basically all counters.
167 |
168 | if (err == KERN_SUCCESS) {
169 | fprintf(
170 | stdout,
171 | "%c%c%c ",
172 | (status.mps_nsrequest ? 'N' : '-'),
173 | (status.mps_pdrequest ? 'P' : '-'),
174 | (status.mps_srights ? 'S' : '-')
175 | );
176 |
177 | fprintf(
178 | stdout,
179 | "%8u %8u %8u %8u %8u %8u",
180 | status.mps_seqno,
181 | status.mps_mscount,
182 | status.mps_qlimit,
183 | status.mps_msgcount,
184 | status.mps_sorights,
185 | status.mps_pset
186 | );
187 | // The kernel always sets mps_flags to 0, so we don't both printing it.
188 | assert(status.mps_flags == 0);
189 | } else {
190 | fprintf(
191 | stdout,
192 | "??? %8s %8s %8s %8s %8s %8s",
193 | "???", "???", "???", "???", "???", "???"
194 | );
195 | }
196 | }
197 |
198 | static kern_return_t PrintProcessPortSpace(pid_t pid, bool verbose)
199 | // Prints port rights owned by the specified process.
200 | {
201 | kern_return_t err;
202 | kern_return_t junk;
203 | mach_port_t taskSendRight;
204 | mach_port_name_array_t rightNames;
205 | mach_msg_type_number_t rightNamesCount;
206 | mach_port_type_array_t rightTypes;
207 | mach_msg_type_number_t rightTypesCount;
208 | unsigned int i;
209 |
210 | taskSendRight = MACH_PORT_NULL;
211 | rightNames = NULL;
212 | rightTypes = NULL;
213 |
214 | // Get the task control port for the process.
215 |
216 | err = task_for_pid(mach_task_self(), pid, &taskSendRight);
217 | if (err != KERN_SUCCESS) {
218 | fprintf(stderr, "%s: Could not attach to process %lld (%#08x).\n", gProgramName, (long long) pid, err);
219 | }
220 |
221 | // Get a snapshot of the port name space for the task.
222 |
223 | if (err == KERN_SUCCESS) {
224 | err = mach_port_names(taskSendRight, &rightNames, &rightNamesCount, &rightTypes, &rightTypesCount);
225 | }
226 | if (err == KERN_SUCCESS) {
227 | if ( rightNamesCount != rightTypesCount ) {
228 | fprintf(stderr, "%s: Count mismatch (%u/%u)\n", gProgramName, rightNamesCount, rightTypesCount);
229 | err = KERN_FAILURE;
230 | }
231 | }
232 |
233 | // Print that snapshot.
234 |
235 | if (err == KERN_SUCCESS) {
236 | fprintf(stdout, " Name Send Receive SendOnce PortSet DeadName DNR");
237 | if (verbose) {
238 | fprintf(stdout, " flg seqno mscount qlimit msgcount sorights pset");
239 | }
240 | fprintf(stdout, "\n");
241 | fprintf(stdout, " ---- ---- ------- -------- ------- -------- ---");
242 | if (verbose) {
243 | fprintf(stdout, " --- ----- ------- ------ -------- -------- ----");
244 | }
245 | fprintf(stdout, "\n");
246 |
247 | // For each name, print a reference count of each type of right. If running
248 | // verbose, print other information as well.
249 |
250 | for (i = 0; i < rightNamesCount; i++) {
251 | mach_port_right_t right;
252 |
253 | // We print the right name in hex because it makes it easier to
254 | // see the index and generation fields. See for
255 | // information about this.
256 |
257 | fprintf(stdout, "%#8x ", rightNames[i]);
258 |
259 | for (right = MACH_PORT_RIGHT_SEND; right <= MACH_PORT_RIGHT_DEAD_NAME; right++) {
260 | mach_port_urefs_t refCount;
261 |
262 | // If the rightTypes for this name has the bit associated
263 | // with this type of right set (that is, if the name
264 | // references this type of right), get the name's reference
265 | // for this right and print it. Otherwise just print an
266 | // empty string to keep the columns lined up.
267 |
268 | if (rightTypes[i] & MACH_PORT_TYPE(right)) {
269 |
270 | err = mach_port_get_refs(taskSendRight, rightNames[i], right, &refCount);
271 | if (err == KERN_SUCCESS) {
272 | fprintf(stdout, "%8d ", refCount);
273 | } else {
274 | fprintf(stdout, "%8s ", "???");
275 | }
276 | } else {
277 | fprintf(stdout, "%8s ", "");
278 | }
279 | }
280 | if ( rightTypes[i] & MACH_PORT_TYPE_DNREQUEST ) {
281 | fprintf(stdout, "yes ");
282 | } else {
283 | fprintf(stdout, " ");
284 | }
285 |
286 | if (verbose) {
287 | if (rightTypes[i] & MACH_PORT_TYPE_PORT_SET) {
288 | PrintPortSetMembers(taskSendRight, rightNames[i]);
289 | } else if (rightTypes[i] & MACH_PORT_TYPE_RECEIVE) {
290 | PrintPortReceiveStatus(taskSendRight, rightNames[i]);
291 | }
292 | }
293 | fprintf(stdout, "\n");
294 | }
295 | }
296 |
297 | // Clean up.
298 |
299 | if (rightNames != NULL) {
300 | junk = vm_deallocate(mach_task_self(), (vm_address_t) rightNames, rightNamesCount * sizeof(*rightNames));
301 | assert(junk == KERN_SUCCESS);
302 | }
303 | if (rightTypes != NULL) {
304 | junk = vm_deallocate(mach_task_self(), (vm_address_t) rightTypes, rightTypesCount * sizeof(*rightTypes));
305 | assert(junk == KERN_SUCCESS);
306 | }
307 | if (taskSendRight != MACH_PORT_NULL) {
308 | junk = mach_port_deallocate(mach_task_self(), taskSendRight);
309 | assert(junk == KERN_SUCCESS);
310 | }
311 |
312 | return err;
313 | }
314 |
315 | typedef struct kinfo_proc kinfo_proc;
316 |
317 | static int GetBSDProcessList(kinfo_proc **procList, size_t *procCount)
318 | // Returns a list of all BSD processes on the system. This routine
319 | // allocates the list and puts it in *procList and a count of the
320 | // number of entries in *procCount. You are responsible for freeing
321 | // this list (use "free" from System framework).
322 | // On success, the function returns 0.
323 | // On error, the function returns a BSD errno value.
324 | {
325 | int err;
326 | kinfo_proc * result;
327 | bool done;
328 | static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
329 | // Declaring name as const requires us to cast it when passing it to
330 | // sysctl because the prototype doesn't include the const modifier.
331 | size_t length;
332 |
333 | assert( procList != NULL);
334 | assert(*procList == NULL);
335 | assert(procCount != NULL);
336 |
337 | *procCount = 0;
338 |
339 | // We start by calling sysctl with result == NULL and length == 0.
340 | // That will succeed, and set length to the appropriate length.
341 | // We then allocate a buffer of that size and call sysctl again
342 | // with that buffer. If that succeeds, we're done. If that fails
343 | // with ENOMEM, we have to throw away our buffer and loop. Note
344 | // that the loop causes use to call sysctl with NULL again; this
345 | // is necessary because the ENOMEM failure case sets length to
346 | // the amount of data returned, not the amount of data that
347 | // could have been returned.
348 |
349 | result = NULL;
350 | done = false;
351 | do {
352 | assert(result == NULL);
353 |
354 | // Call sysctl with a NULL buffer.
355 |
356 | length = 0;
357 | err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
358 | NULL, &length,
359 | NULL, 0);
360 | if (err == -1) {
361 | err = errno;
362 | }
363 |
364 | // Allocate an appropriately sized buffer based on the results
365 | // from the previous call.
366 |
367 | if (err == 0) {
368 | result = malloc(length);
369 | if (result == NULL) {
370 | err = ENOMEM;
371 | }
372 | }
373 |
374 | // Call sysctl again with the new buffer. If we get an ENOMEM
375 | // error, toss away our buffer and start again.
376 |
377 | if (err == 0) {
378 | err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
379 | result, &length,
380 | NULL, 0);
381 | if (err == -1) {
382 | err = errno;
383 | }
384 | if (err == 0) {
385 | done = true;
386 | } else if (err == ENOMEM) {
387 | assert(result != NULL);
388 | free(result);
389 | result = NULL;
390 | err = 0;
391 | }
392 | }
393 | } while (err == 0 && ! done);
394 |
395 | // Clean up and establish post conditions.
396 |
397 | if (err != 0 && result != NULL) {
398 | free(result);
399 | result = NULL;
400 | }
401 | *procList = result;
402 | if (err == 0) {
403 | *procCount = length / sizeof(kinfo_proc);
404 | }
405 |
406 | assert( (err == 0) == (*procList != NULL) );
407 |
408 | return err;
409 | }
410 |
411 | static int FindProcessByName(const char *processName, pid_t *pid)
412 | // Find the process that best matches processName and return
413 | // its PID. It first tries to find an exact match; if that fails
414 | // it tries to find a substring match; if that fails it checks
415 | // whether processName is a number and returns that as the PID.
416 | //
417 | // On entry, processName must not be NULL, and it must not be the
418 | // empty string. pid must not be NULL.
419 | // On success, *pid will be the process ID of the found process.
420 | // On error, *pid is undefined.
421 | {
422 | int err;
423 | int foundCount;
424 | kinfo_proc * processList;
425 | size_t processCount;
426 | size_t processIndex;
427 |
428 | assert(processName != NULL);
429 | assert(processName[0] != 0); // needed for strstr to behave
430 | assert(pid != NULL);
431 |
432 | processList = NULL;
433 |
434 | foundCount = 0;
435 |
436 | // Get the list of all processes.
437 |
438 | err = GetBSDProcessList(&processList, &processCount);
439 |
440 | if (err == 0) {
441 |
442 | // Search for an exact match.
443 |
444 | for (processIndex = 0; processIndex < processCount; processIndex++) {
445 | if ( strcmp(processList[processIndex].kp_proc.p_comm, processName) == 0 ) {
446 | *pid = processList[processIndex].kp_proc.p_pid;
447 | foundCount = 1;
448 | break;
449 | }
450 | }
451 |
452 | // If that failed, search for a substring match.
453 |
454 | if (foundCount == 0) {
455 | for (processIndex = 0; processIndex < processCount; processIndex++) {
456 | if ( strstr(processList[processIndex].kp_proc.p_comm, processName) != NULL ) {
457 | *pid = processList[processIndex].kp_proc.p_pid;
458 | foundCount += 1;
459 | }
460 | }
461 | }
462 |
463 | // If we found more than 1, that's ambiguous and we error out.
464 |
465 | if (foundCount > 1) {
466 | fprintf(stderr, "%s: '%s' does not denote a unique process.\n", gProgramName, processName);
467 | err = EINVAL;
468 | }
469 | }
470 |
471 | // If still not found, try processName as a PID.
472 |
473 | if ( (err == 0) && (foundCount == 0) ) {
474 | char * firstInvalid;
475 |
476 | *pid = (pid_t) strtol(processName, &firstInvalid, 10);
477 | if ( (processName[0] == 0) || (*firstInvalid != 0) ) {
478 | err = EINVAL;
479 | }
480 | }
481 |
482 | free(processList);
483 |
484 | return err;
485 | }
486 |
487 | static void PrintUsage(void)
488 | {
489 | fprintf(stderr, "usage: %s [options] [ [ pid | name ]... ]\n", gProgramName);
490 | fprintf(stderr, " Send, Receive, SendOnce, PortSet, DeadName = right reference counts\n");
491 | fprintf(stderr, " DNR = dead name request\n");
492 | fprintf(stderr, " -w wide output, with lots of extra info\n");
493 | fprintf(stderr, " flg = N (no senders) P (port dead) S (send rights)\n");
494 | fprintf(stderr, " seqno = sequence number\n");
495 | fprintf(stderr, " mscount = make-send count\n");
496 | fprintf(stderr, " qlimit = queue limit\n");
497 | fprintf(stderr, " msgcount = message count\n");
498 | fprintf(stderr, " sorights = send-once right count\n");
499 | fprintf(stderr, " pset = port set count\n");
500 | }
501 |
502 | int main(int argc, char * argv[])
503 | {
504 | kern_return_t err;
505 | bool verbose;
506 | int ch;
507 | int argIndex;
508 |
509 | // Set gProgramName to the last path component of argv[0]
510 |
511 | gProgramName = strrchr(argv[0], '/');
512 | if (gProgramName == NULL) {
513 | gProgramName = argv[0];
514 | } else {
515 | gProgramName += 1;
516 | }
517 |
518 | // Parse our options.
519 |
520 | verbose = false;
521 | do {
522 | ch = getopt(argc, argv, "w");
523 | if (ch != -1) {
524 | switch (ch) {
525 | case 'w':
526 | verbose = true;
527 | break;
528 | case '?':
529 | default:
530 | PrintUsage();
531 | exit(EXIT_FAILURE);
532 | break;
533 | }
534 | }
535 | } while (ch != -1);
536 |
537 | // Handle the remaining arguments. If none, we work against ourselves.
538 | // Otherwise each string is treated as a process name, and we look that
539 | // up using FindProcessByName.
540 |
541 | if (argv[optind] == NULL) {
542 | err = PrintProcessPortSpace(getpid(), verbose);
543 | } else {
544 | for (argIndex = optind; argIndex < argc; argIndex++) {
545 | pid_t pid;
546 |
547 | if (argIndex > optind) {
548 | fprintf(stdout, "\n");
549 | }
550 | if (argv[argIndex][0] == 0) {
551 | err = EINVAL;
552 | } else {
553 | err = FindProcessByName(argv[argIndex], &pid);
554 | }
555 | if (err == 0) {
556 | fprintf(stdout, "Mach ports for '%s' (%lld):\n", argv[argIndex], (long long) pid);
557 | fprintf(stdout, "\n");
558 |
559 | err = PrintProcessPortSpace(pid, verbose);
560 | }
561 |
562 | if (err != 0) {
563 | break;
564 | }
565 | }
566 | }
567 |
568 | if (err != 0) {
569 | fprintf(stderr, "%s: Failed with error %d.\n", gProgramName, err);
570 | }
571 | return (err == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
572 | }
573 |
--------------------------------------------------------------------------------
/sys/machtrace.d:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | #pragma D option quiet
4 | * usage example:
5 | * dtrace -x dynvarsize=4m -x evaltime=exec -Cs machtrace.d -c ls
6 | * dtrace -x dynvarsize=4m -x evaltime=exec -Cs machtrace.d -p 10
7 | */
8 |
9 | /*
10 | * trace system calls together with mach traps
11 | */
12 |
13 | /*
14 | * Command line arguments
15 | */
16 | inline int OPT_follow = 1;
17 | inline int OPT_printid = 1;
18 | inline int OPT_relative = 1;
19 | inline int OPT_elapsed = 1;
20 | inline int OPT_cpu = 0;
21 | inline int OPT_counts = 1;
22 | inline int OPT_stack = 0;
23 |
24 | #ifdef EXECNAME
25 | inline int OPT_name = 1;
26 | inline string NAME = NAME;
27 | #else
28 | inline int OPT_name = 0;
29 | inline string NAME = "";
30 | #endif
31 |
32 | inline int OPT_trace = 0;
33 | inline string TRACE = "";
34 |
35 | dtrace:::BEGIN
36 | /$target/
37 | {
38 | printf("tracing pid %d\n", $target);
39 | }
40 |
41 | dtrace:::BEGIN
42 | {
43 | /* print header */
44 | /* OPT_printid ? printf("%-8s ","PID/LWP") : 1; */
45 | OPT_printid ? printf("\t%-8s ","PID/THRD") : 1;
46 | OPT_relative ? printf("%8s ","RELATIVE") : 1;
47 | OPT_elapsed ? printf("%7s ","ELAPSD") : 1;
48 | OPT_cpu ? printf("%6s ","CPU") : 1;
49 | printf("SYSCALL(args) \t\t = return\n");
50 |
51 | /* globals */
52 | trackedpid[pid] = 0;
53 | self->child = 0;
54 | this->type = 0;
55 | }
56 |
57 | /*
58 | * Syscalls that were started before DTrace attached
59 | */
60 |
61 | syscall:::return,
62 | mach_trap:::return
63 | /!self->start && (pid == $target || ppid == $target)/
64 | {
65 | self->code = errno == 0 ? "" : "Err#";
66 |
67 | printf("UNEXPECTED RETURN: %5d 0x%x %s %s(XXX)\t\t = %d errno = %d\n",
68 | pid, tid, probeprov, probefunc ,(int)arg0, (int)errno);
69 | OPT_stack ? ustack() : 1;
70 | OPT_stack ? trace("\n") : 1;
71 | }
72 |
73 | /*
74 | * Save syscall entry info
75 | */
76 |
77 | /* MacOS X: notice first appearance of child from fork. Its parent
78 | fires syscall::*fork:return in the ususal way (see below) */
79 | syscall:::entry
80 | /OPT_follow && trackedpid[ppid] == -1 && 0 == self->child/
81 | {
82 | /* set as child */
83 | self->child = 1;
84 |
85 | /* print output */
86 | self->code = errno == 0 ? "" : "Err#";
87 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
88 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
89 | OPT_relative ? printf("%8d: ",vtimestamp/1000) : 1;
90 | OPT_elapsed ? printf("%7d: ",0) : 1;
91 | OPT_cpu ? printf("%6d ",0) : 1;
92 | printf("%s()\t\t = %d %s%d\n","fork",
93 | 0,self->code,(int)errno);
94 | }
95 |
96 | /* MacOS X: notice first appearance of child and parent from vfork */
97 | syscall:::entry
98 | /OPT_follow && trackedpid[ppid] > 0 && 0 == self->child/
99 | {
100 | /* set as child */
101 | this->vforking_tid = trackedpid[ppid];
102 | self->child = (this->vforking_tid == tid) ? 0 : 1;
103 |
104 | /* print output */
105 | self->code = errno == 0 ? "" : "Err#";
106 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
107 | OPT_printid ? printf("%5d/0x%x: ",(this->vforking_tid == tid) ? ppid : pid,tid) : 1;
108 | OPT_relative ? printf("%8d: ",vtimestamp/1000) : 1;
109 | OPT_elapsed ? printf("%7d: ",0) : 1;
110 | OPT_cpu ? printf("%6d ",0) : 1;
111 | printf("%s()\t\t = %d %s%d\n","vfork",
112 | (this->vforking_tid == tid) ? pid : 0,self->code,(int)errno);
113 | }
114 |
115 | syscall:::entry,
116 | mach_trap:::entry
117 |
118 | /($target && pid == $target) ||
119 | (OPT_name && NAME == strstr(NAME, execname)) ||
120 | (OPT_name && execname == strstr(execname, NAME)) ||
121 | (self->child)/
122 | {
123 | /* set start details */
124 | self->start = timestamp;
125 | self->vstart = vtimestamp;
126 | self->arg0 = arg0;
127 | self->arg1 = arg1;
128 | self->arg2 = arg2;
129 |
130 | /* count occurances */
131 | OPT_counts == 1 ? @Counts[probeprov, probefunc] = count() : 1;
132 | }
133 |
134 | syscall::select:entry,
135 | syscall::mmap:entry,
136 | syscall::pwrite:entry,
137 | syscall::pread:entry
138 | /($target && pid == $target) ||
139 | (OPT_name && NAME == strstr(NAME, execname)) ||
140 | (OPT_name && execname == strstr(execname, NAME)) ||
141 | (self->child)/
142 | {
143 | self->arg3 = arg3;
144 | self->arg4 = arg4;
145 | self->arg5 = arg5;
146 | }
147 |
148 | /*
149 | * Follow children
150 | */
151 | syscall::fork:entry
152 | /OPT_follow && self->start/
153 | {
154 | /* track this parent process */
155 | trackedpid[pid] = -1;
156 | }
157 |
158 | syscall::vfork:entry
159 | /OPT_follow && self->start/
160 | {
161 | /* track this parent process */
162 | trackedpid[pid] = tid;
163 | }
164 |
165 | /* syscall::rexit:entry */
166 | syscall::exit:entry
167 | {
168 | /* forget child */
169 | self->child = 0;
170 | trackedpid[pid] = 0;
171 | }
172 |
173 | /*
174 | * Check for syscall tracing
175 | */
176 | syscall:::entry
177 | /OPT_trace && probefunc != TRACE/
178 | {
179 | /* drop info */
180 | self->start = 0;
181 | self->vstart = 0;
182 | self->arg0 = 0;
183 | self->arg1 = 0;
184 | self->arg2 = 0;
185 | self->arg3 = 0;
186 | self->arg4 = 0;
187 | self->arg5 = 0;
188 | }
189 |
190 | /*
191 | * Print return data
192 | */
193 |
194 | /*
195 | * NOTE:
196 | * The following code is written in an intentionally repetetive way.
197 | * The first versions had no code redundancies, but performed badly during
198 | * benchmarking. The priority here is speed, not cleverness. I know there
199 | * are many obvious shortcuts to this code, Ive tried them. This style has
200 | * shown in benchmarks to be the fastest (fewest probes, fewest actions).
201 | */
202 |
203 | /* print 3 args, return as hex */
204 | syscall::sigprocmask:return
205 | /self->start/
206 | {
207 | /* calculate elapsed time */
208 | this->elapsed = timestamp - self->start;
209 | self->start = 0;
210 | this->cpu = vtimestamp - self->vstart;
211 | self->vstart = 0;
212 | self->code = errno == 0 ? "" : "Err#";
213 |
214 | /* print optional fields */
215 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
216 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
217 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
218 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
219 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
220 |
221 | /* print main data */
222 | printf("%s(0x%X, 0x%X, 0x%X)\t\t = 0x%X %s%d\n",probefunc,
223 | (int)self->arg0,self->arg1,self->arg2,(int)arg0,
224 | self->code,(int)errno);
225 | OPT_stack ? ustack() : 1;
226 | OPT_stack ? trace("\n") : 1;
227 | self->arg0 = 0;
228 | self->arg1 = 0;
229 | self->arg2 = 0;
230 | }
231 |
232 | /* print 3 args, arg0 as a string */
233 | syscall::execve:return,
234 | syscall::stat:return,
235 | syscall::stat64:return,
236 | syscall::lstat:return,
237 | syscall::lstat64:return,
238 | syscall::access:return,
239 | syscall::mkdir:return,
240 | syscall::chdir:return,
241 | syscall::chroot:return,
242 | syscall::getattrlist:return, /* XXX 5 arguments */
243 | syscall::chown:return,
244 | syscall::lchown:return,
245 | syscall::chflags:return,
246 | syscall::readlink:return,
247 | syscall::utimes:return,
248 | syscall::pathconf:return,
249 | syscall::truncate:return,
250 | syscall::getxattr:return,
251 | syscall::setxattr:return,
252 | syscall::removexattr:return,
253 | syscall::unlink:return,
254 | syscall::open:return,
255 | syscall::open_nocancel:return
256 | /self->start/
257 | {
258 | /* calculate elapsed time */
259 | this->elapsed = timestamp - self->start;
260 | self->start = 0;
261 | this->cpu = vtimestamp - self->vstart;
262 | self->vstart = 0;
263 | self->code = errno == 0 ? "" : "Err#";
264 |
265 | /* print optional fields */
266 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
267 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
268 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
269 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
270 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
271 |
272 | /* print main data */
273 | printf("%s(\"%S\", 0x%X, 0x%X)\t\t = %d %s%d\n",probefunc,
274 | copyinstr(self->arg0),self->arg1,self->arg2,(int)arg0,
275 | self->code,(int)errno);
276 | OPT_stack ? ustack() : 1;
277 | OPT_stack ? trace("\n") : 1;
278 | self->arg0 = 0;
279 | self->arg1 = 0;
280 | self->arg2 = 0;
281 | }
282 |
283 | /* print 3 args, arg1 as a string */
284 | syscall::write:return,
285 | syscall::write_nocancel:return,
286 | syscall::read:return,
287 | syscall::read_nocancel:return
288 | /self->start/
289 | {
290 | /* calculate elapsed time */
291 | this->elapsed = timestamp - self->start;
292 | self->start = 0;
293 | this->cpu = vtimestamp - self->vstart;
294 | self->vstart = 0;
295 | self->code = errno == 0 ? "" : "Err#";
296 |
297 | /* print optional fields */
298 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
299 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
300 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
301 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
302 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
303 |
304 | /* print main data */
305 | printf("%s(0x%X, \"%S\", 0x%X)\t\t = %d %s%d\n",probefunc,self->arg0,
306 | arg0 == -1 ? "" : stringof(copyin(self->arg1,arg0)),self->arg2,(int)arg0,
307 | self->code,(int)errno);
308 | OPT_stack ? ustack() : 1;
309 | OPT_stack ? trace("\n") : 1;
310 | self->arg0 = 0;
311 | self->arg1 = 0;
312 | self->arg2 = 0;
313 | }
314 |
315 | /* print 2 args, arg0 and arg1 as strings */
316 | syscall::rename:return,
317 | syscall::symlink:return,
318 | syscall::link:return
319 | /self->start/
320 | {
321 | /* calculate elapsed time */
322 | this->elapsed = timestamp - self->start;
323 | self->start = 0;
324 | this->cpu = vtimestamp - self->vstart;
325 | self->vstart = 0;
326 | self->code = errno == 0 ? "" : "Err#";
327 |
328 | /* print optional fields */
329 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
330 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
331 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
332 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
333 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
334 |
335 | /* print main data */
336 | printf("%s(\"%S\", \"%S\")\t\t = %d %s%d\n",probefunc,
337 | copyinstr(self->arg0), copyinstr(self->arg1),
338 | (int)arg0,self->code,(int)errno);
339 | OPT_stack ? ustack() : 1;
340 | OPT_stack ? trace("\n") : 1;
341 | self->arg0 = 0;
342 | self->arg1 = 0;
343 | self->arg2 = 0;
344 | }
345 |
346 | /* print 0 arg output */
347 | syscall::*fork:return
348 | /self->start/
349 | {
350 | /* calculate elapsed time */
351 | this->elapsed = timestamp - self->start;
352 | self->start = 0;
353 | this->cpu = vtimestamp - self->vstart;
354 | self->vstart = 0;
355 | self->code = errno == 0 ? "" : "Err#";
356 |
357 | /* print optional fields */
358 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
359 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
360 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
361 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
362 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
363 |
364 | /* print main data */
365 | printf("%s()\t\t = %d %s%d\n",probefunc,
366 | (int)arg0,self->code,(int)errno);
367 | OPT_stack ? ustack() : 1;
368 | OPT_stack ? trace("\n") : 1;
369 | self->arg0 = 0;
370 | self->arg1 = 0;
371 | self->arg2 = 0;
372 | }
373 |
374 | /* print 1 arg output */
375 | syscall::close:return,
376 | syscall::close_nocancel:return
377 | /self->start/
378 | {
379 | /* calculate elapsed time */
380 | this->elapsed = timestamp - self->start;
381 | self->start = 0;
382 | this->cpu = vtimestamp - self->vstart;
383 | self->vstart = 0;
384 | self->code = errno == 0 ? "" : "Err#";
385 |
386 | /* print optional fields */
387 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
388 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
389 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
390 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
391 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
392 |
393 | /* print main data */
394 | printf("%s(0x%X)\t\t = %d %s%d\n",probefunc,self->arg0,
395 | (int)arg0,self->code,(int)errno);
396 | OPT_stack ? ustack() : 1;
397 | OPT_stack ? trace("\n") : 1;
398 | self->arg0 = 0;
399 | self->arg1 = 0;
400 | self->arg2 = 0;
401 | }
402 |
403 | /* print 2 arg output */
404 | syscall::utimes:return,
405 | syscall::munmap:return
406 | /self->start/
407 | {
408 | /* calculate elapsed time */
409 | this->elapsed = timestamp - self->start;
410 | self->start = 0;
411 | this->cpu = vtimestamp - self->vstart;
412 | self->vstart = 0;
413 | self->code = errno == 0 ? "" : "Err#";
414 |
415 | /* print optional fields */
416 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
417 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
418 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
419 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
420 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
421 |
422 | /* print main data */
423 | printf("%s(0x%X, 0x%X)\t\t = %d %s%d\n",probefunc,self->arg0,
424 | self->arg1,(int)arg0,self->code,(int)errno);
425 | OPT_stack ? ustack() : 1;
426 | OPT_stack ? trace("\n") : 1;
427 | self->arg0 = 0;
428 | self->arg1 = 0;
429 | self->arg2 = 0;
430 | }
431 |
432 | /* print pread/pwrite with 4 arguments */
433 | syscall::pread*:return,
434 | syscall::pwrite*:return
435 | /self->start/
436 | {
437 | /* calculate elapsed time */
438 | this->elapsed = timestamp - self->start;
439 | self->start = 0;
440 | this->cpu = vtimestamp - self->vstart;
441 | self->vstart = 0;
442 | self->code = errno == 0 ? "" : "Err#";
443 |
444 | /* print optional fields */
445 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
446 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
447 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
448 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
449 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
450 |
451 | /* print main data */
452 | printf("%s(0x%X, \"%S\", 0x%X, 0x%X)\t\t = %d %s%d\n",probefunc,self->arg0,
453 | stringof(copyin(self->arg1,self->arg2)),self->arg2,self->arg3,(int)arg0,self->code,(int)errno);
454 | OPT_stack ? ustack() : 1;
455 | OPT_stack ? trace("\n") : 1;
456 | self->arg0 = 0;
457 | self->arg1 = 0;
458 | self->arg2 = 0;
459 | self->arg3 = 0;
460 | }
461 |
462 | /* print select with 5 arguments */
463 | syscall::select:return
464 | /self->start/
465 | {
466 | /* calculate elapsed time */
467 | this->elapsed = timestamp - self->start;
468 | self->start = 0;
469 | this->cpu = vtimestamp - self->vstart;
470 | self->vstart = 0;
471 | self->code = errno == 0 ? "" : "Err#";
472 |
473 | /* print optional fields */
474 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
475 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
476 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
477 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
478 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
479 |
480 | /* print main data */
481 | printf("%s(0x%X, 0x%X, 0x%X, 0x%X, 0x%X)\t\t = %d %s%d\n",probefunc,self->arg0,
482 | self->arg1,self->arg2,self->arg3,self->arg4,(int)arg0,self->code,(int)errno);
483 | OPT_stack ? ustack() : 1;
484 | OPT_stack ? trace("\n") : 1;
485 | self->arg0 = 0;
486 | self->arg1 = 0;
487 | self->arg2 = 0;
488 | self->arg3 = 0;
489 | self->arg4 = 0;
490 | }
491 |
492 | /* mmap has 6 arguments */
493 | syscall::mmap:return
494 | /self->start/
495 | {
496 | /* calculate elapsed time */
497 | this->elapsed = timestamp - self->start;
498 | self->start = 0;
499 | this->cpu = vtimestamp - self->vstart;
500 | self->vstart = 0;
501 | self->code = errno == 0 ? "" : "Err#";
502 |
503 | /* print optional fields */
504 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
505 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
506 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
507 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
508 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
509 |
510 | /* print main data */
511 | printf("%s(0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X)\t\t = 0x%X %s%d\n",probefunc,self->arg0,
512 | self->arg1,self->arg2,self->arg3,self->arg4,self->arg5, (int)arg0,self->code,(int)errno);
513 | OPT_stack ? ustack() : 1;
514 | OPT_stack ? trace("\n") : 1;
515 | self->arg0 = 0;
516 | self->arg1 = 0;
517 | self->arg2 = 0;
518 | self->arg3 = 0;
519 | self->arg4 = 0;
520 | self->arg5 = 0;
521 | }
522 |
523 | /* print 3 arg output - default */
524 | syscall:::return,
525 | mach_trap:::return
526 | /self->start/
527 | {
528 | /* calculate elapsed time */
529 | this->elapsed = timestamp - self->start;
530 | self->start = 0;
531 | this->cpu = vtimestamp - self->vstart;
532 | self->vstart = 0;
533 | self->code = errno == 0 ? "" : "Err#";
534 |
535 | /* print optional fields */
536 | /* OPT_printid ? printf("%5d/%d: ",pid,tid) : 1; */
537 | OPT_printid ? printf("%5d/0x%x: ",pid,tid) : 1;
538 | OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
539 | OPT_elapsed ? printf("%7d ",this->elapsed/1000) : 1;
540 | OPT_cpu ? printf("%6d ",this->cpu/1000) : 1;
541 |
542 | /* print main data */
543 | printf("%s %s(0x%X, 0x%X, 0x%X)\t\t = %d %s%d\n",probeprov,probefunc,self->arg0,
544 | self->arg1,self->arg2,(int)arg0,self->code,(int)errno);
545 | OPT_stack ? ustack() : 1;
546 | OPT_stack ? trace("\n") : 1;
547 | self->arg0 = 0;
548 | self->arg1 = 0;
549 | self->arg2 = 0;
550 | }
551 |
552 | dtrace:::END
553 | {
554 | OPT_counts == 1 ? printa(@Counts) : 1;
555 | }
556 |
--------------------------------------------------------------------------------
/sys/portsnoop.d:
--------------------------------------------------------------------------------
1 | #!/usr/sbin/dtrace -Cs
2 |
3 | #define OSX_10_8_2
4 |
5 | #ifdef OSX_10_7_3
6 | #define ipc_space_kernel (void *)0xffffff801341bf40 /* lookup in gdb */
7 | #endif
8 | #ifdef OSX_10_7_4
9 | #define ipc_space_kernel (void *)0xffffff80115b9f40
10 | #endif
11 | #ifdef OSX_10_8_2
12 | #define ipc_space_kernel (void *)0xffffff8043cc6fc0
13 | #endif
14 | #define space_comm(space) ((proc_t)space->is_task->bsd_info)->p_comm
15 | #define IO_BITS_ACTIVE 0x80000000
16 |
17 | ipc_kmsg_send:entry
18 | {
19 | //trace(execname);
20 | this->h = ((struct ipc_kmsg *)arg0)->ikm_header;
21 | this->localp = this->h->msgh_local_port;
22 | this->remotep = this->h->msgh_remote_port;
23 | this->rvalid = this->remotep->ip_object.io_bits & IO_BITS_ACTIVE;
24 | this->rspace = this->remotep->data.receiver;
25 | }
26 |
27 | /*
28 | ipc_kmsg_send:entry
29 | /this->rvalid != 0/
30 | {
31 | this->rspace = this->remotep->data.receiver;
32 | printf("execname %s", execname);
33 | printf(" space %p", this->rspace);
34 | printf(" localp %p", this->localp);
35 | printf(" remotep %p", this->remotep);
36 | }
37 | */
38 |
39 | #if 0
40 | #ifndef FILTER_COMM
41 | ipc_kmsg_send:entry
42 | /this->rspace != ipc_space_kernel/
43 | {
44 | printf("%p -> %p (%s)", this->localp, this->remotep, space_comm(this->rspace));
45 | }
46 | #endif
47 | #endif
48 |
49 | ipc_kmsg_send:entry
50 | /this->rspace == ipc_space_kernel
51 | #ifdef FILTER_COMM
52 | && execname == FILTER_COMM
53 | #endif
54 | /
55 | {
56 | printf("%p -> %p (%s -> kernel)", this->localp, this->remotep, execname);
57 | #if 0
58 | self->mon = 1;
59 | #endif
60 | }
61 |
62 | #if 0
63 | fbt::: /self->mon == 1/ {}
64 |
65 | ipc_kmsg_send:return
66 | /self->mon == 1/
67 | {
68 | self->mon = 0;
69 | }
70 | #endif
71 |
72 | ipc_kmsg_send:entry
73 | /this->rvalid != 0 && this->rspace != ipc_space_kernel
74 | && this->rspace->is_task == 0/
75 | {
76 | printf("%s -> NULL TASK (space %p)", execname, this->rspace);
77 | /*
78 | stack();
79 | */
80 | }
81 |
82 | ipc_kmsg_send:entry
83 | /this->rvalid != 0 && this->rspace != ipc_space_kernel && this->rspace->is_task != 0
84 | #ifdef FILTER_COMM
85 | && (space_comm(this->rspace) == FILTER_COMM || execname == FILTER_COMM)
86 | #endif
87 | /
88 | {
89 | printf("%p -> %p (%s -> %s)", this->localp, this->remotep, execname, space_comm(this->rspace));
90 | @[execname, this->localp, this->remotep, ustack()] = count();
91 | }
92 |
--------------------------------------------------------------------------------
/sys/sockets.d:
--------------------------------------------------------------------------------
1 |
2 | #pragma D option quiet
3 | #pragma D option switchrate=10hz
4 |
5 | /*
6 | * deltas are in microseconds (1 us = 1/1000 ms)
7 | */
8 |
9 | inline int af_unix = 1; /* AF_UNIX defined in sys/socket.h */
10 | inline int af_inet = 2; /* AF_INET defined in bsd/sys/socket.h */
11 | inline int af_inet6 = 30; /* AF_INET6 defined in bsd/sys/socket.h */
12 |
13 | /*
14 | * OSX DTrace stubs
15 | */
16 |
17 | #define NTOHS(X) ((((X) & 0xFF00) >> 8) | (((X) & 0xFF)) << 8)
18 |
19 | /*
20 | * Convert an IPv4 address into a dotted quad decimal string.
21 | * Until the inet_ntoa() functions are available from DTrace, this is
22 | * converted using the existing strjoin() and lltostr(). It's done in
23 | * two parts to avoid exhausting DTrace registers in one line of code.
24 | */
25 | #define INET_NTOA(ADDR_PTR, ADDR_DEST) \
26 | this->a = (uint8_t *)&(ADDR_PTR); \
27 | this->addr1 = strjoin(lltostr(this->a[0] + 0ULL), strjoin(".", strjoin(lltostr(this->a[1] + 0ULL), "."))); \
28 | this->addr2 = strjoin(lltostr(this->a[2] + 0ULL), strjoin(".", lltostr(this->a[3] + 0ULL))); \
29 | (ADDR_DEST) = strjoin(this->addr1, this->addr2);
30 |
31 | #define SOCKADDR_UN(P) \
32 | this->sun = (struct sockaddr_un *)P; \
33 | self->address = this->sun->sun_path; \
34 | self->port = 0;
35 |
36 | #define SOCKADDR_IN(P) \
37 | this->sin = (struct sockaddr_in *)P; \
38 | INET_NTOA(this->sin->sin_addr, self->address); \
39 | self->port = NTOHS(this->sin->sin_port);
40 |
41 | #ifndef IPV6_VERBOSE
42 | #define SOCKADDR_IN6(P) \
43 | this->sin6 = (struct sockaddr_in6 *)P; \
44 | self->port = NTOHS(this->sin6->sin6_port); \
45 | self->address = self->port == 0 ? "::?" : "?";
46 | #else
47 | #define SOCKADDR_IN6(P) \
48 | this->sin6 = (struct sockaddr_in6 *)P; \
49 | self->port = NTOHS(this->sin6->sin6_port); \
50 | self->address = self->port == 0 ? "::?" : "tracememd"; \
51 | tracemem((user_addr_t)&this->sin6->sin6_addr, 16);
52 | #endif
53 |
54 | #define ADDR_INIT(ptr, len) \
55 | this->s = (struct sockaddr *)copyin(ptr, len); \
56 | self->address = "(unknown)"; \
57 | self->port = 0; \
58 | self->family = this->s->sa_family; \
59 |
60 | #define ADDR_CLEANUP() \
61 | self->family = 0; \
62 | self->address = 0; \
63 | self->port = 0; \
64 | self->start = 0;
65 |
66 | #define PRINT_UN() \
67 | this->delta = (timestamp - self->start) / 1000; \
68 | printf("%s(un) %s(%d) %s %d %s\n", probefunc, execname, pid, self->address, this->delta, STR(err, errno)); \
69 | ADDR_CLEANUP();
70 |
71 | #define PRINT() \
72 | this->delta = (timestamp - self->start) / 1000; \
73 | printf("%s(%s=%d) %s(%d) %s:%d %d %s\n", probefunc, AF(self->family), self->s, execname, pid, \
74 | self->address, self->port, this->delta, STR(err, errno)); \
75 | ADDR_CLEANUP();
76 |
77 | #define STR(table, value) (table[value] != NULL ? table[value] : lltostr(value))
78 | #define AF(x) STR(af, x)
79 | #define SOTYPE(x) STR(so_type, x)
80 |
81 | dtrace:::BEGIN
82 | {
83 | /* Add translations as desired from /usr/include/sys/errno.h */
84 | err[0] = "ok";
85 | err[EINTR] = "EINTR";
86 | err[EIO] = "EIO";
87 | err[EACCES] = "EACCES";
88 | err[ENETDOWN] = "ENETDOWN";
89 | err[ENETUNREACH] = "ENETUNREACH";
90 | err[ECONNRESET] = "ECONNRESET";
91 | err[ECONNREFUSED] = "ECONNREFUSED";
92 | err[ETIMEDOUT] = "ETIMEDOUT";
93 | err[EHOSTDOWN] = "EHOSTDOWN";
94 | err[EHOSTUNREACH] = "EHOSTUNREACH";
95 | err[EINPROGRESS] = "EINPROGRESS";
96 |
97 | af[-1] = "UNDEFINED";
98 | af[af_unix] = "un";
99 | af[af_inet] = "in4";
100 | af[af_inet6] = "in6";
101 |
102 | so_type[-1] = "UNDEFINED";
103 | so_type[0] = "UNDEFINED";
104 | so_type[1] = "stream";
105 | so_type[2] = "dgram";
106 | }
107 |
108 | dtrace:::ERROR
109 | /arg4 == DTRACEFLT_UPRIV/
110 | {
111 | printf("user fault: %s\n", execname);
112 | }
113 |
114 | #define socket_syscall_genprobes(func, entry_ptr, entry_len) \
115 | syscall::func:entry { self->s = arg0; ADDR_INIT(entry_ptr, entry_len); self->start = timestamp; } \
116 | syscall::func:entry /self->family == af_inet/ {SOCKADDR_IN(this->s);} \
117 | syscall::func:entry /self->family == af_inet6/ {SOCKADDR_IN6(this->s);} \
118 | syscall::func:entry /self->family == af_unix/ {SOCKADDR_UN(this->s);} \
119 | syscall::func:return /self->family == af_unix/ {PRINT_UN();} \
120 | syscall::func:return /self->family/ {PRINT();}
121 |
122 | socket_syscall_genprobes(connect*, arg1, arg2)
123 | socket_syscall_genprobes(bind, arg1, arg2)
124 |
125 | syscall::socket:entry
126 | {
127 | self->family = arg0;
128 | self->so_type = arg1;
129 | }
130 |
131 | syscall::socket:return
132 | /self->so_type/
133 | {
134 | printf("%s(%s, %s) %s(%d) = %d\n", probefunc, AF(self->family), SOTYPE(self->so_type), execname, pid, arg1);
135 | self->so_type = 0;
136 | self->family = 0;
137 | }
138 |
139 | /*
140 | * accept(2) is special: sockaddrs are written between entry and return by the kernel
141 | */
142 |
143 | syscall::accept*:entry
144 | {
145 | self->sa = arg1; /* kernel will write here later */
146 | self->lenp = (user_addr_t)arg2;
147 |
148 | self->start = timestamp;
149 | }
150 |
151 | syscall::accept*:return
152 | {
153 | this->len = *(socklen_t *)copyin(self->lenp, sizeof(socklen_t));
154 | ADDR_INIT(self->sa, this->len);
155 | }
156 |
157 | syscall::accept*:return
158 | /self->family == af_unix/
159 | {
160 | SOCKADDR_UN(this->s);
161 | PRINT_UN();
162 | }
163 |
164 | syscall::accept*:return
165 | /self->family == af_inet6/
166 | {
167 | SOCKADDR_IN6(this->s);
168 | PRINT();
169 | }
170 |
171 | syscall::accept*:return
172 | /self->family == af_inet/
173 | {
174 | SOCKADDR_IN(this->s);
175 | PRINT();
176 | }
177 |
178 | syscall::accept*:return /self->family/ {PRINT();}
179 |
--------------------------------------------------------------------------------
/sys/waittrace.d:
--------------------------------------------------------------------------------
1 | proc:::exec-success
2 | /execname == EXECNAME/
3 | {
4 | trace(execname);
5 | inf = pid;
6 | }
7 |
8 | syscall::mmap:return
9 | /pid == inf/
10 | {
11 | system(CMD);
12 | exit(0);
13 | }
14 |
--------------------------------------------------------------------------------
/xnu/kdebug.gdb:
--------------------------------------------------------------------------------
1 | set logging file /tmp/gdb
2 | set logging on
3 | #set logging overwrite
4 | target darwin-kernel
5 | file /Volumes/KernelDebugKit/mach_kernel
6 | source /tank/proger/dev/darwin/xnu-1699.24.8/kgmacros
7 | source /tank/proger/dev/darwin/xnu-1699.24.8/kgmacros.my
8 | attach
9 |
--------------------------------------------------------------------------------