summaryrefslogtreecommitdiff
path: root/src/com/android/loganalysis/parser/TraceFormatParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/loganalysis/parser/TraceFormatParser.java')
-rw-r--r--src/com/android/loganalysis/parser/TraceFormatParser.java158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/com/android/loganalysis/parser/TraceFormatParser.java b/src/com/android/loganalysis/parser/TraceFormatParser.java
new file mode 100644
index 0000000..1c444f4
--- /dev/null
+++ b/src/com/android/loganalysis/parser/TraceFormatParser.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.loganalysis.parser;
+
+import com.android.loganalysis.item.TraceFormatItem;
+
+import com.google.common.base.CaseFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Read trace format and generate a regex that matches output of such format.
+ *
+ * <p>Traces under /d/tracing specify the output format with a printf string. This parser reads such
+ * string, finds all parameters, and generates a regex that matches output of such format. Each
+ * parameter corresponds to a named-capturing group in the regex. The parameter names are converted
+ * to camel case because Java regex group name must contain only letters and numbers.
+ *
+ * <p>An end-to-end example:
+ *
+ * <pre>{@code
+ * List<String> formatLine = Arrays.asList("print fmt: \"foo=%llu, bar:%s\", REC->foo, REC->bar");
+ * TraceFormatItem parsedFormat = new TraceFormatParser.parse(formatLine);
+ * parsedFormat.getParameters(); // "foo", "bar"
+ * parsedFormat.getNumericParameters(); // "foo"
+ * Matcher matcher = parsedFormat.getRegex.matcher("foo=123, bar:enabled");
+ * matcher.matches();
+ * matcher.group("foo") // 123
+ * matcher.group("bar") // "enabled"
+ * }</pre>
+ *
+ * <p>The initial implementation supports some commonly used specifiers: signed and unsigned integer
+ * (with or without long or long long modifier), floating point number (with or without precision),
+ * hexadecimal number (with or without 0's padding), and string (contains only [a-zA-Z_0-9]). It is
+ * assumed no characters found in the format line need to be escaped.
+ *
+ * <p>Some examples of trace format line:
+ *
+ * <p>(thermal/tsens_read)
+ *
+ * <p>print fmt: "temp=%lu sensor=tsens_tz_sensor%u", REC->temp, REC->sensor
+ *
+ * <p>(sched/sched_cpu_hotplug)
+ *
+ * <p>print fmt: "cpu %d %s error=%d", REC->affected_cpu, REC->status ? "online" : "offline",
+ * REC->error
+ *
+ * <p>(mmc/mmc_blk_erase_start)
+ *
+ * <p>print fmt: "cmd=%u,addr=0x%08x,size=0x%08x", REC->cmd, REC->addr, REC->size
+ */
+public class TraceFormatParser implements IParser {
+ // split the raw format line
+ private static final Pattern SPLIT_FORMAT_LINE =
+ Pattern.compile(".*?\"(?<printf>.*?)\"(?<params>.*)");
+ // match parameter names
+ private static final Pattern SPLIT_PARAMS = Pattern.compile("->(?<param>\\w+)");
+ // match and categorize common printf specifiers
+ // use ?: to flag all non-capturing group so any group captured correspond to a specifier
+ private static final Pattern PRINTF_SPECIFIERS =
+ Pattern.compile(
+ "(?<num>%(?:llu|lu|u|lld|ld|d|(?:.\\d*)?f))|(?<hex>%\\d*(?:x|X))|(?<str>%s)");
+
+ // regex building blocks to match simple numeric/hex/string parameters, exposed for unit testing
+ static final String MATCH_NUM = "-?\\\\d+(?:\\\\.\\\\d+)?";
+ static final String MATCH_HEX = "[\\\\da-fA-F]+";
+ static final String MATCH_STR = "[\\\\w]*";
+
+ /** Parse a trace format line and return an {@link TraceFormatItem} */
+ @Override
+ public TraceFormatItem parse(List<String> lines) {
+ // sanity check
+ if (lines == null || lines.size() != 1) {
+ throw new RuntimeException("Cannot parse format line: expect one-line trace format");
+ }
+
+ // split the raw format line
+ Matcher formatLineMatcher = SPLIT_FORMAT_LINE.matcher(lines.get(0));
+ if (!formatLineMatcher.matches()) {
+ throw new RuntimeException("Cannot parse format line: unexpected format");
+ }
+ String printfString = formatLineMatcher.group("printf");
+ String paramsString = formatLineMatcher.group("params");
+
+ // list of parameters, to be populated soon
+ List<String> allParams = new ArrayList<>();
+ List<String> numParams = new ArrayList<>();
+ List<String> hexParams = new ArrayList<>();
+ List<String> strParams = new ArrayList<>();
+
+ // find all parameters and convert them to camel case
+ Matcher paramsMatcher = SPLIT_PARAMS.matcher(paramsString);
+ while (paramsMatcher.find()) {
+ String camelCasedParam =
+ CaseFormat.LOWER_UNDERSCORE.to(
+ CaseFormat.LOWER_CAMEL, paramsMatcher.group("param"));
+ allParams.add(camelCasedParam);
+ }
+
+ // scan the printf string, categorizing parameters and generating a matching regex
+ StringBuffer regexBuilder = new StringBuffer();
+ int paramIndex = 0;
+ String currentParam;
+
+ Matcher printfMatcher = PRINTF_SPECIFIERS.matcher(printfString);
+ while (printfMatcher.find()) {
+ // parameter corresponds to the found specifier
+ currentParam = allParams.get(paramIndex++);
+ if (printfMatcher.group("num") != null) {
+ printfMatcher.appendReplacement(
+ regexBuilder, createNamedRegexGroup(MATCH_NUM, currentParam));
+ numParams.add(currentParam);
+ } else if (printfMatcher.group("hex") != null) {
+ printfMatcher.appendReplacement(
+ regexBuilder, createNamedRegexGroup(MATCH_HEX, currentParam));
+ hexParams.add(currentParam);
+ } else if (printfMatcher.group("str") != null) {
+ printfMatcher.appendReplacement(
+ regexBuilder, createNamedRegexGroup(MATCH_STR, currentParam));
+ strParams.add(currentParam);
+ } else {
+ throw new RuntimeException("Unrecognized specifier: " + printfMatcher.group());
+ }
+ }
+ printfMatcher.appendTail(regexBuilder);
+ Pattern generatedRegex = Pattern.compile(regexBuilder.toString());
+
+ // assemble and return a TraceFormatItem
+ TraceFormatItem item = new TraceFormatItem();
+ item.setRegex(generatedRegex);
+ item.setParameters(allParams);
+ item.setNumericParameters(numParams);
+ item.setHexParameters(hexParams);
+ item.setStringParameters(strParams);
+ return item;
+ }
+
+ /** Helper function to create a regex group with given name. */
+ private static String createNamedRegexGroup(String base, String name) {
+ return String.format("(?<%s>%s)", name, base);
+ }
+}