{"record":{"id":"34125facb0886934","repo":"wavetermdev/waveterm","slug":"mach-timebase-info-returned-denom-0","errorCode":null,"errorMessage":"mach_timebase_info returned denom=0","messagePattern":"mach_timebase_info returned denom=0","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/procinfo/procinfo_darwin.go","lineNumber":124,"sourceCode":"func initDarwinProcFuncs() error {\n\tdarwinProcOnce.Do(func() {\n\t\thandle, err := purego.Dlopen(systemLibPath, purego.RTLD_LAZY|purego.RTLD_GLOBAL)\n\t\tif err != nil {\n\t\t\tdarwinProcInitErr = fmt.Errorf(\"dlopen %s: %w\", systemLibPath, err)\n\t\t\treturn\n\t\t}\n\t\tdarwinLibHandle = handle\n\n\t\tpurego.RegisterLibFunc(&darwinProcPidInfo, darwinLibHandle, procPidInfoSym)\n\t\tpurego.RegisterLibFunc(&darwinMachTimebase, darwinLibHandle, machTimebaseSym)\n\n\t\tvar tb machTimebaseInfo\n\t\tif rc := darwinMachTimebase(uintptr(unsafe.Pointer(&tb))); rc != kernSuccess {\n\t\t\tdarwinProcInitErr = fmt.Errorf(\"mach_timebase_info failed: %d\", rc)\n\t\t\treturn\n\t\t}\n\t\tif tb.Denom == 0 {\n\t\t\tdarwinProcInitErr = fmt.Errorf(\"mach_timebase_info returned denom=0\")\n\t\t\treturn\n\t\t}\n\n\t\tdarwinTimeScale = float64(tb.Numer) / float64(tb.Denom)\n\t})\n\treturn darwinProcInitErr\n}\n\nfunc getDarwinProcTaskInfo(pid int32) (*procTaskInfo, error) {\n\tif err := initDarwinProcFuncs(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ti procTaskInfo\n\tret := darwinProcPidInfo(\n\t\tpid,\n\t\tprocPidTaskInfo,\n\t\t0,","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/util/procinfo/procinfo_darwin.go#L106-L142","documentation":"mach_timebase_info succeeded but returned denom=0, which would cause a division by zero when computing darwinTimeScale = numer/denom. The library treats this defensively as an init failure and caches it via sync.Once, disabling CPU-time scaling for the process.","triggerScenarios":"First GetProcInfo call on darwin when the kernel fills machTimebaseInfo with Numer>0, Denom==0 — practically only from memory corruption, a misdeclared struct layout, or garbage from a bad FFI binding.","commonSituations":"Struct ABI mismatch after an OS/SDK change so the Denom field reads wrong memory; custom toolchains or emulators (e.g. Rosetta edge cases) returning malformed output.","solutions":["Verify machTimebaseInfo matches the C struct mach_timebase_info { uint32 numer; uint32 denom; } (two uint32, no padding)","Update purego and re-test; binding regressions can mis-map arguments","Log the full tb struct (numer and denom) to confirm the corruption pattern","Avoid re-running in the same process — sync.Once caches the error; restart after fixing","Fall back to a known-good scale (numer=1, denom=1) if exact CPU times are non-critical"],"exampleFix":"// before\ndarwinTimeScale = float64(tb.Numer) / float64(tb.Denom)\n// after\nif tb.Denom == 0 {\n    darwinProcInitErr = fmt.Errorf(\"mach_timebase_info returned denom=0 (numer=%d)\", tb.Numer)\n    return\n}\ndarwinTimeScale = float64(tb.Numer) / float64(tb.Denom)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"info, err := procinfo.GetProcInfo(ctx, nil, pid)\nif err != nil && strings.Contains(err.Error(), \"denom=0\") {\n    // timebase invalid; treat CPU-time data as unavailable\n}","preventionTips":["Verify machTimebaseInfo struct matches the C layout (two uint32)","Test on all supported macOS versions and architectures (amd64/arm64)","Handle the error at startup rather than per-call, since it is cached","Prefer graceful degradation (skip CPU scaling) over hard failure"],"tags":["macos","mach","timebase","division-by-zero"],"backgroundTag":"mach-call-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}