package parser import ( "regexp" "strconv" "strings" ) var parseFuncList = []func(chan string, chan jsonObject){ parseMemory, parseMemoryLayout, parseTimestamps, parseStatus, parseHashTables, parseLinux, parseCpu, parseModel, parseDeviceFound, parseKernelCommandLine, parseCma, parseSlub, parsePidMax, parseWorkingset, parseBootloaderDram, parseBootloaderMmc, parseBootloaderConsole, parseBootloaderGadgetDriver, parseBootloaderSetupSize, parseBootloaderCpu, parseBootloaderBoard, parseBootloaderRstStat, parseBootloaderNet, } var reTimestamp = regexp.MustCompile(`^\[[[:blank:]]*[[:digit:]]+\.[[:digit:]]+[[:blank:]]*\]`) var reAddress = regexp.MustCompile(`0x[[:xdigit:]]+`) func parseMemory(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "memory" rLine := regexp.MustCompile("Memory: [[:ascii:]]+ available") rBytes := regexp.MustCompile("[[:digit:]]+[Kk]") for line := range lines { if found := rLine.FindString(line); found != "" { r := rBytes.FindAllString(line, 2) m["available"] = r[0] m["total"] = r[1] chanOutput <- m return } } m["available"] = "" m["total"] = "" chanOutput <- m } func parseMemoryLayout(lines chan string, chanOutput chan jsonObject) { rHeaderArm := regexp.MustCompile("Virtual kernel memory layout:") rHeaderX86 := regexp.MustCompile("Zone ranges:") for line := range lines { if rHeaderArm.MatchString(line) { parseMemoryLayoutArm(lines, chanOutput) return } if rHeaderX86.MatchString(line) { parseMemoryLayoutX86(lines, chanOutput) return } } m := make(jsonObject) m["type"] = "memory_layout" m["layout"] = make([]jsonObject, 0) chanOutput <- m } func parseMemoryLayoutArm(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "memory_layout" m["layout"] = make([]jsonObject, 0) rLine := regexp.MustCompile(`[[:print:]]+:[[:blank:]]*0x[[:xdigit:]]+[[:blank:]]*-[[:blank:]]*0x[[:xdigit:]]+`) rLabel := regexp.MustCompile(`[[:graph:]]+`) for line := range lines { line = removeTimestamp(line) if rLine.MatchString(line) { item := make(jsonObject) item["label"] = rLabel.FindString(line) r := reAddress.FindAllString(line, 2) item["from"] = r[0] item["to"] = r[1] m["layout"] = append(m["layout"].([]jsonObject), item) } else { break } } chanOutput <- m } func parseMemoryLayoutX86(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "memory_layout" m["layout"] = make([]jsonObject, 0) rLine := regexp.MustCompile(`[[:print:]]+\[mem[[:blank:]]+0x[[:xdigit:]]+-0x[[:xdigit:]]+\]`) rLabel := regexp.MustCompile(`[[:graph:]]+`) for line := range lines { line = removeTimestamp(line) if rLine.MatchString(line) { item := make(jsonObject) item["label"] = rLabel.FindString(line) r := reAddress.FindAllString(line, 2) item["from"] = r[0] item["to"] = r[1] m["layout"] = append(m["layout"].([]jsonObject), item) } else { break } } chanOutput <- m } func parseTimestamps(lines chan string, chanOutput chan jsonObject) { rVal := regexp.MustCompile(`[[:digit:]]+\.[[:digit:]]+`) list := make(jsonArray, 0) for line := range lines { if reTimestamp.MatchString(line) { t := reTimestamp.FindString(line) v := rVal.FindString(t) f, _ := strconv.ParseFloat(v, 64) list = append(list, f) } } m := make(jsonObject) m["type"] = "timestamps" m["timestamps"] = list chanOutput <- m } func parseStatus(lines chan string, chanOutput chan jsonObject) { rLine := regexp.MustCompile(`^\[[[:blank:]]*(OK|FAIL)[[:blank:]]*\][[:blank:]]+`) rOk := regexp.MustCompile(`^\[[[:blank:]]*OK[[:blank:]]*\]`) list := make([]jsonObject, 0) for line := range lines { if rLine.MatchString(line) { item := make(jsonObject) if rOk.MatchString(line) { item["status"] = "ok" } else { item["status"] = "fail" } prefix := rLine.FindString(line) label := strings.TrimPrefix(line, prefix) item["label"] = label list = append(list, item) } } m := make(jsonObject) m["type"] = "status" m["list"] = list chanOutput <- m } func parseHashTables(lines chan string, chanOutput chan jsonObject) { rLine := regexp.MustCompile(`[[:graph:]]+ hash table entries:`) list := make([]jsonObject, 0) for line := range lines { if rLine.MatchString(line) { line = removeTimestamp(line) item := make(jsonObject) label := regexp.MustCompile(`[[:print:]]+ hash table entries:`).FindString(line) item["label"] = strings.TrimSuffix(label, " hash table entries:") entries := regexp.MustCompile(`hash table entries: [[:digit:]]+`).FindString(line) entries = strings.TrimPrefix(entries, "hash table entries: ") item["entries"], _ = strconv.ParseInt(entries, 10, 64) order := regexp.MustCompile(`order:? [[:digit:]-]+`).FindString(line) orderPrefix := regexp.MustCompile(`order:? `).FindString(order) order = strings.TrimPrefix(order, orderPrefix) item["order"], _ = strconv.ParseInt(order, 10, 64) size := regexp.MustCompile(`[[:digit:]]+ bytes`).FindString(line) size = strings.TrimSuffix(size, " bytes") item["size"], _ = strconv.ParseInt(size, 10, 64) list = append(list, item) } } m := make(jsonObject) m["type"] = "hash_tables" m["list"] = list chanOutput <- m } func parseLinux(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "linux" m["version"] = "" m["builder"] = "" m["toolchain"] = "" m["label"] = "" rLine := regexp.MustCompile(`Linux version[[:blank:]]+[[:graph:]]+[[:blank:]]+([[:print:]]+)[[:blank:]]+([[:print:]]+)[[:blank:]]+#[[:print:]]+`) for line := range lines { if rLine.MatchString(line) { line = rLine.FindString(line) line = strings.TrimPrefix(line, "Linux version ") m["version"] = regexp.MustCompile(`[[:graph:]]+`).FindString(line) m["os"] = "linux" bracketed := regexp.MustCompile(`\([[:print:]]+?\)`).FindAllString(line, 2) for i, s := range bracketed { switch i { case 0: m["builder"] = removeBrackets(s) case 1: m["toolchain"] = removeBrackets(s) } } m["label"] = regexp.MustCompile(`#[[:print:]]+`).FindString(line) break } } chanOutput <- m } func parseCpu(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "cpu" m["name"] = "" m["id"] = "" m["revision"] = "" m["arch"] = "" m["cr"] = "" m["data_cache"] = "" m["instruction_cache"] = "" rLine1 := regexp.MustCompile(`CPU:[[:blank:]]+[[:print:]]+[[:blank:]]+\[[[:xdigit:]]+\][[:blank:]]+revision[[:blank:]]+[[:digit:]][[:blank:]]+\([[:print:]]+\), cr=[[:xdigit:]]+`) rLine2 := regexp.MustCompile(`CPU:[[:blank:]]+[[:print:]]+[[:blank:]]+data cache,[[:blank:]]+[[:print:]]+[[:blank:]]+instruction cache`) for line := range lines { if rLine1.MatchString(line) { line = rLine1.FindString(line) line = strings.TrimPrefix(line, "CPU: ") name := regexp.MustCompile(`[[:print:]]+\[`).FindString(line) name = strings.TrimSuffix(name, "[") name = strings.TrimSpace(name) m["name"] = name id := regexp.MustCompile(`\[[[:xdigit:]]+\]`).FindString(line) id = strings.TrimPrefix(id, "[") id = strings.TrimSuffix(id, "]") m["id"] = id revision := regexp.MustCompile(`revision[[:blank:]]+[[:digit:]]+`).FindString(line) revision = strings.TrimPrefix(revision, "revision") revision = strings.TrimSpace(revision) m["revision"] = revision arch := regexp.MustCompile(`\([[:print:]]+\)`).FindString(line) arch = strings.TrimPrefix(arch, "(") arch = strings.TrimSuffix(arch, ")") arch = strings.TrimSpace(arch) m["arch"] = arch cr := regexp.MustCompile(`cr=[[:xdigit:]]+`).FindString(line) cr = strings.TrimPrefix(cr, "cr=") m["cr"] = cr } else if rLine2.MatchString(line) { line = rLine2.FindString(line) line = strings.TrimPrefix(line, `CPU: `) dataCache := regexp.MustCompile(`[[:print:]]+data cache`).FindString(line) dataCache = strings.TrimSuffix(dataCache, "data cache") dataCache = strings.TrimSpace(dataCache) m["data_cache"] = dataCache instructionCache := regexp.MustCompile(`,[[:print:]]+instruction cache`).FindString(line) instructionCache = strings.TrimPrefix(instructionCache, ",") instructionCache = strings.TrimSuffix(instructionCache, "instruction cache") instructionCache = strings.TrimSpace(instructionCache) m["instruction_cache"] = instructionCache } } chanOutput <- m } func parseModel(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "model" m["model"] = "" rLine := regexp.MustCompile(`(Machine model: [[:print:]]+)|(Machine: [[:print:]]+)`) rLineEdison := regexp.MustCompile(`Linux version[[:print:]]+edison`) rLineARTIK := regexp.MustCompile(`CPU EXYNOS3250`) for line := range lines { if rLine.MatchString(line) { line = rLine.FindString(line) line = strings.TrimPrefix(line, "Machine model: ") line = strings.TrimPrefix(line, "Machine: ") m["model"] = strings.TrimSpace(line) break } else if rLineEdison.MatchString(line) { m["model"] = "Intel Edison" break } else if rLineARTIK.MatchString(line) { m["model"] = "SAMSUNG ARTIK5" break } } chanOutput <- m } func parseDeviceFound(lines chan string, chanOutput chan jsonObject) { list := make([]string, 0) for line := range lines { for _, e := range deviceTable { if e.re.MatchString(line) { list = append(list, e.name) } } } m := make(jsonObject) m["type"] = "device_found" m["list"] = list chanOutput <- m } func parseKernelCommandLine(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "kernel_command_line" m["kernel_command_line"] = "" rLine := regexp.MustCompile("Kernel command line: [[:print:]]+") for line := range lines { if rLine.MatchString(line) { line = rLine.FindString(line) cmdline := strings.TrimPrefix(line, "Kernel command line: ") cmdline = strings.TrimSpace(cmdline) if strings.HasPrefix(cmdline, `"`) && strings.HasSuffix(cmdline, `"`) { cmdline = strings.TrimPrefix(cmdline, `"`) cmdline = strings.TrimSuffix(cmdline, `"`) } m["kernel_command_line"] = cmdline break } } chanOutput <- m } func parseCma(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "cma" m["size"] = "" m["address"] = "" rLine := regexp.MustCompile("cma: Reserved [[:print:]]+ at [[:print:]]+") for line := range lines { if rLine.MatchString(line) { line = rLine.FindString(line) line = strings.TrimPrefix(line, "cma: Reserved ") size := regexp.MustCompile(`[[:print:]]+ at`).FindString(line) size = strings.TrimSuffix(size, "at") size = strings.TrimSpace(size) m["size"] = size address := regexp.MustCompile(`at [[:print:]]+`).FindString(line) address = strings.TrimPrefix(address, "at") address = strings.TrimSpace(address) m["address"] = address break } } chanOutput <- m } func parseSlub(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "slub" m["hw_align"] = "" m["order"] = "" m["min_objects"] = "" m["cpus"] = "" m["nodes"] = "" rLine := regexp.MustCompile("SLUB: HWalign=[[:digit:]]+, Order=[[:digit:]]+-[[:digit:]]+, MinObjects=[[:digit:]]+, CPUs=[[:digit:]]+, Nodes=[[:digit:]]+") for line := range lines { if rLine.MatchString(line) { line = rLine.FindString(line) hwAlign := regexp.MustCompile(`HWalign=[[:digit:]]+`).FindString(line) hwAlign = strings.TrimPrefix(hwAlign, `HWalign=`) m["hw_align"] = hwAlign order := regexp.MustCompile(`Order=[[:digit:]]+-[[:digit:]]+`).FindString(line) order = strings.TrimPrefix(order, `Order=`) m["order"] = order minObjects := regexp.MustCompile(`MinObjects=[[:digit:]]+`).FindString(line) minObjects = strings.TrimPrefix(minObjects, `MinObjects=`) m["min_objects"] = minObjects cpus := regexp.MustCompile(`CPUs=[[:digit:]]+`).FindString(line) cpus = strings.TrimPrefix(cpus, `CPUs=`) m["cpus"] = cpus nodes := regexp.MustCompile(`Nodes=[[:digit:]]+`).FindString(line) nodes = strings.TrimPrefix(nodes, `Nodes=`) m["nodes"] = nodes break } } chanOutput <- m } func parsePidMax(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "pid_max" m["default"] = "" m["min"] = "" rLine := regexp.MustCompile("pid_max:[[:blank:]]+default:[[:blank:]]*[[:digit:]]+[[:blank:]]+minimum:[[:blank:]]*[[:digit:]]+") for line := range lines { if rLine.MatchString(line) { line = rLine.FindString(line) m["default"] = extract(line, `default:[[:blank:]]*[[:digit:]]+`, `default:[[:blank:]]*`, ``) m["min"] = extract(line, `minimum:[[:blank:]]*[[:digit:]]+`, `minimum:[[:blank:]]*`, ``) break } } chanOutput <- m } func parseWorkingset(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "workingset" m["timestamp bits"] = "" m["max_order"] = "" m["bucket_order"] = "" rLine := regexp.MustCompile("workingset: timestamp_bits=[[:digit:]]+ max_order=[[:digit:]]+ bucket_order=[[:digit:]]+") for line := range lines { if rLine.MatchString(line) { line = rLine.FindString(line) m["timestamp_bits"] = extract(line, `timestamp_bits=[[:digit:]]+`, `timestamp_bits=`, ``) m["max_order"] = extract(line, `max_order=[[:digit:]]+`, `max_order=`, ``) m["bucket_order"] = extract(line, `bucket_order=[[:digit:]]+`, `bucket_order=`, ``) break } } chanOutput <- m } func parseBootloaderDram(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_dram" m["value"] = "" rLine := regexp.MustCompile(`^DRAM:\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func parseBootloaderMmc(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_mmc" m["value"] = "" rLine := regexp.MustCompile(`^MMC:\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func parseBootloaderConsole(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_console" m["in"] = "" m["out"] = "" m["err"] = "" const ( FIND_IN = iota FIND_OUT FIND_ERR FIN ) state := FIND_IN rIn := regexp.MustCompile(`^In:\s+([[:print:]]+)`) rOut := regexp.MustCompile(`^Out:\s+([[:print:]]+)`) rErr := regexp.MustCompile(`^Err:\s+([[:print:]]+)`) for line := range lines { switch state { case FIND_IN: if sub := rIn.FindStringSubmatch(line); len(sub) > 1 { m["in"] = sub[1] state = FIND_OUT } case FIND_OUT: if sub := rOut.FindStringSubmatch(line); len(sub) > 1 { m["out"] = sub[1] state = FIND_ERR } case FIND_ERR: if sub := rErr.FindStringSubmatch(line); len(sub) > 1 { m["err"] = sub[1] state = FIN } } if state == FIN { break } } chanOutput <- m } func parseBootloaderGadgetDriver(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_gadget_driver" m["value"] = "" rLine := regexp.MustCompile(`^GADGET DRIVER:\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func parseBootloaderSetupSize(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_setup_size" m["value"] = "" rLine := regexp.MustCompile(`^Setup Size =\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func parseBootloaderCpu(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_cpu" m["value"] = "" rLine := regexp.MustCompile(`^CPU:\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func parseBootloaderBoard(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_board" m["value"] = "" rLine := regexp.MustCompile(`^Board:\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func parseBootloaderRstStat(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_rst_stat" m["value"] = "" rLine := regexp.MustCompile(`^rst_stat\s+:\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func parseBootloaderNet(lines chan string, chanOutput chan jsonObject) { m := make(jsonObject) m["type"] = "bootloader_net" m["value"] = "" rLine := regexp.MustCompile(`^Net:\s+([[:print:]]+)`) for line := range lines { if rLine.MatchString(line) { if sub := rLine.FindStringSubmatch(line); len(sub) > 1 { m["value"] = sub[1] break } } } chanOutput <- m } func removeBrackets(s string) string { for strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") { openCount := strings.Count(s, "(") closeCount := strings.Count(s, ")") if openCount > closeCount { s = strings.TrimPrefix(s, "(") } else if openCount < closeCount { s = strings.TrimSuffix(s, ")") } else { s = strings.TrimPrefix(s, "(") s = strings.TrimSuffix(s, ")") } } return s } func removeTimestamp(s string) string { if reTimestamp.MatchString(s) { t := reTimestamp.FindString(s) s = strings.TrimPrefix(s, t) } return strings.TrimSpace(s) } func extract(from string, chunk, prefix, suffix string) string { reChunk, err := regexp.Compile(chunk) if err != nil { return "" } rePrefix, err := regexp.Compile(prefix) if err != nil { return "" } reSuffix, err := regexp.Compile(suffix) if err != nil { return "" } b := reChunk.FindString(from) p := rePrefix.FindString(b) b = strings.TrimPrefix(b, p) s := reSuffix.FindString(b) b = strings.TrimSuffix(b, s) return b }