static function loadObject($filename)
 {
     if (isset(self::$cache[$filename])) {
         return self::$cache[$filename];
     }
     $D = new PhenomDwarfLineInfo($filename);
     $D->parse();
     self::$cache[$filename] = $D;
     return $D;
 }
 public function runUnitTests()
 {
     // Build any unit tests
     $this->make('build-tests');
     // Now find all the test programs
     $root = $this->getProjectRoot();
     $futures = array();
     $cg_files = array();
     $obj_files = array();
     $coverage = $this->getEnableCoverage();
     $tests = $this->getTestsToRun();
     foreach ($tests as $relname => $test) {
         if ($coverage) {
             $cg = new TempFile();
             $cg_files[$relname] = $cg;
             $obj_files[] = $test;
             $test = 'valgrind --tool=callgrind --collect-jumps=yes ' . '--callgrind-out-file=' . $cg . ' ' . $test;
         }
         $futures[$relname] = new ExecFuture($test);
     }
     $results = array();
     // Use a smaller limit for SunOS because my test VM has crappy
     // timing characteristics and that causes timer.t to fail
     $limit = PHP_OS == 'SunOS' ? 1 : 4;
     foreach (Futures($futures)->limit($limit) as $test => $future) {
         list($err, $stdout, $stderr) = $future->resolve();
         $results[$test] = $this->parseTestResults($test, $err, $stdout, $stderr);
     }
     if ($coverage) {
         // Find all executables, not just the test executables.
         // We need to do this because the test executables may not
         // include all of the possible code (linker decides to omit
         // it from the image) so we see a skewed representation of
         // the source lines.
         $fp = popen("find {$root} -type f -name \\*.a -o -name \\*.so -o -name \\*.dylib", "r");
         while (true) {
             $line = fgets($fp);
             if ($line === false) {
                 break;
             }
             $obj_files[] = trim($line);
         }
         // Parse line information from the objects
         foreach ($obj_files as $object) {
             PhenomDwarfLineInfo::loadObject($object);
         }
         // Now process coverage data files
         foreach ($cg_files as $relname => $cg) {
             $CG = new PhenomCallgrindFile((string) $cg);
             $CG->parse();
             $source_files = array();
             foreach ($CG->getSourceFiles() as $filename) {
                 if (Filesystem::isDescendant($filename, $root) && !preg_match("/(thirdparty|tests)/", $filename)) {
                     $source_files[$filename] = $filename;
                 }
             }
             $cov = array();
             foreach ($source_files as $filename) {
                 $relsrc = substr($filename, strlen($root) + 1);
                 $cov[$relsrc] = PhenomCallgrindFile::mergeSourceLineData($filename, array($CG));
             }
             // Update the coverage data for that test.
             // arcanist will merge the final results
             $results[$relname]->setCoverage($cov);
         }
     }
     return $results;
 }
 function collectSourceFileData($sourcefile)
 {
     $all_lines = array();
     $this->getTouchedLines($sourcefile, $all_lines);
     foreach ($this->getObjectFiles() as $objfile) {
         PhenomDwarfLineInfo::loadObject($objfile);
     }
     $exe_lines = PhenomDwarfLineInfo::mergedSourceLines($sourcefile);
     $max_line = max($exe_lines);
     /* now build a string containing the coverage info.
      * This is compatible with ArcanistUnitTestResult coverage data:
      * N not executable
      * C covered
      * U uncovered
      * X unreachable (we can't detect that here)
      */
     $cov = str_repeat('N', $max_line);
     for ($i = 1; $i <= $max_line; $i++) {
         if (isset($all_lines[$i])) {
             $cov[$i - 1] = 'C';
         } else {
             if (isset($exe_lines[$i])) {
                 $cov[$i - 1] = 'U';
             }
         }
     }
     return $cov;
 }