+ private static final String UNKNOWN_VALUE = "??"; //$NON-NLS-1$
+
+ /**
+ * Cache of all calls to 'addr2line', so that we can avoid recalling the
+ * external process repeatedly.
+ *
+ * It is static, meaning one cache for the whole application, since the
+ * symbols in a file on disk are independent from the trace referring to it.
+ */
+ private static final LoadingCache<FileOffset, @NonNull Iterable<Addr2lineInfo>> ADDR2LINE_INFO_CACHE;
+ static {
+ ADDR2LINE_INFO_CACHE = checkNotNull(CacheBuilder.newBuilder()
+ .maximumSize(CACHE_SIZE)
+ .build(new CacheLoader<FileOffset, @NonNull Iterable<Addr2lineInfo>>() {
+ @Override
+ public @NonNull Iterable<Addr2lineInfo> load(FileOffset fo) {
+ LOGGER.fine(() -> "[FileOffsetMapper:CacheMiss] file/offset=" + fo.toString()); //$NON-NLS-1$
+ return callAddr2line(fo);
+ }
+ }));
+ }
+
+ private static class Addr2lineInfo {
+
+ private final @Nullable String fSourceFileName;
+ private final @Nullable Long fSourceLineNumber;
+ private final @Nullable String fFunctionName;
+
+ public Addr2lineInfo(@Nullable String sourceFileName, @Nullable String functionName, @Nullable Long sourceLineNumber) {
+ fSourceFileName = sourceFileName;
+ fSourceLineNumber = sourceLineNumber;
+ fFunctionName = functionName;
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("fSourceFileName", fSourceFileName) //$NON-NLS-1$
+ .add("fSourceLineNumber", fSourceLineNumber) //$NON-NLS-1$
+ .add("fFunctionName", fFunctionName) //$NON-NLS-1$
+ .toString();
+ }
+ }
+
+ private static @Nullable Iterable<Addr2lineInfo> getAddr2lineInfo(File file, @Nullable String buildId, long offset) {
+ LOGGER.finer(() -> String.format("[FileOffsetMapper:Addr2lineRequest] file=%s, buildId=%s, offset=0x%h", //$NON-NLS-1$
+ file.toString(), buildId, offset));
+