Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Unified Diff: tools/binary_size/java/src/org/chromium/tools/binary_size/ParallelAddress2Line.java

Issue 258633003: Graphical version of the run_binary_size_analysis tool. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Made the code fully pylint clean. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/binary_size/java/src/org/chromium/tools/binary_size/ParallelAddress2Line.java
diff --git a/tools/binary_size/java/src/org/chromium/tools/binary_size/ParallelAddress2Line.java b/tools/binary_size/java/src/org/chromium/tools/binary_size/ParallelAddress2Line.java
deleted file mode 100644
index 8e49c43cfaf5890df67923e752a6018641372a8e..0000000000000000000000000000000000000000
--- a/tools/binary_size/java/src/org/chromium/tools/binary_size/ParallelAddress2Line.java
+++ /dev/null
@@ -1,526 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-package org.chromium.tools.binary_size;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * A tool for parallelizing "addr2line" against a given binary.
- * The tool runs "nm" to dump the symbols from a library, then spawns a pool
- * of addr2name workers that resolve addresses to name in parallel.
- * <p>
- * This tool is intentionally written to be standalone so that it can be
- * compiled without reliance upon any other libraries. All that is required
- * is a vanilla installation of the Java Runtime Environment, 1.5 or later.
- */
-// TODO(andrewhayden): Rewrite entire tool in Python
-public class ParallelAddress2Line {
- private final AtomicBoolean mStillEnqueuing = new AtomicBoolean(true);
- private final AtomicInteger mEnqueuedCount =
- new AtomicInteger(Integer.MAX_VALUE);
- private final AtomicInteger mDoneCount = new AtomicInteger(0);
- private final AtomicInteger mSuccessCount = new AtomicInteger(0);
- private final AtomicInteger mAddressSkipCount = new AtomicInteger(0);
- private final String mLibraryPath;
- private final String mNmPath;
- private final String mNmInPath;
- private final String mAddr2linePath;
- private final boolean mVerbose;
- private final boolean mNoProgress;
- private Addr2LineWorkerPool mPool;
- private final boolean mNoDedupe;
- private final boolean mDisambiguate;
- private final NmDumper mNmDumper;
-
- private static final String USAGE =
- "--addr2line [ARG] instead of the 'addr2line' in $PATH, use this (e.g.,\n" +
- " arch-specific binary) (optional)\n" +
- "--disambiguate create a listing of all source files which can be used\n" +
- " to disambiguate some percentage of ambiguous source\n" +
- " references; only useful on some architectures and adds\n" +
- " significant startup cost (optional)\n" +
- "--failfile [ARG] output symbols from failed lookups to the specified\n" +
- " file (optional)\n" +
- "--library [ARG] path to the library to process, e.g.\n" +
- " out/Release/lib/libchromeview.so (required)\n" +
- "--nm [ARG] instead of the 'nm' in $PATH, use this (e.g.,\n" +
- " arch-specific binary) (optional)\n" +
- "--nm-infile [ARG] instead of running nm on the specified library,\n" +
- " ingest the specified nm file. (optional)\n" +
- "--no-dedupe don't de-dupe symbols that live at the same address;\n" +
- " deduping more accurately describes the use of space\n" +
- " within the binary; if spatial analysis is your goal,\n" +
- " leave deduplication on. (optional)\n" +
- "--no-progress don't output periodic progress reports (optional)\n" +
- "--outfile [ARG] output results into the specified file (required)\n" +
- "--skipfile [ARG] output skipped symbols to the specified file (optional)\n" +
- "--threads [ARG] number of parallel worker threads to create. Start low\n" +
- " and watch your memory, defaults to 1 (optional)\n" +
- "--verbose be verbose (optional)\n";
-
- // Regex for parsing "nm" output. A sample line looks like this:
- // 0167b39c 00000018 t ACCESS_DESCRIPTION_free /path/file.c:95
- //
- // The fields are: address, size, type, name, source location
- // Regular expression explained ( see also: https://xkcd.com/208 ):
- // ([0-9a-f]{8}+) The address
- // [\\s]+ Whitespace separator
- // ([0-9a-f]{8}+) The size. From here on out it's all optional.
- // [\\s]+ Whitespace separator
- // (\\S?) The symbol type, which is any non-whitespace char
- // [\\s*] Whitespace separator
- // ([^\\t]*) Symbol name, any non-tab character (spaces ok!)
- // [\\t]? Tab separator
- // (.*) The location (filename[:linennum|?][ (discriminator n)]
- private static final Pattern sNmPattern = Pattern.compile(
- "([0-9a-f]{8}+)[\\s]+([0-9a-f]{8}+)[\\s]*(\\S?)[\\s*]([^\\t]*)[\\t]?(.*)");
-
- private ParallelAddress2Line(
- final String libraryPath,
- final String nmPath,
- final String nmInPath,
- final String addr2linePath,
- final String outPath,
- final String skipPath,
- final String failPath,
- final boolean verbose,
- final boolean noProgress,
- final boolean noDedupe,
- final boolean disambiguate) {
- this.mLibraryPath = libraryPath;
- this.mNmPath = nmPath;
- this.mNmInPath = nmInPath;
- this.mAddr2linePath = addr2linePath;
- this.mVerbose = verbose;
- this.mNoProgress = noProgress;
- this.mNoDedupe = noDedupe;
- this.mDisambiguate = disambiguate;
- this.mNmDumper = new NmDumper(outPath, failPath, skipPath);
-
- final File libraryFile = new File(libraryPath);
- if (!(libraryFile.exists() && libraryFile.canRead())) {
- throw new IllegalStateException("Can't read library file: " + libraryPath);
- }
- }
-
- private static final File findFile(File directory, String target) {
- for (File file : directory.listFiles()) {
- if (file.isDirectory() && file.canRead()) {
- File result = findFile(file, target);
- if (result != null) return result;
- } else {
- if (target.equals(file.getName())) return file;
- }
- }
- return null;
- }
-
- private void run(final int addr2linePoolSize) throws InterruptedException {
- try {
- runInternal(addr2linePoolSize);
- } finally {
- mNmDumper.close();
- }
- }
-
- private void runInternal(final int addr2linePoolSize) throws InterruptedException {
- // Step 1: Dump symbols with nm
- final String nmOutputPath;
- if (mNmInPath == null) {
- // Generate nm output with nm binary
- logVerbose("Running nm to dump symbols from " + mLibraryPath);
- try {
- nmOutputPath = dumpSymbols();
- } catch (Exception e) {
- throw new RuntimeException("nm failed", e);
- }
- } else {
- // Use user-supplied nm output
- logVerbose("Using user-supplied nm file: " + mNmInPath);
- nmOutputPath = mNmInPath;
- }
-
- // Step 2: Prepare addr2line worker pool to process nm output
- try {
- logVerbose("Creating " + addr2linePoolSize + " workers for " + mAddr2linePath);
- mPool = new Addr2LineWorkerPool(addr2linePoolSize,
- mAddr2linePath, mLibraryPath, mDisambiguate, !mNoDedupe);
- } catch (IOException e) {
- throw new RuntimeException("Couldn't initialize name2address pool!", e);
- }
-
- // Step 3: Spool symbol-processing tasks to workers
- final long startTime = System.currentTimeMillis();
- Timer timer = null;
- if (!mNoProgress) {
- timer = startTaskMonitor(startTime);
- }
- final int queued = spoolTasks(nmOutputPath);
-
- // All tasks have been enqueued.
- mEnqueuedCount.set(queued);
- mStillEnqueuing.set(false);
- mPool.allRecordsSubmitted();
- float percentAddressesSkipped = 100f * (mAddressSkipCount.floatValue()
- / (queued + mAddressSkipCount.get()));
- float percentAddressesQueued = 100f - percentAddressesSkipped;
- int totalAddresses = mAddressSkipCount.get() + queued;
- logVerbose("All addresses have been enqueued (total " + queued + ").");
- // Remember that the queue to which the addresses was enqueued is of a
- // small fixed size; by the time this code executes, there is very
- // little work left to do. Await the termination of the pool with a
- // reasonable timeout for safety purposes.
- boolean timedOut = !mPool.await(5, TimeUnit.MINUTES);
- if (timedOut) {
- throw new RuntimeException("Worker pool did not terminate!");
- }
- if (!mNoProgress) timer.cancel();
- log(totalAddresses + " addresses discovered; " +
- queued + " queued for processing (" +
- String.format("%.2f", percentAddressesQueued) + "%), " +
- mAddressSkipCount.get() + " skipped (" +
- String.format("%.2f", percentAddressesSkipped) + "%)");
- dumpStats(startTime);
- log("Done.");
- }
-
- /**
- * Monitors the pool periodically printing status updates to stdout.
- * @param addressProcessingStartTime the time address processing began
- * @return the daemon timer that is generating the status updates
- */
- private final Timer startTaskMonitor(
- final long addressProcessingStartTime) {
- Runnable monitorTask = new OutputSpooler();
- Thread monitor = new Thread(monitorTask, "progress monitor");
- monitor.setDaemon(true);
- monitor.start();
-
- TimerTask task = new TimerTask() {
- @Override
- public void run() {
- dumpStats(addressProcessingStartTime);
- }
- };
- Timer timer = new Timer(true);
- timer.schedule(task, 1000L, 1000L);
- return timer;
- }
-
- /**
- * Spools address-lookup tasks to the addr2line workers.
- * This method will block until most of (or possibly all of) the tasks
- * have been spooled.
- * If a skip path is set, any line in the input file that doesn't have
- * an address will be copied into the skip file.
- *
- * @param inputPath the path to the dump produced by nm
- * @return the number of tasks spooled
- */
- private final int spoolTasks(final String inputPath) {
- FileReader inputReader = null;
- try {
- inputReader = new FileReader(inputPath);
- } catch (IOException e) {
- throw new RuntimeException("Can't open input file: " + inputPath, e);
- }
- final BufferedReader bufferedReader = new BufferedReader(inputReader);
-
- String currentLine = null;
- int numSpooled = 0;
- try {
- while ((currentLine = bufferedReader.readLine()) != null) {
- try {
- final Matcher matcher = sNmPattern.matcher(currentLine);
- if (!matcher.matches()) {
- // HACK: Special case for ICU data.
- // This thing is HUGE (5+ megabytes) and is currently
- // missed because there is no size information.
- // torne@ has volunteered to patch the generation code
- // so that the generated ASM includes a size attribute
- // so that this hard-coding can go away in the future.
- if (currentLine.endsWith("icudt46_dat")) {
- Record record = getIcuRecord(currentLine);
- if (record != null) {
- numSpooled++;
- mPool.submit(record);
- continue;
- }
- }
- mNmDumper.skipped(currentLine);
- mAddressSkipCount.incrementAndGet();
- continue;
- }
- final Record record = new Record();
- record.address = matcher.group(1);
- record.size = matcher.group(2);
- if (matcher.groupCount() >= 3) {
- record.symbolType = matcher.group(3).charAt(0);
- }
- if (matcher.groupCount() >= 4) {
- // May or may not be present
- record.symbolName = matcher.group(4);
- }
- numSpooled++;
- mPool.submit(record);
- } catch (Exception e) {
- throw new RuntimeException("Error processing line: '" + currentLine + "'", e);
- }
- }
- } catch (Exception e) {
- throw new RuntimeException("Input processing failed", e);
- } finally {
- try {
- bufferedReader.close();
- } catch (Exception ignored) {
- // Nothing to be done
- }
- try {
- inputReader.close();
- } catch (Exception ignored) {
- // Nothing to be done
- }
- }
- return numSpooled;
- }
-
- private Record getIcuRecord(String line) throws IOException {
- // Line looks like this:
- // 01c9ee00 r icudt46_dat
- String[] parts = line.split("\\s");
- if (parts.length != 3) return null;
-
- // Convert /src/out/Release/lib/[libraryfile] -> /src/out/Release
- final File libraryOutputDirectory = new File(mLibraryPath)
- .getParentFile().getParentFile().getCanonicalFile();
- final File icuDir = new File(
- libraryOutputDirectory.getAbsolutePath() +
- "/obj/third_party/icu");
- final File icuFile = findFile(icuDir, "icudata.icudt46l_dat.o");
- if (!icuFile.exists()) return null;
- final Record record = new Record();
- record.address = parts[0];
- record.symbolType = parts[1].charAt(0);
- record.symbolName = parts[2];
- record.size = Integer.toHexString((int) icuFile.length());
- record.location = icuFile.getCanonicalPath() + ":0";
- record.resolvedSuccessfully = true;
- while (record.size.length() < 8) {
- record.size = "0" + record.size;
- }
- return record;
- }
-
- /**
- * @return the path to the file that nm wrote
- * @throws Exception
- * @throws FileNotFoundException
- * @throws InterruptedException
- */
- private String dumpSymbols() throws Exception, FileNotFoundException, InterruptedException {
- final Process process = createNmProcess();
- final File tempFile = File.createTempFile("ParallelAddress2Line", "nm");
- tempFile.deleteOnExit();
- final CountDownLatch completionLatch = sink(
- process.getInputStream(), new FileOutputStream(tempFile), true);
- sink(process.getErrorStream(), System.err, false);
- logVerbose("Dumping symbols to: " + tempFile.getAbsolutePath());
- final int nmRc = process.waitFor();
- if (nmRc != 0) {
- throw new RuntimeException("nm process returned " + nmRc);
- }
- completionLatch.await(); // wait for output to be done
- return tempFile.getAbsolutePath();
- }
-
- private void dumpStats(final long startTime) {
- long successful = mSuccessCount.get();
- long doneNow = mDoneCount.get();
- long unsuccessful = doneNow - successful;
- float successPercent = doneNow == 0 ? 100f : 100f * ((float)successful / (float)doneNow);
- long elapsedMillis = System.currentTimeMillis() - startTime;
- float elapsedSeconds = elapsedMillis / 1000f;
- long throughput = doneNow / (elapsedMillis / 1000);
- final int mapLookupSuccess = mPool.getDisambiguationSuccessCount();
- final int mapLookupFailure = mPool.getDisambiguationFailureCount();
- final int mapLookupTotal = mapLookupSuccess + mapLookupFailure;
- float mapLookupSuccessPercent = 0f;
- if (mapLookupTotal != 0 && mapLookupSuccess != 0) {
- mapLookupSuccessPercent = 100f *
- ((float) mapLookupSuccess / (float) mapLookupTotal);
- }
-
- log(doneNow + " addresses processed (" +
- mSuccessCount.get() + " ok, " + unsuccessful + " failed)" +
- ", avg " + throughput + " addresses/sec, " +
- String.format("%.2f", successPercent) + "% success" +
- ", " + mapLookupTotal + " ambiguous path" +
- (!mDisambiguate ? "" :
- ", (" + String.format("%.2f", mapLookupSuccessPercent) + "% disambiguated)") +
- (mNoDedupe ? "" : ", " + mPool.getDedupeCount() + " deduped") +
- ", elapsed time " + String.format("%.3f", elapsedSeconds) + " seconds");
- }
-
- private Process createNmProcess() throws Exception {
- ProcessBuilder builder = new ProcessBuilder(
- mNmPath,
- "-C", // demangle (for the humans)
- "-S", // print size
- mLibraryPath);
- logVerbose("Creating process: " + builder.command());
- return builder.start();
- }
-
- /**
- * Make a pipe to drain the specified input stream into the specified
- * output stream asynchronously.
- * @param in read from here
- * @param out and write to here
- * @param closeWhenDone whether or not to close the target output stream
- * when the input stream terminates
- * @return a latch that can be used to await the final write to the
- * output stream, which occurs when either of the streams closes
- */
- private static final CountDownLatch sink(final InputStream in,
- final OutputStream out, final boolean closeWhenDone) {
- final CountDownLatch latch = new CountDownLatch(1);
- final Runnable task = new Runnable() {
- @Override
- public void run() {
- byte[] buffer = new byte[4096];
- try {
- int numRead = 0;
- do {
- numRead = in.read(buffer);
- if (numRead > 0) {
- out.write(buffer, 0, numRead);
- out.flush();
- }
- } while (numRead >= 0);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try { out.flush(); } catch (Exception ignored) {
- // Nothing to be done
- }
- if (closeWhenDone) {
- try { out.close(); } catch (Exception ignored) {
- // Nothing to be done
- }
- }
- latch.countDown();
- }
- }
- };
- final Thread worker = new Thread(task, "pipe " + in + "->" + out);
- worker.setDaemon(true);
- worker.start();
- return latch;
- }
-
- private final class OutputSpooler implements Runnable {
- @Override
- public void run() {
- do {
- readRecord();
- } while (mStillEnqueuing.get() || (mDoneCount.get() < mEnqueuedCount.get()));
- }
-
- /**
- * Read a record and process it.
- */
- private void readRecord() {
- Record record = mPool.poll();
- if (record != null) {
- mDoneCount.incrementAndGet();
- if (record.resolvedSuccessfully) {
- mSuccessCount.incrementAndGet();
- mNmDumper.succeeded(record);
- } else {
- mNmDumper.failed(record);
- }
- } else {
- try {
- // wait to keep going
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- /**
- * Log a message to the console.
- * @param message the message to log
- */
- private final void log(String message) {
- System.out.println(message);
- }
-
- /**
- * Log a message to the console iff verbose logging is enabled.
- * @param message the message to log
- */
- private final void logVerbose(String message) {
- if (mVerbose) log(message);
- }
-
- /**
- * Runs the tool. Run with --help for limited help.
- * @param args
- * @throws Exception if anything explodes
- */
- public static void main(String[] args) throws Exception {
- ParallelAddress2Line tool = new ParallelAddress2Line(
- getArg(args, "--library"),
- getArg(args, "--nm", "nm"),
- getArg(args, "--nm-infile", null),
- getArg(args, "--addr2line", "addr2line"),
- getArg(args, "--outfile"),
- getArg(args, "--skipfile", null),
- getArg(args, "--failfile", null),
- hasFlag(args, "--verbose"),
- hasFlag(args, "--no-progress"),
- hasFlag(args, "--no-dedupe"),
- hasFlag(args, "--disambiguate"));
- tool.run(Integer.parseInt(getArg(args, "--threads", "1")));
- }
-
- private static boolean hasFlag(String[] args, String name) {
- for (int x = 0; x < args.length; x++) if (name.equals(args[x])) return true;
- return false;
- }
-
- private static String getArg(String[] args, String name, String defaultValue) {
- for (int x = 0; x < args.length; x++) {
- if (name.equals(args[x])) {
- if (x < args.length - 1) return args[x + 1];
- throw new RuntimeException(name + " is missing a value\n" + USAGE);
- }
- }
- return defaultValue;
- }
-
- private static String getArg(String[] args, String name) {
- String result = getArg(args, name, null);
- if (result == null) throw new RuntimeException(name + " is required\n" + USAGE);
- return result;
- }
-}

Powered by Google App Engine
This is Rietveld 408576698