Skip to content

ExecutionLogs

Crash Reports: macOS DiagnosticReports (.ips)

macOS crash and diagnostic reports record which binary crashed, its path, parent, signing identity, launch and crash time, even for malware.

Location
/Library/Logs/DiagnosticReports/
Proves
That a given executable ran on the Mac, from which path, launched by which parent, and when it crashed or hung
Timestamps
Local time with UTC offset in JSON strings and file names
Access
root, or membership of _analyticsusers (admins are members)
Retention
Moved to Retired and cleaned up by SubmitDiagInfo; days to weeks
Collection
Aftermath, UAC, sysdiagnose, ditto

What it is

When a process crashes, hangs or exceeds a resource limit, macOS writes a diagnostic report. Since macOS 12 Monterey, crash reports are JSON documents with an .ips extension; earlier releases wrote plain-text .crash files. Console presents both in the same human-readable layout.

For an investigator, a crash report is proof of execution with rich context: the full executable path, the parent process, code-signing identity, the loaded libraries and exact launch and crash times. Malware, droppers and exploit attempts crash more often than polished software, and the report can outlive the binary.

Where it lives

PathContentVersions
/Library/Logs/DiagnosticReports/System-wide and daemon reportsAll
~/Library/Logs/DiagnosticReports/Reports for the user's processesAll
.../DiagnosticReports/Retired/Older reports moved aside by SubmitDiagInfoAll recent
<process>-YYYY-MM-DD-HHMMSS.ipsJSON crash (bug_type 309) and other .ips types12 Monterey+
<process>_YYYY-MM-DD-HHMMSS_<host>.diagText resource and microstackshot reports (e.g. .cpu_resource.diag)Observed on 26 Tahoe
*.crashText crash reports11 Big Sur and earlier
~/Library/Application Support/CrashReporter/<App>_<UUID>.plistPer-app keys such as ForceQuitDate (plus Date, Path read by mac_apt)Observed on 26 Tahoe

The DiagnosticReports folders are mode 770, group _analyticsusers; admin accounts are members, so an admin can read and delete reports without sudo. Per-user folders sit inside ~/Library; collecting with a Full Disk Access terminal avoids surprises.

What it proves

  • A binary at procPath executed, with pid, parentProc / parentPid, and on recent releases responsibleProc and userID.
  • When it started (procLaunch) and when it crashed (captureTime), and how long the system had been up.
  • Code identity at crash time: codeSigningID, codeSigningTeamID, codeSigningFlags, plus bundle version and slice_uuid in the header.
  • Loaded libraries (usedImages), which exposes injected or unusual dylibs.
  • Why it died: exception (type, signal, codes) and termination (namespace, reason, and which process killed it).
  • For force-quits, the CrashReporter plist records ForceQuitDate per application.

It does not prove that the program did anything harmful, and the absence of reports only means nothing crashed (or reports were deleted).

Key fields

.ips files hold two JSON objects: a one-line metadata header, then the report body.

ObjectFieldMeaning
Headername, app_name, bundleIDProcess and bundle
Headerbug_type309 crash; other values for other report types (288 stackshot, 298 observed for JetsamEvent)
Headertimestamp, os_version, incident_id, slice_uuidReport time, OS build, unique ID, binary UUID
BodyprocName, procPath, pidCrashed executable
BodyparentProc, parentPid, responsibleProc, userIDWho launched it and as which user (last two observed on 26)
BodyprocLaunch, captureTime, uptimeLaunch time, crash time, seconds since boot
BodycodeSigningID, codeSigningTeamID, codeSigningFlagsSigning identity (observed on 26)
Bodyexception, termination, asiCause, killer, app-specific messages
BodyusedImages, threads, faultingThreadLoaded images and backtraces
Bodytranslated, cpuTypeRosetta and architecture

Apple redacts paths in procPath and usedImages: the user name becomes USER, volume names become VOLUME and intermediate folders collapse to * (for example /Users/USER/*/tool, observed on macOS 26), so the exact directory is often not recoverable from the report alone.

Timestamps

timestamp, procLaunch and captureTime are strings in local time with an explicit offset, e.g. 2026-09-29 10:09:30.00 +0200. File names use local time without an offset. uptime is seconds since boot. CrashReporter plist dates are plist date values (UTC).

tail -n +2 app-2026-09-29-100930.ips | jq -r '[.procLaunch, .captureTime, .procPath, .parentProc] | @tsv'

Retention

SubmitDiagInfo periodically moves older reports into Retired and cleans that folder up, so expect days to weeks of history rather than months. Users can delete their own reports, and admins can delete system reports because of the _analyticsusers group permission. Older copies may exist in APFS snapshots, Time Machine or a sysdiagnose.

Collection

sudo ditto /Library/Logs/DiagnosticReports ./case/diag_system
for u in /Users/*; do
  [ -d "$u/Library/Logs/DiagnosticReports" ] && \
  sudo ditto "$u/Library/Logs/DiagnosticReports" "./case/diag_$(basename "$u")"
  [ -d "$u/Library/Application Support/CrashReporter" ] && \
  sudo ditto "$u/Library/Application Support/CrashReporter" "./case/crashreporter_$(basename "$u")"
done
  • Aftermath copies system and per-user DiagnosticReports.
  • UAC collects /Library/Logs and each ~/Library/Logs (files/logs/macos.yaml).
  • sysdiagnose bundles recent reports.

Parsing

  • jq or Python: read line 1 as the header, the rest as the body.
for f in */*.ips; do
  head -1 "$f" | jq -r --arg f "$f" '[$f, .bug_type, .name, .timestamp] | @tsv'
done | sort -t$'\t' -k4

tail -n +2 suspect.ips | jq '{procPath, parentProc, responsibleProc, userID,
  codeSigningID, codeSigningTeamID, procLaunch, captureTime, exception, termination}'
  • mac_apt CRASHREPORTER parses the per-user CrashReporter plists (Date, ForceQuitDate, Path).
  • Console on a Mac renders .ips in the traditional text layout.

Investigator tips

  • Grep all reports for /tmp/, /Users/Shared/ and /private/var/folders/ in procPath and usedImages: legitimate apps rarely run from there. Paths under a home folder or volume are redacted (/Users/USER/*/<name>), so recover the real location from other artifacts.
  • An empty codeSigningTeamID, ad-hoc flags, or a parentProc of bash, zsh, osascript or python3 for a GUI-looking binary deserves a closer look. Pivot to launchd jobs.
  • termination names the killing process (byProc) and a namespace; a code-signing termination points to a modified or badly signed binary, which ties in with Gatekeeper and XProtect.
  • Use slice_uuid and usedImages UUIDs to match a binary recovered elsewhere, even if it was renamed.
  • Correlate captureTime with the Unified Logs (ReportCrash, osanalyticshelper) for surrounding context.

See also