Publish full source including logcat visual map engine
This commit is contained in:
parent
10902567d8
commit
ac609bc80c
132 changed files with 19071 additions and 2759 deletions
169
backend_logcatpatterns.go
Normal file
169
backend_logcatpatterns.go
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Relationship extraction for the Logcat visual map — ported to Go so the mining
|
||||
// heuristics live in native (compiled) code rather than shipped JavaScript.
|
||||
//
|
||||
// A log line is just text, but Android's framework + system-event logs encode
|
||||
// real relationships: who started whom, who crashed, who got killed, who sent a
|
||||
// signal to which pid. We mine those so the map can draw meaningful edges on top
|
||||
// of the ambient co-occurrence web. Runs in the existing per-line log pipeline
|
||||
// (parseLogcatLine), so the result ships attached to each LogcatLine — no extra IPC.
|
||||
|
||||
// LogRef is one relationship a log line implies. JSON shape matches the frontend.
|
||||
type LogRef struct {
|
||||
Kind string `json:"kind"` // activity|spawn|death|crash|anr|signal|gfx|mention
|
||||
Target string `json:"target"` // package, component, or pid payload
|
||||
TargetKind string `json:"targetKind"` // package|component|pid
|
||||
}
|
||||
|
||||
var (
|
||||
lcpPkg = regexp.MustCompile(`\b([a-z][a-z0-9_]*(?:\.[a-z0-9_]+){2,})\b`)
|
||||
lcpPkgAnchor = regexp.MustCompile(`^[a-z][a-z0-9_]*(?:\.[a-z0-9_]+){2,}$`)
|
||||
lcpComponent = regexp.MustCompile(`(?i)([a-z][a-z0-9_.]+)/([a-z0-9_.$]+)`)
|
||||
lcpEventCSV = regexp.MustCompile(`\[([^\]]*)\]`)
|
||||
lcpSig = regexp.MustCompile(`Sending signal\.\s*PID:\s*(\d+)`)
|
||||
lcpProcess = regexp.MustCompile(`(?i)Process:\s*([a-z][a-z0-9_.]+)`)
|
||||
lcpANRin = regexp.MustCompile(`\bANR in\b`)
|
||||
lcpStartProc = regexp.MustCompile(`\bStart proc\b`)
|
||||
lcpKilling = regexp.MustCompile(`\bKilling\b|\bhas died\b|\bdied\b`)
|
||||
lcpStartAct = regexp.MustCompile(`\bSTART u\d+|\bDisplayed\b|\bmoveTaskTo`)
|
||||
lcpFatal = regexp.MustCompile(`FATAL EXCEPTION`)
|
||||
lcpGfxTag = regexp.MustCompile(`^(SurfaceFlinger|WindowManager|ViewRootImpl|Choreographer|gralloc|OpenGLRenderer)`)
|
||||
lcpMentSkip = regexp.MustCompile(`^(java|javax|sun|kotlin|android|androidx|dalvik)\.`)
|
||||
)
|
||||
|
||||
func lcpFirstPackage(s string) string {
|
||||
if m := lcpPkg.FindStringSubmatch(s); m != nil {
|
||||
return m[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Pull the package field out of an event-log CSV payload (first dotted token).
|
||||
func lcpEventPackage(msg string) string {
|
||||
csv := lcpEventCSV.FindStringSubmatch(msg)
|
||||
if csv == nil {
|
||||
return lcpFirstPackage(msg)
|
||||
}
|
||||
for _, f := range strings.Split(csv[1], ",") {
|
||||
t := strings.TrimSpace(f)
|
||||
if lcpPkgAnchor.MatchString(t) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return lcpFirstPackage(msg)
|
||||
}
|
||||
|
||||
// lcpExtractRefs mines the relationships a single log line implies. Returns an
|
||||
// empty slice for the vast majority of lines.
|
||||
func lcpExtractRefs(tag, msg string) []LogRef {
|
||||
refs := []LogRef{}
|
||||
push := func(kind, target, targetKind string) {
|
||||
if target != "" {
|
||||
refs = append(refs, LogRef{Kind: kind, Target: target, TargetKind: targetKind})
|
||||
}
|
||||
}
|
||||
|
||||
// binary event-log tags (events buffer)
|
||||
switch tag {
|
||||
case "am_proc_start", "am_proc_bound":
|
||||
push("spawn", lcpEventPackage(msg), "package")
|
||||
return refs
|
||||
case "am_proc_died", "am_kill", "am_low_memory":
|
||||
push("death", lcpEventPackage(msg), "package")
|
||||
return refs
|
||||
case "am_crash":
|
||||
push("crash", lcpEventPackage(msg), "package")
|
||||
return refs
|
||||
case "am_anr":
|
||||
push("anr", lcpEventPackage(msg), "package")
|
||||
return refs
|
||||
case "am_activity_launch_time", "am_focused_activity", "am_resume_activity", "am_pause_activity", "wm_focused_window":
|
||||
if c := lcpComponent.FindStringSubmatch(msg); c != nil {
|
||||
push("activity", c[1]+"/"+c[2], "component")
|
||||
} else {
|
||||
push("activity", lcpEventPackage(msg), "package")
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
// framework text logs (main/system buffers)
|
||||
if tag == "ActivityManager" || tag == "ActivityTaskManager" {
|
||||
if lcpANRin.MatchString(msg) {
|
||||
push("anr", lcpFirstPackage(msg), "package")
|
||||
}
|
||||
if lcpStartProc.MatchString(msg) {
|
||||
push("spawn", lcpFirstPackage(msg), "package")
|
||||
}
|
||||
if lcpKilling.MatchString(msg) {
|
||||
push("death", lcpFirstPackage(msg), "package")
|
||||
}
|
||||
if sig := lcpSig.FindStringSubmatch(msg); sig != nil {
|
||||
push("signal", sig[1], "pid")
|
||||
}
|
||||
if lcpStartAct.MatchString(msg) {
|
||||
if c := lcpComponent.FindStringSubmatch(msg); c != nil {
|
||||
push("activity", c[1]+"/"+c[2], "component")
|
||||
} else {
|
||||
push("activity", lcpFirstPackage(msg), "package")
|
||||
}
|
||||
}
|
||||
if len(refs) > 0 {
|
||||
return refs
|
||||
}
|
||||
}
|
||||
|
||||
if tag == "AndroidRuntime" || lcpFatal.MatchString(msg) {
|
||||
if p := lcpProcess.FindStringSubmatch(msg); p != nil {
|
||||
push("crash", p[1], "package")
|
||||
} else {
|
||||
push("crash", lcpFirstPackage(msg), "package")
|
||||
}
|
||||
if len(refs) > 0 {
|
||||
return refs
|
||||
}
|
||||
}
|
||||
|
||||
if tag == "lowmemorykiller" || tag == "lmkd" {
|
||||
push("death", lcpFirstPackage(msg), "package")
|
||||
if len(refs) > 0 {
|
||||
return refs
|
||||
}
|
||||
}
|
||||
|
||||
if lcpGfxTag.MatchString(tag) {
|
||||
if c := lcpComponent.FindStringSubmatch(msg); c != nil {
|
||||
push("gfx", c[1]+"/"+c[2], "component")
|
||||
return refs
|
||||
}
|
||||
}
|
||||
|
||||
return refs
|
||||
}
|
||||
|
||||
// lcpExtractMentions: generic fallback — up to `max` package-looking tokens
|
||||
// (used when the map's "parsed mentions" toggle is on).
|
||||
func lcpExtractMentions(msg string, max int) []LogRef {
|
||||
out := []LogRef{}
|
||||
seen := map[string]bool{}
|
||||
for _, m := range lcpPkg.FindAllStringSubmatch(msg, -1) {
|
||||
if len(out) >= max {
|
||||
break
|
||||
}
|
||||
t := m[1]
|
||||
if seen[t] {
|
||||
continue
|
||||
}
|
||||
seen[t] = true
|
||||
if lcpMentSkip.MatchString(t) {
|
||||
continue
|
||||
}
|
||||
out = append(out, LogRef{Kind: "mention", Target: t, TargetKind: "package"})
|
||||
}
|
||||
return out
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue