aboutsummaryrefslogtreecommitdiffstats
path: root/system_cmds/zlog.tproj
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /system_cmds/zlog.tproj
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'system_cmds/zlog.tproj')
-rw-r--r--system_cmds/zlog.tproj/SymbolicationHelper.c181
-rw-r--r--system_cmds/zlog.tproj/SymbolicationHelper.h35
-rw-r--r--system_cmds/zlog.tproj/entitlements.plist8
-rw-r--r--system_cmds/zlog.tproj/zlog.156
-rw-r--r--system_cmds/zlog.tproj/zlog.c257
5 files changed, 537 insertions, 0 deletions
diff --git a/system_cmds/zlog.tproj/SymbolicationHelper.c b/system_cmds/zlog.tproj/SymbolicationHelper.c
new file mode 100644
index 0000000..68a1285
--- /dev/null
+++ b/system_cmds/zlog.tproj/SymbolicationHelper.c
@@ -0,0 +1,181 @@
+//
+// SymbolicationHelper.c
+// zlog
+//
+// Created by Rasha Eqbal on 2/26/18.
+//
+
+#include "SymbolicationHelper.h"
+
+/*
+ * Most of the CoreSymbolication code here has been copied from ioclasscount in the IOKitTools project.
+ */
+
+#define kAddressKey CFSTR("Address")
+#define kNameKey CFSTR("Name")
+#define kPathKey CFSTR("Path")
+#define kSegmentsKey CFSTR("Segments")
+#define kSizeKey CFSTR("Size")
+#define kUuidKey CFSTR("UUID")
+
+static void AddSymbolOwnerSummary(CSSymbolOwnerRef owner, CFMutableDictionaryRef binaryImages);
+static void ShowBinaryImage(const void *key, const void *value, void *context);
+
+/*
+ * Symbolicates 'addr' using the 'symbolicator' passed in.
+ * Adds owner info to 'binaryImages' for offline symbolication.
+ *
+ * Top-level function that needs to be called on each frame address in the backtrace.
+ */
+void PrintSymbolicatedAddress(CSSymbolicatorRef symbolicator, mach_vm_address_t addr, CFMutableDictionaryRef binaryImages)
+{
+ printf("0x%llx", addr);
+
+ CSSymbolOwnerRef ownerInfo = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, addr, kCSNow);
+ if (!CSIsNull(ownerInfo)) {
+ const char *moduleName = CSSymbolOwnerGetName(ownerInfo);
+ if (moduleName) {
+ printf(" <%s>", moduleName);
+ }
+ }
+
+ CSSymbolRef symbolInfo = CSSymbolicatorGetSymbolWithAddressAtTime(symbolicator, addr, kCSNow);
+ if (!CSIsNull(symbolInfo)) {
+ printf(" %s", CSSymbolGetName(symbolInfo));
+ }
+
+ CSSourceInfoRef sourceInfo = CSSymbolicatorGetSourceInfoWithAddressAtTime(symbolicator, addr, kCSNow);
+ if (!CSIsNull(sourceInfo)) {
+ const char *fileName = CSSourceInfoGetPath(sourceInfo);
+ if (fileName) {
+ printf(" at %s:%d", fileName, CSSourceInfoGetLineNumber(sourceInfo));
+ }
+ }
+ printf("\n");
+
+ AddSymbolOwnerSummary(ownerInfo, binaryImages);
+}
+
+/*
+ * Adds symbolication information for 'owner' to 'binaryImages' to help with offline symbolication.
+ *
+ * This is called from PrintSymbolicatedAddress() on the symbol owner for each address it symbolicates.
+ */
+static void AddSymbolOwnerSummary(CSSymbolOwnerRef owner, CFMutableDictionaryRef binaryImages)
+{
+ const CFUUIDBytes *uuidBytes = NULL;
+ CFUUIDRef uuid = NULL;
+ CFStringRef uuidString = NULL, path = NULL, name = NULL;
+ CFMutableDictionaryRef summaryDict = NULL;
+ __block CSSegmentRef textSegment = kCSNull, textExecSegment = kCSNull;
+ CSSegmentRef segment = kCSNull;
+ CSRange range;
+ CFNumberRef address = NULL, size = NULL;
+
+#define RETURN_IF_NULL(ptr) \
+if (!(ptr)) { \
+goto cleanup; \
+}
+
+ uuidBytes = CSSymbolOwnerGetCFUUIDBytes(owner);
+ if (uuidBytes) {
+ uuid = CFUUIDCreateFromUUIDBytes(NULL, *uuidBytes);
+ if (uuid) {
+ uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
+ }
+ }
+ RETURN_IF_NULL(uuidString);
+
+ if (!CFDictionaryContainsKey(binaryImages, uuidString)) {
+ summaryDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ RETURN_IF_NULL(summaryDict);
+
+ CFDictionarySetValue(summaryDict, kUuidKey, uuidString);
+
+ path = CFStringCreateWithCString(kCFAllocatorDefault, CSSymbolOwnerGetPath(owner), kCFStringEncodingUTF8);
+ RETURN_IF_NULL(path);
+ CFDictionarySetValue(summaryDict, kPathKey, path);
+
+ name = CFStringCreateWithCString(kCFAllocatorDefault, CSSymbolOwnerGetName(owner), kCFStringEncodingUTF8);
+ RETURN_IF_NULL(name);
+ CFDictionarySetValue(summaryDict, kNameKey, name);
+
+ CSSymbolOwnerForeachSegment(owner, ^(CSSegmentRef segment) {
+ if (strcmp(CSRegionGetName(segment), "__TEXT SEGMENT") == 0) {
+ textSegment = segment;
+ CSRetain(textSegment);
+ } else if (strcmp(CSRegionGetName(segment), "__TEXT_EXEC SEGMENT") == 0) {
+ textExecSegment = segment;
+ CSRetain(textExecSegment);
+ }
+ });
+
+ segment = !CSIsNull(textExecSegment) ? textExecSegment : textSegment;
+ if (CSIsNull(segment)) {
+ goto cleanup;
+ }
+ range = CSRegionGetRange(segment);
+
+ address = CFNumberCreate(NULL, kCFNumberLongLongType, &range.location);
+ RETURN_IF_NULL(address);
+ CFDictionarySetValue(summaryDict, kAddressKey, address);
+
+ size = CFNumberCreate(NULL, kCFNumberLongLongType, &range.length);
+ RETURN_IF_NULL(size);
+ CFDictionarySetValue(summaryDict, kSizeKey, size);
+
+ CFDictionarySetValue(binaryImages, uuidString, summaryDict);
+ }
+
+cleanup:
+ if (size) CFRelease(size);
+ if (address) CFRelease(address);
+ if (!CSIsNull(textExecSegment)) CSRelease(textExecSegment);
+ if (!CSIsNull(textSegment)) CSRelease(textSegment);
+ if (name) CFRelease(name);
+ if (path) CFRelease(path);
+ if (summaryDict) CFRelease(summaryDict);
+ if (uuidString) CFRelease(uuidString);
+ if (uuid) CFRelease(uuid);
+}
+
+/*
+ * Prints offline symbolication information for the images passed in 'binaryImages'.
+ *
+ * Top-level function that needs to be called if the tool wants to include support
+ * for offline symbolication.
+ */
+void PrintBinaryImagesInfo(CFMutableDictionaryRef binaryImages)
+{
+ if (CFDictionaryGetCount(binaryImages) > 0) {
+ printf("\nBinary Images:\n");
+ CFDictionaryApplyFunction(binaryImages, ShowBinaryImage, NULL);
+ } else {
+ printf("No binary images\n");
+ }
+}
+
+/*
+ * Prints information about a binary image necessary for offline symbolication.
+ *
+ * This is called from PrintBinaryImagesInfo() on each element in 'binaryImages'.
+ */
+static void ShowBinaryImage(const void *key, const void *value, void *context)
+{
+ char nameString[256] = {0}, uuidString[256] = {0}, pathString[256] = {0};
+ CFStringRef uuid = (CFStringRef)key;
+ CFStringGetCString(uuid, uuidString, sizeof(uuidString), kCFStringEncodingASCII);
+ CFDictionaryRef summary = (CFDictionaryRef)value;
+
+ CFStringRef name = CFDictionaryGetValue(summary, kNameKey);
+ CFStringGetCString(name, nameString, sizeof(nameString), kCFStringEncodingASCII);
+ CFStringRef path = CFDictionaryGetValue(summary, kPathKey);
+ CFStringGetCString(path, pathString, sizeof(pathString), kCFStringEncodingASCII);
+ CFNumberRef addressNumber = CFDictionaryGetValue(summary, kAddressKey);
+ CFNumberRef sizeNumber = CFDictionaryGetValue(summary, kSizeKey);
+ int64_t address, size;
+ CFNumberGetValue(addressNumber, kCFNumberSInt64Type, &address);
+ CFNumberGetValue(sizeNumber, kCFNumberSInt64Type, &size);
+
+ printf("%p - %p %s <%s> %s\n", (void*)address, (void*)address + size, nameString, uuidString, pathString);
+}
diff --git a/system_cmds/zlog.tproj/SymbolicationHelper.h b/system_cmds/zlog.tproj/SymbolicationHelper.h
new file mode 100644
index 0000000..f9d90c5
--- /dev/null
+++ b/system_cmds/zlog.tproj/SymbolicationHelper.h
@@ -0,0 +1,35 @@
+//
+// SymbolicationHelper.h
+// zlog
+//
+// Created by Rasha Eqbal on 2/26/18.
+//
+
+#ifndef SymbolicationHelper_h
+#define SymbolicationHelper_h
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreSymbolication/CoreSymbolication.h>
+
+/*
+ * Call this function on each address that needs to be symbolicated.
+ *
+ * sym: The CSSymbolicatorRef which will be used for symbolication. For example, to symbolicate
+ * kernel addresses create a CSSymbolicatorRef by calling CSSymbolicatorCreateWithMachKernel().
+ * addr: The address that needs to be symbolicated.
+ * binaryImages: The dictionary that aggregates binary image info for offline symbolication.
+ */
+void PrintSymbolicatedAddress(CSSymbolicatorRef sym, mach_vm_address_t addr, CFMutableDictionaryRef binaryImages);
+
+/*
+ * Call this function to dump binary image info required for offline symbolication.
+ *
+ * binaryImages: The dictionary that stores this info.
+ *
+ * The preferred way to use this is to create a CFMutableDictionaryRef with a call to CFDictionaryCreateMutable()
+ * and pass it in to PrintSymbolicatedAddress() when symbolicating addresses. This will auto-populate the dictionary,
+ * which just needs to be passed in here to print the relevant information.
+ */
+void PrintBinaryImagesInfo(CFMutableDictionaryRef binaryImages);
+
+#endif /* SymbolicationHelper_h */
diff --git a/system_cmds/zlog.tproj/entitlements.plist b/system_cmds/zlog.tproj/entitlements.plist
new file mode 100644
index 0000000..600122d
--- /dev/null
+++ b/system_cmds/zlog.tproj/entitlements.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.kernel.get-kext-info</key>
+ <true/>
+</dict>
+</plist>
diff --git a/system_cmds/zlog.tproj/zlog.1 b/system_cmds/zlog.tproj/zlog.1
new file mode 100644
index 0000000..141caa9
--- /dev/null
+++ b/system_cmds/zlog.tproj/zlog.1
@@ -0,0 +1,56 @@
+.\" Copyright (c) 2018, Apple Inc. All rights reserved.
+.\"
+.Dd February 21, 2018
+.Dt ZLOG 1
+.Os "Mac OS X"
+.Sh NAME
+.Nm zlog
+.Nd show allocation backtraces for kernel zones
+.Sh SYNOPSIS
+.Nm
+.Op Fl t
+.Op Fl z Ar name Op Fl n Ar num | Fl l
+.Op Fl h
+.Sh DESCRIPTION
+.Nm
+displays allocation (and free, if used in corruption tracking mode with
+the boot-arg "-zc") backtraces for zones that have zone logging enabled.
+Zone logging can be turned on by using the boot-arg "zlog<N>=<name>",
+where 'N' can range from 1 to 10, and 'name' is the name of the zone to
+be tracked.
+.Pp
+.Nm
+interprets the following options:
+.Pp
+.Bl -tag -width "disable -"
+.\" -t
+.It Fl t
+(Default) list all the zones that have logging enabled
+.\" -z
+.It Fl z Ar name
+show all allocation backtraces for zone
+.Ar name
+.\" -n
+.It Fl n Ar num
+Can be used in combination with the
+.Fl z
+option to show the top
+.Ar num
+backtraces with the most active references in the zone
+.Ar name
+.\" -l
+.It Fl l
+Can be used in combination with the
+.Fl z
+option to show the backtrace most likely contributing to a leak in the zone
+.Ar name
+(prints the backtrace with the most active references)
+.\" -h
+.It Fl h
+show the help text
+.El
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr zprint 1 ,
+.Xr ioclasscount 1
diff --git a/system_cmds/zlog.tproj/zlog.c b/system_cmds/zlog.tproj/zlog.c
new file mode 100644
index 0000000..c091120
--- /dev/null
+++ b/system_cmds/zlog.tproj/zlog.c
@@ -0,0 +1,257 @@
+//
+// zlog.c
+// zlog
+//
+// Created by Rasha Eqbal on 1/4/18.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach_debug/mach_debug.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreSymbolication/CoreSymbolication.h>
+#include "SymbolicationHelper.h"
+
+extern kern_return_t
+mach_zone_get_btlog_records(host_priv_t host,
+ mach_zone_name_t name,
+ zone_btrecord_array_t *recsp,
+ mach_msg_type_number_t *recsCntp);
+extern kern_return_t
+mach_zone_get_zlog_zones(host_priv_t host,
+ mach_zone_name_array_t *namesp,
+ mach_msg_type_number_t *namesCntp);
+
+static int compare_zone_btrecords(const void *left, const void *right);
+static void usage(FILE *stream, char **argv);
+static void print_zone_info(const char *name);
+static void get_zone_btrecords(const char *name, int topN);
+static void list_zones_with_zlog_enabled(void);
+
+static void usage(FILE *stream, char **argv)
+{
+ fprintf (stream, "usage: %s [-t] [-z name [-n num | -l]] [-h]\n", argv[0]);
+ fprintf (stream, " -t : list all the zones that have logging enabled\n");
+ fprintf (stream, " -z <name> : show all allocation backtraces for zone <name>\n");
+ fprintf (stream, " -n <num> : show top <num> backtraces with the most active references in zone <name>\n");
+ fprintf (stream, " -l : show the backtrace most likely contributing to a leak in zone <name>\n");
+ fprintf (stream, " (prints the backtrace with the most active references)\n");
+ fprintf (stream, " -h : print this help text\n");
+ exit(stream != stdout);
+}
+
+static int compare_zone_btrecords(const void *left, const void *right)
+{
+ zone_btrecord_t *btl = (zone_btrecord_t *)left;
+ zone_btrecord_t *btr = (zone_btrecord_t *)right;
+
+ return (btr->ref_count - btl->ref_count);
+}
+
+static void print_zone_info(const char *name)
+{
+ mach_zone_name_t zname;
+ mach_zone_info_t zone_info;
+ kern_return_t kr;
+
+ strcpy(zname.mzn_name, name);
+ kr = mach_zone_info_for_zone(mach_host_self(), zname, &zone_info);
+ if (kr != KERN_SUCCESS) {
+ fprintf(stderr, "error: call to mach_zone_info_for_zone() failed: %s\n", mach_error_string(kr));
+ exit(1);
+ }
+ printf("zone name : %s\n", name);
+ printf("element size (bytes) : %lld\n", zone_info.mzi_elem_size);
+ printf("in-use size (bytes) : %lld\n", zone_info.mzi_count * zone_info.mzi_elem_size);
+ printf("total size (bytes) : %lld\n", zone_info.mzi_cur_size);
+ printf("\n");
+}
+
+static void get_zone_btrecords(const char *name, int topN)
+{
+ kern_return_t kr;
+ int i, j, index;
+ mach_zone_name_t zname;
+ unsigned int recs_count = 0;
+ zone_btrecord_t *recs, *recs_addr = NULL;
+ CSSymbolicatorRef kernelSym;
+ CFMutableDictionaryRef binaryImages;
+
+ /* Create kernel symbolicator */
+ kernelSym = CSSymbolicatorCreateWithMachKernel();
+ if (CSIsNull(kernelSym)) {
+ fprintf(stderr, "error: CSSymbolicatorCreateWithMachKernel() returned NULL\n");
+ exit(1);
+ }
+ /* Create dictionary to collect binary image info for offline symbolication */
+ binaryImages = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ /* Query the kernel for backtrace records */
+ strcpy(zname.mzn_name, name);
+ kr = mach_zone_get_btlog_records(mach_host_self(), zname, &recs_addr, &recs_count);
+ if (kr != KERN_SUCCESS) {
+ fprintf(stderr, "error: call to mach_zone_get_btlog_records() failed: %s\n", mach_error_string(kr));
+ exit(1);
+ }
+
+ if (recs_count == 0) {
+ goto finish;
+ }
+
+ recs = recs_addr;
+ if (topN == 1) {
+ /* Print the backtrace with the highest no. of refs */
+ index = 0;
+ for (i = 0; i < recs_count; i++) {
+ if (recs[i].ref_count > recs[index].ref_count) {
+ index = i;
+ }
+ }
+ recs = recs_addr + index;
+ } else if (topN == 0) {
+ /* Print all backtraces */
+ topN = recs_count;
+ } else {
+ /* Sort the records by no. of refs, and print the top <topN> */
+ qsort(recs, recs_count, sizeof *recs, compare_zone_btrecords);
+ }
+
+ printf("printing top %d (out of %d) allocation backtrace(s) for zone %s\n", topN, recs_count, zname.mzn_name);
+
+ for (i = 0; i < topN; i++) {
+ printf("\nactive refs: %d operation type: %s\n", recs[i].ref_count,
+ (recs[i].operation_type == ZOP_ALLOC)? "ALLOC": (recs[i].operation_type == ZOP_FREE)? "FREE": "UNKNOWN");
+
+ for (j = 0; j < MAX_ZTRACE_DEPTH; j++) {
+ mach_vm_address_t addr = (mach_vm_address_t)recs[i].bt[j];
+ if (!addr) {
+ break;
+ }
+ PrintSymbolicatedAddress(kernelSym, addr, binaryImages);
+ }
+ }
+
+ /* Print relevant info for offline symbolication */
+ PrintBinaryImagesInfo(binaryImages);
+ CFRelease(binaryImages);
+
+finish:
+ if ((recs_addr != NULL) && (recs_count != 0)) {
+ kr = vm_deallocate(mach_task_self(), (vm_address_t) recs_addr, (vm_size_t) (recs_count * sizeof *recs));
+ if (kr != KERN_SUCCESS) {
+ fprintf(stderr, "call to vm_deallocate() failed: %s\n", mach_error_string(kr));
+ exit(1);
+ }
+ }
+ CSRelease(kernelSym);
+}
+
+static void list_zones_with_zlog_enabled(void)
+{
+ kern_return_t kr;
+ mach_zone_name_t *name = NULL;
+ unsigned int name_count = 0, i;
+
+ /* Get names for zones that have zone logging enabled */
+ kr = mach_zone_get_zlog_zones(mach_host_self(), &name, &name_count);
+ if (kr != KERN_SUCCESS) {
+ fprintf(stderr, "error: call to mach_zone_get_zlog_zones() failed: %s\n", mach_error_string(kr));
+ exit(1);
+ }
+
+ if (name_count == 0) {
+ printf("zlog not enabled for any zones.\n");
+ } else {
+ printf("zlog enabled for zones...\n");
+ }
+
+ for (i = 0; i < name_count; i++) {
+ print_zone_info(name[i].mzn_name);
+ }
+
+ if ((name != NULL) && (name_count != 0)) {
+ kr = vm_deallocate(mach_task_self(), (vm_address_t) name, (vm_size_t) (name_count * sizeof *name));
+ if (kr != KERN_SUCCESS) {
+ fprintf(stderr, "call to vm_deallocate() failed: %s\n", mach_error_string(kr));
+ exit(1);
+ }
+ }
+}
+
+#define VERSION_STRING "zlog output version: 1"
+
+static void
+get_osversion(char *buffer, size_t buffer_len)
+{
+ int mib[] = {CTL_KERN, KERN_OSVERSION};
+ int ret;
+ ret = sysctl(mib, sizeof(mib) / sizeof(int), buffer, &buffer_len, NULL, 0);
+ if (ret != 0) {
+ strlcpy(buffer, "Unknown", buffer_len);
+ return;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int c, topN = 0;
+ const char *zone_name = NULL;
+
+ /* Identifier string for SpeedTracer parsing */
+ printf("%s\n\n", VERSION_STRING);
+ char version_buffer[32] = {0};
+ get_osversion(version_buffer, sizeof(version_buffer));
+ printf("Collected on build: %s\n\n", version_buffer);
+
+ if (argc == 1) {
+ /* default when no arguments are specified */
+ list_zones_with_zlog_enabled();
+ printf("Run 'zlog -h' for usage info.\n");
+ return 0;
+ }
+
+ while ((c = getopt(argc, argv, "tz:n:lh")) != -1) {
+ switch(c) {
+ case 't':
+ list_zones_with_zlog_enabled();
+ break;
+ case 'z':
+ zone_name = optarg;
+ break;
+ case 'n':
+ topN = atoi(optarg);
+ break;
+ case 'l':
+ topN = 1;
+ break;
+ case 'h':
+ usage(stdout, argv);
+ break;
+ case '?':
+ default:
+ usage(stderr, argv);
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ usage(stderr, argv);
+ }
+
+ if (zone_name) {
+ print_zone_info(zone_name);
+ get_zone_btrecords(zone_name, topN);
+ } else {
+ /* -n or -l was specified without -z */
+ if (topN != 0) {
+ usage(stderr, argv);
+ }
+ }
+
+ return 0;
+}