tools/reorder/grope.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6
michael@0 7 A program that computes a function ordering for an executable based
michael@0 8 on runtime profile information.
michael@0 9
michael@0 10 This program is directly based on work done by Roger Chickering
michael@0 11 <rogc@netscape.com> in
michael@0 12
michael@0 13 <http://bugzilla.mozilla.org/show_bug.cgi?id=65845>
michael@0 14
michael@0 15 to implement Nat Friedman's <nat@nat.org> `grope',
michael@0 16
michael@0 17 _GNU Rope - A Subroutine Position Optimizer_
michael@0 18 <http://www.hungry.com/~shaver/grope/grope.ps>
michael@0 19
michael@0 20 Specifically, it implements the procedure re-ordering algorithm
michael@0 21 described in:
michael@0 22
michael@0 23 K. Pettis and R. Hansen. ``Profile-Guided Core Position.'' In
michael@0 24 _Proceedings of the Int. Conference on Programming Language Design
michael@0 25 and Implementation_, pages 16-27, June 1990.
michael@0 26
michael@0 27 */
michael@0 28
michael@0 29 #include <assert.h>
michael@0 30 #include <fstream>
michael@0 31 #include <hash_set>
michael@0 32 #include <map>
michael@0 33 #include <set>
michael@0 34 #include <list>
michael@0 35 #include <vector>
michael@0 36 #include <limits.h>
michael@0 37 #include <unistd.h>
michael@0 38 #include <stdio.h>
michael@0 39 #include <fcntl.h>
michael@0 40
michael@0 41 #include "elf_symbol_table.h"
michael@0 42
michael@0 43 #define _GNU_SOURCE
michael@0 44 #include <getopt.h>
michael@0 45
michael@0 46 elf_symbol_table symtab;
michael@0 47
michael@0 48 // Straight outta plhash.c!
michael@0 49 #define GOLDEN_RATIO 0x9E3779B9U
michael@0 50
michael@0 51 struct hash<const Elf32_Sym *>
michael@0 52 {
michael@0 53 size_t operator()(const Elf32_Sym *sym) const {
michael@0 54 return (reinterpret_cast<size_t>(sym) >> 4) * GOLDEN_RATIO; }
michael@0 55 };
michael@0 56
michael@0 57 typedef unsigned int call_count_t;
michael@0 58
michael@0 59 struct call_graph_arc
michael@0 60 {
michael@0 61 const Elf32_Sym *m_from;
michael@0 62 const Elf32_Sym *m_to;
michael@0 63 call_count_t m_count;
michael@0 64
michael@0 65 call_graph_arc(const Elf32_Sym *left, const Elf32_Sym *right, call_count_t count = 0)
michael@0 66 : m_count(count)
michael@0 67 {
michael@0 68 assert(left != right);
michael@0 69
michael@0 70 if (left > right) {
michael@0 71 m_from = left;
michael@0 72 m_to = right;
michael@0 73 }
michael@0 74 else {
michael@0 75 m_from = right;
michael@0 76 m_to = left;
michael@0 77 }
michael@0 78 }
michael@0 79
michael@0 80 friend bool
michael@0 81 operator==(const call_graph_arc &lhs, const call_graph_arc &rhs)
michael@0 82 {
michael@0 83 return (lhs.m_from == rhs.m_from) && (lhs.m_to == rhs.m_to);
michael@0 84 }
michael@0 85
michael@0 86 friend ostream &
michael@0 87 operator<<(ostream &out, const call_graph_arc &arc)
michael@0 88 {
michael@0 89 out << &arc << ": ";
michael@0 90 out.form("<(%s %s) %d>",
michael@0 91 symtab.get_symbol_name(arc.m_from),
michael@0 92 symtab.get_symbol_name(arc.m_to),
michael@0 93 arc.m_count);
michael@0 94
michael@0 95 return out;
michael@0 96 }
michael@0 97 };
michael@0 98
michael@0 99 struct hash<call_graph_arc *>
michael@0 100 {
michael@0 101 size_t
michael@0 102 operator()(const call_graph_arc* arc) const
michael@0 103 {
michael@0 104 size_t result;
michael@0 105 result = reinterpret_cast<unsigned long>(arc->m_from);
michael@0 106 result ^= reinterpret_cast<unsigned long>(arc->m_to) >> 16;
michael@0 107 result ^= reinterpret_cast<unsigned long>(arc->m_to) << 16;
michael@0 108 result *= GOLDEN_RATIO;
michael@0 109 return result;
michael@0 110 }
michael@0 111 };
michael@0 112
michael@0 113 struct equal_to<call_graph_arc *>
michael@0 114 {
michael@0 115 bool
michael@0 116 operator()(const call_graph_arc* lhs, const call_graph_arc *rhs) const
michael@0 117 {
michael@0 118 return *lhs == *rhs;
michael@0 119 }
michael@0 120 };
michael@0 121
michael@0 122 typedef hash_set<call_graph_arc *> arc_container_t;
michael@0 123 arc_container_t arcs;
michael@0 124
michael@0 125 typedef multimap<const Elf32_Sym *, call_graph_arc *> arc_map_t;
michael@0 126 arc_map_t from_map;
michael@0 127 arc_map_t to_map;
michael@0 128
michael@0 129 struct less_call_graph_arc_count
michael@0 130 {
michael@0 131 bool
michael@0 132 operator()(const call_graph_arc *lhs, const call_graph_arc *rhs) const
michael@0 133 {
michael@0 134 if (lhs->m_count == rhs->m_count) {
michael@0 135 if (lhs->m_from == rhs->m_from)
michael@0 136 return lhs->m_to > rhs->m_to;
michael@0 137
michael@0 138 return lhs->m_from > rhs->m_from;
michael@0 139 }
michael@0 140 return lhs->m_count > rhs->m_count;
michael@0 141 }
michael@0 142 };
michael@0 143
michael@0 144 typedef set<call_graph_arc *, less_call_graph_arc_count> arc_count_index_t;
michael@0 145
michael@0 146 bool opt_debug = false;
michael@0 147 const char *opt_out = "order.out";
michael@0 148 int opt_tick = 0;
michael@0 149 bool opt_verbose = false;
michael@0 150 int opt_window = 16;
michael@0 151
michael@0 152 static struct option long_options[] = {
michael@0 153 { "debug", no_argument, 0, 'd' },
michael@0 154 { "exe", required_argument, 0, 'e' },
michael@0 155 { "help", no_argument, 0, '?' },
michael@0 156 { "out", required_argument, 0, 'o' },
michael@0 157 { "tick", optional_argument, 0, 't' },
michael@0 158 { "verbose", no_argument, 0, 'v' },
michael@0 159 { "window", required_argument, 0, 'w' },
michael@0 160 { 0, 0, 0, 0 }
michael@0 161 };
michael@0 162
michael@0 163 //----------------------------------------------------------------------
michael@0 164
michael@0 165 static void
michael@0 166 usage(const char *name)
michael@0 167 {
michael@0 168 cerr << "usage: " << name << " [options] [<file> ...]" << endl;
michael@0 169 cerr << " Options:" << endl;
michael@0 170 cerr << " --debug, -d" << endl;
michael@0 171 cerr << " Print lots of verbose debugging cruft." << endl;
michael@0 172 cerr << " --exe=<image>, -e <image> (required)" << endl;
michael@0 173 cerr << " Specify the executable image from which to read symbol information." << endl;
michael@0 174 cerr << " --help, -?" << endl;
michael@0 175 cerr << " Print this message and exit." << endl;
michael@0 176 cerr << " --out=<file>, -o <file>" << endl;
michael@0 177 cerr << " Specify the output file to which to dump the symbol ordering of the" << endl;
michael@0 178 cerr << " best individual (default is `order.out')." << endl;
michael@0 179 cerr << " --tick[=<num>], -t [<num>]" << endl;
michael@0 180 cerr << " When reading address data, print a dot to stderr every <num>th" << endl;
michael@0 181 cerr << " address processed from the call trace. If specified with no argument," << endl;
michael@0 182 cerr << " a dot will be printed for every million addresses processed." << endl;
michael@0 183 cerr << " --verbose, -v" << endl;
michael@0 184 cerr << " Issue progress messages to stderr." << endl;
michael@0 185 cerr << " --window=<num>, -w <num>" << endl;
michael@0 186 cerr << " Use a sliding window instead of pagination to score orderings." << endl;
michael@0 187 }
michael@0 188
michael@0 189 /**
michael@0 190 * Using the symbol table, map a stream of address references into a
michael@0 191 * callgraph.
michael@0 192 */
michael@0 193 static void
michael@0 194 map_addrs(int fd)
michael@0 195 {
michael@0 196 long long total_calls = 0;
michael@0 197 typedef list<const Elf32_Sym *> window_t;
michael@0 198
michael@0 199 window_t window;
michael@0 200 int window_size = 0;
michael@0 201 unsigned int buf[128];
michael@0 202 ssize_t cb;
michael@0 203
michael@0 204 while ((cb = read(fd, buf, sizeof buf)) > 0) {
michael@0 205 if (cb % sizeof buf[0])
michael@0 206 fprintf(stderr, "unaligned read\n");
michael@0 207
michael@0 208 unsigned int *addr = buf;
michael@0 209 unsigned int *limit = buf + (cb / 4);
michael@0 210
michael@0 211 for (; addr < limit; ++addr) {
michael@0 212 const Elf32_Sym *sym = symtab.lookup(*addr);
michael@0 213 if (! sym)
michael@0 214 continue;
michael@0 215
michael@0 216 ++total_calls;
michael@0 217
michael@0 218 window.push_front(sym);
michael@0 219
michael@0 220 if (window_size >= opt_window)
michael@0 221 window.pop_back();
michael@0 222 else
michael@0 223 ++window_size;
michael@0 224
michael@0 225 window_t::const_iterator i = window.begin();
michael@0 226 window_t::const_iterator end = window.end();
michael@0 227 for (; i != end; ++i) {
michael@0 228 if (sym != *i) {
michael@0 229 call_graph_arc *arc;
michael@0 230 call_graph_arc key(sym, *i);
michael@0 231 arc_container_t::iterator iter = arcs.find(&key);
michael@0 232 if (iter == arcs.end()) {
michael@0 233 arc = new call_graph_arc(sym, *i);
michael@0 234 arcs.insert(arc);
michael@0 235 from_map.insert(arc_map_t::value_type(arc->m_from, arc));
michael@0 236 to_map.insert(arc_map_t::value_type(arc->m_to, arc));
michael@0 237 }
michael@0 238 else
michael@0 239 arc = const_cast<call_graph_arc *>(*iter);
michael@0 240
michael@0 241 ++(arc->m_count);
michael@0 242 }
michael@0 243 }
michael@0 244
michael@0 245 if (opt_verbose && opt_tick && (total_calls % opt_tick == 0)) {
michael@0 246 cerr << ".";
michael@0 247 flush(cerr);
michael@0 248 }
michael@0 249 }
michael@0 250 }
michael@0 251
michael@0 252 if (opt_verbose) {
michael@0 253 if (opt_tick)
michael@0 254 cerr << endl;
michael@0 255
michael@0 256 cerr << "Total calls: " << total_calls << endl;
michael@0 257 }
michael@0 258 }
michael@0 259
michael@0 260 static void
michael@0 261 remove_from(arc_map_t& map, const Elf32_Sym *sym, const call_graph_arc *arc)
michael@0 262 {
michael@0 263 pair<arc_map_t::iterator, arc_map_t::iterator> p
michael@0 264 = map.equal_range(sym);
michael@0 265
michael@0 266 for (arc_map_t::iterator i = p.first; i != p.second; ++i) {
michael@0 267 if (i->second == arc) {
michael@0 268 map.erase(i);
michael@0 269 break;
michael@0 270 }
michael@0 271 }
michael@0 272 }
michael@0 273
michael@0 274 /**
michael@0 275 * The main program
michael@0 276 */
michael@0 277 int
michael@0 278 main(int argc, char *argv[])
michael@0 279 {
michael@0 280 const char *opt_exe = 0;
michael@0 281
michael@0 282 int c;
michael@0 283 while (1) {
michael@0 284 int option_index = 0;
michael@0 285 c = getopt_long(argc, argv, "?de:o:t:vw:", long_options, &option_index);
michael@0 286
michael@0 287 if (c < 0)
michael@0 288 break;
michael@0 289
michael@0 290 switch (c) {
michael@0 291 case '?':
michael@0 292 usage(argv[0]);
michael@0 293 return 0;
michael@0 294
michael@0 295 case 'd':
michael@0 296 opt_debug = true;
michael@0 297 break;
michael@0 298
michael@0 299 case 'e':
michael@0 300 opt_exe = optarg;
michael@0 301 break;
michael@0 302
michael@0 303 case 'o':
michael@0 304 opt_out = optarg;
michael@0 305 break;
michael@0 306
michael@0 307 case 't':
michael@0 308 opt_tick = optarg ? atoi(optarg) : 1000000;
michael@0 309 break;
michael@0 310
michael@0 311 case 'v':
michael@0 312 opt_verbose = true;
michael@0 313 break;
michael@0 314
michael@0 315 case 'w':
michael@0 316 opt_window = atoi(optarg);
michael@0 317 if (opt_window <= 0) {
michael@0 318 cerr << "invalid window size: " << opt_window << endl;
michael@0 319 return 1;
michael@0 320 }
michael@0 321 break;
michael@0 322
michael@0 323 default:
michael@0 324 usage(argv[0]);
michael@0 325 return 1;
michael@0 326 }
michael@0 327 }
michael@0 328
michael@0 329 // Make sure an image was specified
michael@0 330 if (! opt_exe) {
michael@0 331 usage(argv[0]);
michael@0 332 return 1;
michael@0 333 }
michael@0 334
michael@0 335 // Read the sym table.
michael@0 336 symtab.init(opt_exe);
michael@0 337
michael@0 338 // Process addresses to construct the weighted call graph.
michael@0 339 if (optind >= argc) {
michael@0 340 map_addrs(STDIN_FILENO);
michael@0 341 }
michael@0 342 else {
michael@0 343 do {
michael@0 344 int fd = open(argv[optind], O_RDONLY);
michael@0 345 if (fd < 0) {
michael@0 346 perror(argv[optind]);
michael@0 347 return 1;
michael@0 348 }
michael@0 349
michael@0 350 map_addrs(fd);
michael@0 351 close(fd);
michael@0 352 } while (++optind < argc);
michael@0 353 }
michael@0 354
michael@0 355 // Emit the ordering.
michael@0 356 ofstream out(opt_out);
michael@0 357
michael@0 358 // Collect all of the arcs into a sorted container, with arcs
michael@0 359 // having the largest weight at the front.
michael@0 360 arc_count_index_t sorted_arcs(arcs.begin(), arcs.end());
michael@0 361
michael@0 362 while (sorted_arcs.size()) {
michael@0 363 if (opt_debug) {
michael@0 364 cerr << "==========================================" << endl << endl;
michael@0 365
michael@0 366 cerr << "Sorted Arcs:" << endl;
michael@0 367 for (arc_count_index_t::const_iterator iter = sorted_arcs.begin();
michael@0 368 iter != sorted_arcs.end();
michael@0 369 ++iter) {
michael@0 370 cerr << **iter << endl;
michael@0 371 }
michael@0 372
michael@0 373 cerr << endl << "Arc Container:" << endl;
michael@0 374 for (arc_container_t::const_iterator iter = arcs.begin();
michael@0 375 iter != arcs.end();
michael@0 376 ++iter) {
michael@0 377 cerr << **iter << endl;
michael@0 378 }
michael@0 379
michael@0 380 cerr << endl << "From Map:" << endl;
michael@0 381 for (arc_map_t::const_iterator iter = from_map.begin();
michael@0 382 iter != from_map.end();
michael@0 383 ++iter) {
michael@0 384 cerr << symtab.get_symbol_name(iter->first) << ": " << *(iter->second) << endl;
michael@0 385 }
michael@0 386
michael@0 387 cerr << endl << "To Map:" << endl;
michael@0 388 for (arc_map_t::const_iterator iter = to_map.begin();
michael@0 389 iter != to_map.end();
michael@0 390 ++iter) {
michael@0 391 cerr << symtab.get_symbol_name(iter->first) << ": " << *(iter->second) << endl;
michael@0 392 }
michael@0 393
michael@0 394 cerr << endl;
michael@0 395 }
michael@0 396
michael@0 397 // The first arc in the sorted set will have the largest
michael@0 398 // weight. Pull it out, and emit its sink.
michael@0 399 arc_count_index_t::iterator max = sorted_arcs.begin();
michael@0 400 call_graph_arc *arc = const_cast<call_graph_arc *>(*max);
michael@0 401
michael@0 402 sorted_arcs.erase(max);
michael@0 403
michael@0 404 if (opt_debug)
michael@0 405 cerr << "pulling " << *arc << endl;
michael@0 406
michael@0 407 arcs.erase(arc);
michael@0 408 remove_from(from_map, arc->m_from, arc);
michael@0 409 remove_from(to_map, arc->m_to, arc);
michael@0 410
michael@0 411 out << symtab.get_symbol_name(arc->m_to) << endl;
michael@0 412
michael@0 413 // Merge arc->m_to into arc->m_from. First, modify anything
michael@0 414 // that used to point to arc->m_to to point to arc->m_from.
michael@0 415 typedef list<call_graph_arc *> arc_list_t;
michael@0 416 arc_list_t map_add_queue;
michael@0 417
michael@0 418 pair<arc_map_t::iterator, arc_map_t::iterator> p;
michael@0 419
michael@0 420 // Find all the arcs that point to arc->m_to.
michael@0 421 p = to_map.equal_range(arc->m_to);
michael@0 422 for (arc_map_t::iterator i = p.first; i != p.second; ++i) {
michael@0 423 // Remove the arc that points to arc->m_to (`doomed') from
michael@0 424 // all of our indicies.
michael@0 425 call_graph_arc *doomed = i->second;
michael@0 426 const Elf32_Sym *source = doomed->m_from;
michael@0 427
michael@0 428 sorted_arcs.erase(doomed);
michael@0 429 arcs.erase(doomed);
michael@0 430 remove_from(from_map, source, doomed);
michael@0 431 // N.B. that `doomed' will be removed from the `to_map'
michael@0 432 // after the loop completes.
michael@0 433
michael@0 434 // Create a new (or locate an existing) arc whose source
michael@0 435 // was the doomed arc's source, and whose sink is
michael@0 436 // arc->m_from (i.e., the node that subsumed arc->m_to).
michael@0 437 call_graph_arc *merge;
michael@0 438 call_graph_arc key = call_graph_arc(source, arc->m_from);
michael@0 439 arc_container_t::iterator iter = arcs.find(&key);
michael@0 440
michael@0 441 if (iter == arcs.end()) {
michael@0 442 merge = new call_graph_arc(source, arc->m_from);
michael@0 443
michael@0 444 arcs.insert(merge);
michael@0 445 from_map.insert(arc_map_t::value_type(merge->m_from, merge));
michael@0 446 map_add_queue.push_back(merge);
michael@0 447 }
michael@0 448 else {
michael@0 449 // We found an existing arc: temporarily remove it
michael@0 450 // from the sorted index.
michael@0 451 merge = const_cast<call_graph_arc *>(*iter);
michael@0 452 sorted_arcs.erase(merge);
michael@0 453 }
michael@0 454
michael@0 455 // Include the doomed arc's weight in the merged arc, and
michael@0 456 // (re-)insert it into the sorted index.
michael@0 457 merge->m_count += doomed->m_count;
michael@0 458 sorted_arcs.insert(merge);
michael@0 459
michael@0 460 delete doomed;
michael@0 461 }
michael@0 462
michael@0 463 to_map.erase(p.first, p.second);
michael@0 464
michael@0 465 for (arc_list_t::iterator merge = map_add_queue.begin();
michael@0 466 merge != map_add_queue.end();
michael@0 467 map_add_queue.erase(merge++)) {
michael@0 468 to_map.insert(arc_map_t::value_type((*merge)->m_to, *merge));
michael@0 469 }
michael@0 470
michael@0 471 // Now, roll anything that arc->m_to used to point at into
michael@0 472 // what arc->m_from now points at.
michael@0 473
michael@0 474 // Collect all of the arcs that originate at arc->m_to.
michael@0 475 p = from_map.equal_range(arc->m_to);
michael@0 476 for (arc_map_t::iterator i = p.first; i != p.second; ++i) {
michael@0 477 // Remove the arc originating from arc->m_to (`doomed')
michael@0 478 // from all of our indicies.
michael@0 479 call_graph_arc *doomed = i->second;
michael@0 480 const Elf32_Sym *sink = doomed->m_to;
michael@0 481
michael@0 482 // There shouldn't be any self-referential arcs.
michael@0 483 assert(sink != arc->m_to);
michael@0 484
michael@0 485 sorted_arcs.erase(doomed);
michael@0 486 arcs.erase(doomed);
michael@0 487 remove_from(to_map, sink, doomed);
michael@0 488 // N.B. that `doomed' will be removed from the `from_map'
michael@0 489 // once the loop completes.
michael@0 490
michael@0 491 // Create a new (or locate an existing) arc whose source
michael@0 492 // is arc->m_from and whose sink was the removed arc's
michael@0 493 // sink.
michael@0 494 call_graph_arc *merge;
michael@0 495 call_graph_arc key = call_graph_arc(arc->m_from, sink);
michael@0 496 arc_container_t::iterator iter = arcs.find(&key);
michael@0 497
michael@0 498 if (iter == arcs.end()) {
michael@0 499 merge = new call_graph_arc(arc->m_from, sink);
michael@0 500
michael@0 501 arcs.insert(merge);
michael@0 502
michael@0 503 map_add_queue.push_back(merge);
michael@0 504 to_map.insert(arc_map_t::value_type(merge->m_to, merge));
michael@0 505 }
michael@0 506 else {
michael@0 507 // We found an existing arc: temporarily remove it
michael@0 508 // from the sorted index.
michael@0 509 merge = const_cast<call_graph_arc *>(*iter);
michael@0 510 sorted_arcs.erase(merge);
michael@0 511 }
michael@0 512
michael@0 513 // Include the doomed arc's weight in the merged arc, and
michael@0 514 // (re-)insert it into the sorted index.
michael@0 515 merge->m_count += doomed->m_count;
michael@0 516 sorted_arcs.insert(merge);
michael@0 517
michael@0 518 delete doomed;
michael@0 519 }
michael@0 520
michael@0 521 from_map.erase(p.first, p.second);
michael@0 522
michael@0 523 for (arc_list_t::iterator merge = map_add_queue.begin();
michael@0 524 merge != map_add_queue.end();
michael@0 525 map_add_queue.erase(merge++)) {
michael@0 526 from_map.insert(arc_map_t::value_type((*merge)->m_from, *merge));
michael@0 527 }
michael@0 528 }
michael@0 529
michael@0 530 out.close();
michael@0 531 return 0;
michael@0 532 }

mercurial