| // Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.android.tools.r8.retrace; |
| |
| import com.android.tools.r8.DiagnosticsHandler; |
| import com.android.tools.r8.Keep; |
| import com.android.tools.r8.retrace.internal.RetraceStackFrameResultWithContextImpl; |
| import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy; |
| import com.android.tools.r8.utils.StringUtils; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| |
| /** |
| * Specialized Retrace class for retracing string retraces, with special handling for appending |
| * additional information into the strings, such as OR's for ambiguous lines. |
| */ |
| @Keep |
| public class StringRetrace extends Retrace<String, StackTraceElementStringProxy> { |
| |
| StringRetrace( |
| StackTraceLineParser<String, StackTraceElementStringProxy> stackTraceLineParser, |
| MappingSupplier<?> mappingSupplier, |
| DiagnosticsHandler diagnosticsHandler, |
| boolean isVerbose) { |
| super(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose); |
| } |
| |
| /** |
| * Default entry point for creating a retracer designed for string input and output. |
| * |
| * @param command the command with information about creating the StringRetrace |
| * @return a StringRetrace object |
| */ |
| public static StringRetrace create(RetraceOptions command) { |
| return create( |
| command.getMappingSupplier(), |
| command.getDiagnosticsHandler(), |
| command.getRegularExpression(), |
| command.isVerbose()); |
| } |
| |
| /** |
| * Entry point for creating a retracer designed for string input and output where the mapping file |
| * has already been parsed. |
| * |
| * @param mappingSupplier a supplier that can be used to construct a retracer |
| * @param diagnosticsHandler a diagnosticshandler for emitting information |
| * @param regularExpression the regular expression to use for identifying information in strings |
| * @param isVerbose specify to emit verbose information |
| * @return a StringRetrace object |
| */ |
| public static StringRetrace create( |
| MappingSupplier<?> mappingSupplier, |
| DiagnosticsHandler diagnosticsHandler, |
| String regularExpression, |
| boolean isVerbose) { |
| return new StringRetrace( |
| StackTraceLineParser.createRegularExpressionParser(regularExpression), |
| mappingSupplier, |
| diagnosticsHandler, |
| isVerbose); |
| } |
| |
| /** |
| * Retraces a list of stack-trace lines and returns a list. Ambiguous and inline frames will be |
| * appended automatically to the retraced string. |
| * |
| * @param stackTrace the incoming stack trace |
| * @param context The context to retrace the stack trace in |
| * @return the retraced stack trace |
| */ |
| public RetraceStackFrameResultWithContext<String> retrace( |
| List<String> stackTrace, RetraceStackTraceContext context) { |
| RetraceStackTraceResult<String> result = retraceStackTrace(stackTrace, context); |
| return RetraceStackFrameResultWithContextImpl.create( |
| joinAmbiguousLines(result.getResult()), result.getContext()); |
| } |
| |
| /** |
| * Retraces a list of parsed stack trace lines and returns a list. Ambiguous and inline frames |
| * will be appended automatically to the retraced string. |
| * |
| * @param stackTrace the incoming parsed stack trace |
| * @param context The context to retrace the stack trace in |
| * @return the retraced stack trace |
| */ |
| public RetraceStackFrameResultWithContext<String> retraceParsed( |
| List<StackTraceElementStringProxy> stackTrace, RetraceStackTraceContext context) { |
| RetraceStackTraceResult<String> result = retraceStackTraceParsed(stackTrace, context); |
| return RetraceStackFrameResultWithContextImpl.create( |
| joinAmbiguousLines(result.getResult()), result.getContext()); |
| } |
| |
| /** |
| * Retraces a single stack trace line and returns the potential list of original frames |
| * |
| * @param stackTraceLine the stack trace line to retrace |
| * @param context The context to retrace the stack trace in |
| * @return the retraced frames |
| */ |
| public RetraceStackFrameResultWithContext<String> retrace( |
| String stackTraceLine, RetraceStackTraceContext context) { |
| RetraceStackFrameAmbiguousResultWithContext<String> listRetraceStackTraceResult = |
| retraceFrame(stackTraceLine, context); |
| return RetraceStackFrameResultWithContextImpl.create( |
| joinAmbiguousLines(Collections.singletonList(listRetraceStackTraceResult)), |
| listRetraceStackTraceResult.getContext()); |
| } |
| |
| /** |
| * Processes supplied strings and calls lineConsumer with retraced strings in a streaming way |
| * |
| * @param lineSupplier the supplier of strings with returning null as terminator |
| * @param lineConsumer the consumer of retraced strings |
| */ |
| public <E extends Throwable> void retraceSupplier( |
| StreamSupplier<E> lineSupplier, Consumer<String> lineConsumer) throws E { |
| RetraceStackTraceContext context = RetraceStackTraceContext.empty(); |
| String retraceLine; |
| while ((retraceLine = lineSupplier.getNext()) != null) { |
| RetraceStackFrameResultWithContext<String> result = retrace(retraceLine, context); |
| context = result.getContext(); |
| result.forEach(lineConsumer); |
| } |
| } |
| |
| private List<String> joinAmbiguousLines( |
| List<RetraceStackFrameAmbiguousResult<String>> retracedResult) { |
| List<String> result = new ArrayList<>(); |
| for (RetraceStackFrameAmbiguousResult<String> ambiguousResult : retracedResult) { |
| boolean addedLines = true; |
| int lineIndex = 0; |
| while (addedLines) { |
| addedLines = false; |
| Set<String> reportedFrames = new HashSet<>(); |
| RetraceStackFrameResult<String> firstResult = null; |
| for (RetraceStackFrameResult<String> inlineFrames : ambiguousResult.getAmbiguousResult()) { |
| if (firstResult == null) { |
| firstResult = inlineFrames; |
| } |
| if (lineIndex < inlineFrames.size()) { |
| addedLines = true; |
| String frameToAdd = inlineFrames.get(lineIndex); |
| if (reportedFrames.add(frameToAdd)) { |
| boolean isAmbiguous = inlineFrames != firstResult; |
| if (isAmbiguous) { |
| result.add(insertOrIntoStackTraceLine(frameToAdd)); |
| } else { |
| result.add(frameToAdd); |
| } |
| } |
| } |
| } |
| lineIndex += 1; |
| } |
| } |
| return result; |
| } |
| |
| private String insertOrIntoStackTraceLine(String stackTraceLine) { |
| // We are reporting an ambiguous frame. To support retracing tools that |
| // retrace line by line we have to emit <OR> at the point of the first 'at ' |
| // if we can find it. |
| int indexToInsertOr = stackTraceLine.indexOf("at "); |
| if (indexToInsertOr < 0) { |
| indexToInsertOr = Math.max(StringUtils.firstNonWhitespaceCharacter(stackTraceLine), 0); |
| } |
| return stackTraceLine.substring(0, indexToInsertOr) |
| + "<OR> " |
| + stackTraceLine.substring(indexToInsertOr); |
| } |
| } |