Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 attempts to find an optimal function ordering for an |
michael@0 | 8 | executable using a genetic algorithm whose fitness function is |
michael@0 | 9 | computed using runtime profile information. |
michael@0 | 10 | |
michael@0 | 11 | The fitness function was inspired by Nat Friedman's <nat@nat.org> |
michael@0 | 12 | work on `grope': |
michael@0 | 13 | |
michael@0 | 14 | _GNU Rope - A Subroutine Position Optimizer_ |
michael@0 | 15 | <http://www.hungry.com/~shaver/grope/grope.ps> |
michael@0 | 16 | |
michael@0 | 17 | Brendan Eich <brendan@mozilla.org> told me tales about Scott Furman |
michael@0 | 18 | doing something like this, which sort of made me want to try it. |
michael@0 | 19 | |
michael@0 | 20 | As far as I can tell, it would take a lot of computers a lot of time |
michael@0 | 21 | to actually find something useful on a non-trivial program using |
michael@0 | 22 | this. |
michael@0 | 23 | |
michael@0 | 24 | */ |
michael@0 | 25 | |
michael@0 | 26 | #include <assert.h> |
michael@0 | 27 | #include <fstream> |
michael@0 | 28 | #include <hash_map> |
michael@0 | 29 | #include <vector> |
michael@0 | 30 | #include <limits.h> |
michael@0 | 31 | #include <unistd.h> |
michael@0 | 32 | #include <stdio.h> |
michael@0 | 33 | #include <fcntl.h> |
michael@0 | 34 | |
michael@0 | 35 | #include "elf_symbol_table.h" |
michael@0 | 36 | |
michael@0 | 37 | #define _GNU_SOURCE |
michael@0 | 38 | #include <getopt.h> |
michael@0 | 39 | |
michael@0 | 40 | #define PAGE_SIZE 4096 |
michael@0 | 41 | #define SYMBOL_ALIGN 4 |
michael@0 | 42 | |
michael@0 | 43 | //---------------------------------------------------------------------- |
michael@0 | 44 | |
michael@0 | 45 | class call_pair |
michael@0 | 46 | { |
michael@0 | 47 | public: |
michael@0 | 48 | const Elf32_Sym *m_lo; |
michael@0 | 49 | const Elf32_Sym *m_hi; |
michael@0 | 50 | |
michael@0 | 51 | call_pair(const Elf32_Sym *site1, const Elf32_Sym *site2) |
michael@0 | 52 | { |
michael@0 | 53 | if (site1 < site2) { |
michael@0 | 54 | m_lo = site1; |
michael@0 | 55 | m_hi = site2; |
michael@0 | 56 | } |
michael@0 | 57 | else { |
michael@0 | 58 | m_hi = site1; |
michael@0 | 59 | m_lo = site2; |
michael@0 | 60 | } |
michael@0 | 61 | } |
michael@0 | 62 | |
michael@0 | 63 | friend bool |
michael@0 | 64 | operator==(const call_pair &lhs, const call_pair &rhs) |
michael@0 | 65 | { |
michael@0 | 66 | return (lhs.m_lo == rhs.m_lo) && (lhs.m_hi == rhs.m_hi); |
michael@0 | 67 | } |
michael@0 | 68 | }; |
michael@0 | 69 | |
michael@0 | 70 | // Straight outta plhash.c! |
michael@0 | 71 | #define GOLDEN_RATIO 0x9E3779B9U |
michael@0 | 72 | |
michael@0 | 73 | template<> |
michael@0 | 74 | struct hash<call_pair> |
michael@0 | 75 | { |
michael@0 | 76 | size_t operator()(const call_pair &pair) const |
michael@0 | 77 | { |
michael@0 | 78 | size_t h = (reinterpret_cast<size_t>(pair.m_hi) >> 4); |
michael@0 | 79 | h += (reinterpret_cast<size_t>(pair.m_lo) >> 4); |
michael@0 | 80 | h *= GOLDEN_RATIO; |
michael@0 | 81 | return h; |
michael@0 | 82 | } |
michael@0 | 83 | }; |
michael@0 | 84 | |
michael@0 | 85 | //---------------------------------------------------------------------- |
michael@0 | 86 | |
michael@0 | 87 | struct hash<const Elf32_Sym *> |
michael@0 | 88 | { |
michael@0 | 89 | size_t operator()(const Elf32_Sym *sym) const |
michael@0 | 90 | { |
michael@0 | 91 | return (reinterpret_cast<size_t>(sym) >> 4) * GOLDEN_RATIO; |
michael@0 | 92 | } |
michael@0 | 93 | }; |
michael@0 | 94 | |
michael@0 | 95 | //---------------------------------------------------------------------- |
michael@0 | 96 | |
michael@0 | 97 | typedef hash_map<call_pair, unsigned int> call_graph_t; |
michael@0 | 98 | call_graph_t call_graph; |
michael@0 | 99 | |
michael@0 | 100 | typedef hash_map<const Elf32_Sym *, unsigned int> histogram_t; |
michael@0 | 101 | histogram_t histogram; |
michael@0 | 102 | long long total_calls = 0; |
michael@0 | 103 | |
michael@0 | 104 | elf_symbol_table symtab; |
michael@0 | 105 | |
michael@0 | 106 | bool opt_debug = false; |
michael@0 | 107 | int opt_generations = 10; |
michael@0 | 108 | int opt_mutate = 0; |
michael@0 | 109 | const char *opt_out = "order.out"; |
michael@0 | 110 | int opt_population_size = 100; |
michael@0 | 111 | int opt_tick = 0; |
michael@0 | 112 | bool opt_verbose = false; |
michael@0 | 113 | int opt_window = 0; |
michael@0 | 114 | |
michael@0 | 115 | static struct option long_options[] = { |
michael@0 | 116 | { "debug", no_argument, 0, 'd' }, |
michael@0 | 117 | { "exe", required_argument, 0, 'e' }, |
michael@0 | 118 | { "generations", required_argument, 0, 'g' }, |
michael@0 | 119 | { "help", no_argument, 0, '?' }, |
michael@0 | 120 | { "mutate", required_argument, 0, 'm' }, |
michael@0 | 121 | { "out", required_argument, 0, 'o' }, |
michael@0 | 122 | { "population", required_argument, 0, 'p' }, |
michael@0 | 123 | { "seed", required_argument, 0, 's' }, |
michael@0 | 124 | { "tick", optional_argument, 0, 't' }, |
michael@0 | 125 | { "verbose", no_argument, 0, 'v' }, |
michael@0 | 126 | { "window", required_argument, 0, 'w' }, |
michael@0 | 127 | { 0, 0, 0, 0 } |
michael@0 | 128 | }; |
michael@0 | 129 | |
michael@0 | 130 | //---------------------------------------------------------------------- |
michael@0 | 131 | |
michael@0 | 132 | static long long |
michael@0 | 133 | llrand() |
michael@0 | 134 | { |
michael@0 | 135 | long long result; |
michael@0 | 136 | result = (long long) rand(); |
michael@0 | 137 | result *= (long long) (unsigned int) (RAND_MAX + 1); |
michael@0 | 138 | result += (long long) rand(); |
michael@0 | 139 | return result; |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | //---------------------------------------------------------------------- |
michael@0 | 143 | |
michael@0 | 144 | class symbol_order { |
michael@0 | 145 | public: |
michael@0 | 146 | typedef vector<const Elf32_Sym *> vector_t; |
michael@0 | 147 | typedef long long score_t; |
michael@0 | 148 | |
michael@0 | 149 | static const score_t max_score; |
michael@0 | 150 | |
michael@0 | 151 | /** |
michael@0 | 152 | * A vector of symbols that is this ordering. |
michael@0 | 153 | */ |
michael@0 | 154 | vector_t m_ordering; |
michael@0 | 155 | |
michael@0 | 156 | /** |
michael@0 | 157 | * The symbol ordering's score. |
michael@0 | 158 | */ |
michael@0 | 159 | score_t m_score; |
michael@0 | 160 | |
michael@0 | 161 | symbol_order() : m_score(0) {} |
michael@0 | 162 | |
michael@0 | 163 | /** |
michael@0 | 164 | * ``Shuffle'' a symbol ordering, randomizing it. |
michael@0 | 165 | */ |
michael@0 | 166 | void shuffle(); |
michael@0 | 167 | |
michael@0 | 168 | /** |
michael@0 | 169 | * Initialize this symbol ordering by performing a crossover from |
michael@0 | 170 | * two ``parent'' symbol orderings. |
michael@0 | 171 | */ |
michael@0 | 172 | void crossover_from(const symbol_order *father, const symbol_order *mother); |
michael@0 | 173 | |
michael@0 | 174 | /** |
michael@0 | 175 | * Randomly mutate this symbol ordering. |
michael@0 | 176 | */ |
michael@0 | 177 | void mutate(); |
michael@0 | 178 | |
michael@0 | 179 | /** |
michael@0 | 180 | * Score a symbol ordering based on paginated locality. |
michael@0 | 181 | */ |
michael@0 | 182 | score_t compute_score_page(); |
michael@0 | 183 | |
michael@0 | 184 | /** |
michael@0 | 185 | * Score a symbol ordering based on a sliding window. |
michael@0 | 186 | */ |
michael@0 | 187 | score_t compute_score_window(int window_size); |
michael@0 | 188 | |
michael@0 | 189 | static score_t compute_score(symbol_order &order); |
michael@0 | 190 | |
michael@0 | 191 | /** |
michael@0 | 192 | * Use the symbol table to dump the ordered symbolic constants. |
michael@0 | 193 | */ |
michael@0 | 194 | void dump_symbols() const; |
michael@0 | 195 | |
michael@0 | 196 | friend ostream & |
michael@0 | 197 | operator<<(ostream &out, const symbol_order &order); |
michael@0 | 198 | }; |
michael@0 | 199 | |
michael@0 | 200 | const symbol_order::score_t |
michael@0 | 201 | symbol_order::max_score = ~((symbol_order::score_t)1 << ((sizeof(symbol_order::score_t) * 8) - 1)); |
michael@0 | 202 | |
michael@0 | 203 | symbol_order::score_t |
michael@0 | 204 | symbol_order::compute_score_page() |
michael@0 | 205 | { |
michael@0 | 206 | m_score = 0; |
michael@0 | 207 | |
michael@0 | 208 | unsigned int off = 0; // XXX in reality, probably not page-aligned to start |
michael@0 | 209 | |
michael@0 | 210 | vector_t::const_iterator end = m_ordering.end(), |
michael@0 | 211 | last = end, |
michael@0 | 212 | sym = m_ordering.begin(); |
michael@0 | 213 | |
michael@0 | 214 | while (sym != end) { |
michael@0 | 215 | vector_t page; |
michael@0 | 216 | |
michael@0 | 217 | // If we had a symbol that spilled over from the last page, |
michael@0 | 218 | // then include it here. |
michael@0 | 219 | if (last != end) |
michael@0 | 220 | page.push_back(*last); |
michael@0 | 221 | |
michael@0 | 222 | // Pack symbols into the page |
michael@0 | 223 | do { |
michael@0 | 224 | page.push_back(*sym); |
michael@0 | 225 | |
michael@0 | 226 | int size = (*sym)->st_size; |
michael@0 | 227 | size += SYMBOL_ALIGN - 1; |
michael@0 | 228 | size &= ~(SYMBOL_ALIGN - 1); |
michael@0 | 229 | |
michael@0 | 230 | off += size; |
michael@0 | 231 | } while (++sym != end && off < PAGE_SIZE); |
michael@0 | 232 | |
michael@0 | 233 | // Remember if there was spill-over. |
michael@0 | 234 | off %= PAGE_SIZE; |
michael@0 | 235 | last = (off != 0) ? sym : end; |
michael@0 | 236 | |
michael@0 | 237 | // Now score the page as the count of all calls to symbols on |
michael@0 | 238 | // the page, less calls between the symbols on the page. |
michael@0 | 239 | vector_t::const_iterator page_end = page.end(); |
michael@0 | 240 | for (vector_t::const_iterator i = page.begin(); i != page_end; ++i) { |
michael@0 | 241 | histogram_t::const_iterator func = histogram.find(*i); |
michael@0 | 242 | if (func == histogram.end()) |
michael@0 | 243 | continue; |
michael@0 | 244 | |
michael@0 | 245 | m_score += func->second; |
michael@0 | 246 | |
michael@0 | 247 | vector_t::const_iterator j = i; |
michael@0 | 248 | for (++j; j != page_end; ++j) { |
michael@0 | 249 | call_graph_t::const_iterator call = |
michael@0 | 250 | call_graph.find(call_pair(*i, *j)); |
michael@0 | 251 | |
michael@0 | 252 | if (call != call_graph.end()) |
michael@0 | 253 | m_score -= call->second; |
michael@0 | 254 | } |
michael@0 | 255 | } |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | assert(m_score >= 0); |
michael@0 | 259 | |
michael@0 | 260 | // Integer reciprocal so we minimize instead of maximize. |
michael@0 | 261 | if (m_score == 0) |
michael@0 | 262 | m_score = 1; |
michael@0 | 263 | |
michael@0 | 264 | m_score = (total_calls / m_score) + 1; |
michael@0 | 265 | |
michael@0 | 266 | return m_score; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | symbol_order::score_t |
michael@0 | 270 | symbol_order::compute_score_window(int window_size) |
michael@0 | 271 | { |
michael@0 | 272 | m_score = 0; |
michael@0 | 273 | |
michael@0 | 274 | vector_t::const_iterator *window = new vector_t::const_iterator[window_size]; |
michael@0 | 275 | int window_fill = 0; |
michael@0 | 276 | |
michael@0 | 277 | vector_t::const_iterator end = m_ordering.end(), |
michael@0 | 278 | sym = m_ordering.begin(); |
michael@0 | 279 | |
michael@0 | 280 | for (; sym != end; ++sym) { |
michael@0 | 281 | histogram_t::const_iterator func = histogram.find(*sym); |
michael@0 | 282 | if (func != histogram.end()) { |
michael@0 | 283 | long long scale = ((long long) 1) << window_size; |
michael@0 | 284 | |
michael@0 | 285 | m_score += func->second * scale * 2; |
michael@0 | 286 | |
michael@0 | 287 | vector_t::const_iterator *limit = window + window_fill; |
michael@0 | 288 | vector_t::const_iterator *iter; |
michael@0 | 289 | for (iter = window ; iter < limit; ++iter) { |
michael@0 | 290 | call_graph_t::const_iterator call = |
michael@0 | 291 | call_graph.find(call_pair(*sym, **iter)); |
michael@0 | 292 | |
michael@0 | 293 | if (call != call_graph.end()) |
michael@0 | 294 | m_score -= (call->second * scale); |
michael@0 | 295 | |
michael@0 | 296 | scale >>= 1; |
michael@0 | 297 | } |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | // Slide the window. |
michael@0 | 301 | vector_t::const_iterator *begin = window; |
michael@0 | 302 | vector_t::const_iterator *iter; |
michael@0 | 303 | for (iter = window + (window_size - 1); iter > begin; --iter) |
michael@0 | 304 | *iter = *(iter - 1); |
michael@0 | 305 | |
michael@0 | 306 | if (window_fill < window_size) |
michael@0 | 307 | ++window_fill; |
michael@0 | 308 | |
michael@0 | 309 | *window = sym; |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | delete[] window; |
michael@0 | 313 | |
michael@0 | 314 | assert(m_score >= 0); |
michael@0 | 315 | |
michael@0 | 316 | // Integer reciprocal so we minimize instead of maximize. |
michael@0 | 317 | if (m_score == 0) |
michael@0 | 318 | m_score = 1; |
michael@0 | 319 | |
michael@0 | 320 | m_score = (total_calls / m_score) + 1; |
michael@0 | 321 | |
michael@0 | 322 | return m_score; |
michael@0 | 323 | } |
michael@0 | 324 | |
michael@0 | 325 | symbol_order::score_t |
michael@0 | 326 | symbol_order::compute_score(symbol_order &order) |
michael@0 | 327 | { |
michael@0 | 328 | if (opt_window) |
michael@0 | 329 | return order.compute_score_window(opt_window); |
michael@0 | 330 | |
michael@0 | 331 | return order.compute_score_page(); |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | void |
michael@0 | 335 | symbol_order::shuffle() |
michael@0 | 336 | { |
michael@0 | 337 | vector_t::iterator sym = m_ordering.begin(); |
michael@0 | 338 | vector_t::iterator end = m_ordering.end(); |
michael@0 | 339 | for (; sym != end; ++sym) { |
michael@0 | 340 | int i = rand() % m_ordering.size(); |
michael@0 | 341 | const Elf32_Sym *temp = *sym; |
michael@0 | 342 | *sym = m_ordering[i]; |
michael@0 | 343 | m_ordering[i] = temp; |
michael@0 | 344 | } |
michael@0 | 345 | } |
michael@0 | 346 | |
michael@0 | 347 | void |
michael@0 | 348 | symbol_order::crossover_from(const symbol_order *father, const symbol_order *mother) |
michael@0 | 349 | { |
michael@0 | 350 | histogram_t used; |
michael@0 | 351 | |
michael@0 | 352 | m_ordering = vector_t(father->m_ordering.size(), 0); |
michael@0 | 353 | |
michael@0 | 354 | vector_t::const_iterator parent_sym = father->m_ordering.begin(); |
michael@0 | 355 | vector_t::iterator sym = m_ordering.begin(); |
michael@0 | 356 | vector_t::iterator end = m_ordering.end(); |
michael@0 | 357 | |
michael@0 | 358 | for (; sym != end; ++sym, ++parent_sym) { |
michael@0 | 359 | if (rand() % 2) { |
michael@0 | 360 | *sym = *parent_sym; |
michael@0 | 361 | used[*parent_sym] = 1; |
michael@0 | 362 | } |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | parent_sym = mother->m_ordering.begin(); |
michael@0 | 366 | sym = m_ordering.begin(); |
michael@0 | 367 | |
michael@0 | 368 | for (; sym != end; ++sym) { |
michael@0 | 369 | if (! *sym) { |
michael@0 | 370 | while (used[*parent_sym]) |
michael@0 | 371 | ++parent_sym; |
michael@0 | 372 | |
michael@0 | 373 | *sym = *parent_sym++; |
michael@0 | 374 | } |
michael@0 | 375 | } |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | void |
michael@0 | 379 | symbol_order::mutate() |
michael@0 | 380 | { |
michael@0 | 381 | int i, j; |
michael@0 | 382 | i = rand() % m_ordering.size(); |
michael@0 | 383 | j = rand() % m_ordering.size(); |
michael@0 | 384 | |
michael@0 | 385 | const Elf32_Sym *temp = m_ordering[i]; |
michael@0 | 386 | m_ordering[i] = m_ordering[j]; |
michael@0 | 387 | m_ordering[j] = temp; |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | void |
michael@0 | 391 | symbol_order::dump_symbols() const |
michael@0 | 392 | { |
michael@0 | 393 | ofstream out(opt_out); |
michael@0 | 394 | |
michael@0 | 395 | vector_t::const_iterator sym = m_ordering.begin(); |
michael@0 | 396 | vector_t::const_iterator end = m_ordering.end(); |
michael@0 | 397 | for (; sym != end; ++sym) |
michael@0 | 398 | out << symtab.get_symbol_name(*sym) << endl; |
michael@0 | 399 | |
michael@0 | 400 | out.close(); |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | ostream & |
michael@0 | 404 | operator<<(ostream &out, const symbol_order &order) |
michael@0 | 405 | { |
michael@0 | 406 | out << "symbol_order(" << order.m_score << ") "; |
michael@0 | 407 | |
michael@0 | 408 | symbol_order::vector_t::const_iterator sym = order.m_ordering.begin(); |
michael@0 | 409 | symbol_order::vector_t::const_iterator end = order.m_ordering.end(); |
michael@0 | 410 | for (; sym != end; ++sym) |
michael@0 | 411 | out.form("%08x ", *sym); |
michael@0 | 412 | |
michael@0 | 413 | out << endl; |
michael@0 | 414 | |
michael@0 | 415 | return out; |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | //---------------------------------------------------------------------- |
michael@0 | 419 | |
michael@0 | 420 | static void |
michael@0 | 421 | usage(const char *name) |
michael@0 | 422 | { |
michael@0 | 423 | cerr << "usage: " << name << " [options] [<file> ...]" << endl; |
michael@0 | 424 | cerr << " Options:" << endl; |
michael@0 | 425 | cerr << " --debug, -d" << endl; |
michael@0 | 426 | cerr << " Print lots of verbose debugging cruft." << endl; |
michael@0 | 427 | cerr << " --exe=<image>, -e <image> (required)" << endl; |
michael@0 | 428 | cerr << " Specify the executable image from which to read symbol information." << endl; |
michael@0 | 429 | cerr << " --generations=<num>, -g <num>" << endl; |
michael@0 | 430 | cerr << " Specify the number of generations to run the GA (default is 10)." << endl; |
michael@0 | 431 | cerr << " --help, -?" << endl; |
michael@0 | 432 | cerr << " Print this message and exit." << endl; |
michael@0 | 433 | cerr << " --mutate=<num>, -m <num>" << endl; |
michael@0 | 434 | cerr << " Mutate every <num>th individual, or zero for no mutation (default)." << endl; |
michael@0 | 435 | cerr << " --out=<file>, -o <file>" << endl; |
michael@0 | 436 | cerr << " Specify the output file to which to dump the symbol ordering of the" << endl; |
michael@0 | 437 | cerr << " best individual (default is `order.out')." << endl; |
michael@0 | 438 | cerr << " --population=<num>, -p <num>" << endl; |
michael@0 | 439 | cerr << " Set the population size to <num> individuals (default is 100)." << endl; |
michael@0 | 440 | cerr << " --seed=<num>, -s <num>" << endl; |
michael@0 | 441 | cerr << " Specify a seed to srand()." << endl; |
michael@0 | 442 | cerr << " --tick[=<num>], -t [<num>]" << endl; |
michael@0 | 443 | cerr << " When reading address data, print a dot to stderr every <num>th" << endl; |
michael@0 | 444 | cerr << " address processed from the call trace. If specified with no argument," << endl; |
michael@0 | 445 | cerr << " a dot will be printed for every million addresses processed." << endl; |
michael@0 | 446 | cerr << " --verbose, -v" << endl; |
michael@0 | 447 | cerr << " Issue progress messages to stderr." << endl; |
michael@0 | 448 | cerr << " --window=<num>, -w <num>" << endl; |
michael@0 | 449 | cerr << " Use a sliding window instead of pagination to score orderings." << endl; |
michael@0 | 450 | cerr << endl; |
michael@0 | 451 | cerr << "This program uses a genetic algorithm to produce an `optimal' ordering for" << endl; |
michael@0 | 452 | cerr << "an executable based on call patterns." << endl; |
michael@0 | 453 | cerr << endl; |
michael@0 | 454 | cerr << "Addresses from a call trace are read as binary data from the files" << endl; |
michael@0 | 455 | cerr << "specified, or from stdin if no files are specified. These addresses" << endl; |
michael@0 | 456 | cerr << "are used with the symbolic information from the executable to create" << endl; |
michael@0 | 457 | cerr << "a call graph. This call graph is used to `score' arbitrary symbol" << endl; |
michael@0 | 458 | cerr << "orderings, and provides the fitness function for the GA." << endl; |
michael@0 | 459 | cerr << endl; |
michael@0 | 460 | } |
michael@0 | 461 | |
michael@0 | 462 | /** |
michael@0 | 463 | * Using the symbol table, map a stream of address references into a |
michael@0 | 464 | * callgraph and a histogram. |
michael@0 | 465 | */ |
michael@0 | 466 | static void |
michael@0 | 467 | map_addrs(int fd) |
michael@0 | 468 | { |
michael@0 | 469 | const Elf32_Sym *last = 0; |
michael@0 | 470 | unsigned int buf[128]; |
michael@0 | 471 | ssize_t cb; |
michael@0 | 472 | |
michael@0 | 473 | unsigned int count = 0; |
michael@0 | 474 | while ((cb = read(fd, buf, sizeof buf)) > 0) { |
michael@0 | 475 | if (cb % sizeof buf[0]) |
michael@0 | 476 | fprintf(stderr, "unaligned read\n"); |
michael@0 | 477 | |
michael@0 | 478 | unsigned int *addr = buf; |
michael@0 | 479 | unsigned int *limit = buf + (cb / 4); |
michael@0 | 480 | |
michael@0 | 481 | for (; addr < limit; ++addr) { |
michael@0 | 482 | const Elf32_Sym *sym = symtab.lookup(*addr); |
michael@0 | 483 | |
michael@0 | 484 | if (last && sym && last != sym) { |
michael@0 | 485 | ++total_calls; |
michael@0 | 486 | ++histogram[sym]; |
michael@0 | 487 | ++call_graph[call_pair(last, sym)]; |
michael@0 | 488 | |
michael@0 | 489 | if (opt_tick && (++count % opt_tick == 0)) { |
michael@0 | 490 | cerr << "."; |
michael@0 | 491 | flush(cerr); |
michael@0 | 492 | } |
michael@0 | 493 | } |
michael@0 | 494 | |
michael@0 | 495 | last = sym; |
michael@0 | 496 | } |
michael@0 | 497 | } |
michael@0 | 498 | |
michael@0 | 499 | if (opt_tick) |
michael@0 | 500 | cerr << endl; |
michael@0 | 501 | |
michael@0 | 502 | cerr << "Total calls: " << total_calls << endl; |
michael@0 | 503 | total_calls *= 1024; |
michael@0 | 504 | |
michael@0 | 505 | if (opt_window) |
michael@0 | 506 | total_calls <<= (opt_window + 1); |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | static symbol_order * |
michael@0 | 510 | pick_parent(symbol_order *ordering, int max, int index) |
michael@0 | 511 | { |
michael@0 | 512 | while (1) { |
michael@0 | 513 | index -= ordering->m_score; |
michael@0 | 514 | if (index < 0) |
michael@0 | 515 | break; |
michael@0 | 516 | |
michael@0 | 517 | ++ordering; |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | return ordering; |
michael@0 | 521 | } |
michael@0 | 522 | |
michael@0 | 523 | /** |
michael@0 | 524 | * The main program |
michael@0 | 525 | */ |
michael@0 | 526 | int |
michael@0 | 527 | main(int argc, char *argv[]) |
michael@0 | 528 | { |
michael@0 | 529 | const char *opt_exe = 0; |
michael@0 | 530 | |
michael@0 | 531 | int c; |
michael@0 | 532 | while (1) { |
michael@0 | 533 | int option_index = 0; |
michael@0 | 534 | c = getopt_long(argc, argv, "?de:g:m:o:p:s:t:vw:", long_options, &option_index); |
michael@0 | 535 | |
michael@0 | 536 | if (c < 0) |
michael@0 | 537 | break; |
michael@0 | 538 | |
michael@0 | 539 | switch (c) { |
michael@0 | 540 | case '?': |
michael@0 | 541 | usage(argv[0]); |
michael@0 | 542 | return 0; |
michael@0 | 543 | |
michael@0 | 544 | case 'd': |
michael@0 | 545 | opt_debug = true; |
michael@0 | 546 | break; |
michael@0 | 547 | |
michael@0 | 548 | case 'e': |
michael@0 | 549 | opt_exe = optarg; |
michael@0 | 550 | break; |
michael@0 | 551 | |
michael@0 | 552 | case 'g': |
michael@0 | 553 | opt_generations = atoi(optarg); |
michael@0 | 554 | break; |
michael@0 | 555 | |
michael@0 | 556 | case 'm': |
michael@0 | 557 | opt_mutate = atoi(optarg); |
michael@0 | 558 | break; |
michael@0 | 559 | |
michael@0 | 560 | case 'o': |
michael@0 | 561 | opt_out = optarg; |
michael@0 | 562 | break; |
michael@0 | 563 | |
michael@0 | 564 | case 'p': |
michael@0 | 565 | opt_population_size = atoi(optarg); |
michael@0 | 566 | break; |
michael@0 | 567 | |
michael@0 | 568 | case 's': |
michael@0 | 569 | srand(atoi(optarg)); |
michael@0 | 570 | break; |
michael@0 | 571 | |
michael@0 | 572 | case 't': |
michael@0 | 573 | opt_tick = optarg ? atoi(optarg) : 1000000; |
michael@0 | 574 | break; |
michael@0 | 575 | |
michael@0 | 576 | case 'v': |
michael@0 | 577 | opt_verbose = true; |
michael@0 | 578 | break; |
michael@0 | 579 | |
michael@0 | 580 | case 'w': |
michael@0 | 581 | opt_window = atoi(optarg); |
michael@0 | 582 | if (opt_window < 0 || opt_window > 8) { |
michael@0 | 583 | cerr << "invalid window size: " << opt_window << endl; |
michael@0 | 584 | return 1; |
michael@0 | 585 | } |
michael@0 | 586 | |
michael@0 | 587 | break; |
michael@0 | 588 | |
michael@0 | 589 | default: |
michael@0 | 590 | usage(argv[0]); |
michael@0 | 591 | return 1; |
michael@0 | 592 | } |
michael@0 | 593 | } |
michael@0 | 594 | |
michael@0 | 595 | // Make sure an image was specified |
michael@0 | 596 | if (! opt_exe) { |
michael@0 | 597 | usage(argv[0]); |
michael@0 | 598 | return 1; |
michael@0 | 599 | } |
michael@0 | 600 | |
michael@0 | 601 | // Read the sym table. |
michael@0 | 602 | symtab.init(opt_exe); |
michael@0 | 603 | |
michael@0 | 604 | // Process addresses to construct the call graph. |
michael@0 | 605 | if (optind >= argc) { |
michael@0 | 606 | map_addrs(STDIN_FILENO); |
michael@0 | 607 | } |
michael@0 | 608 | else { |
michael@0 | 609 | do { |
michael@0 | 610 | int fd = open(argv[optind], O_RDONLY); |
michael@0 | 611 | if (fd < 0) { |
michael@0 | 612 | perror(argv[optind]); |
michael@0 | 613 | return 1; |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | map_addrs(fd); |
michael@0 | 617 | close(fd); |
michael@0 | 618 | } while (++optind < argc); |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | if (opt_debug) { |
michael@0 | 622 | cerr << "Call graph:" << endl; |
michael@0 | 623 | |
michael@0 | 624 | call_graph_t::const_iterator limit = call_graph.end(); |
michael@0 | 625 | call_graph_t::const_iterator i; |
michael@0 | 626 | for (i = call_graph.begin(); i != limit; ++i) { |
michael@0 | 627 | const call_pair& pair = i->first; |
michael@0 | 628 | cerr.form("%08x %08x %10d\n", |
michael@0 | 629 | pair.m_lo->st_value, |
michael@0 | 630 | pair.m_hi->st_value, |
michael@0 | 631 | i->second); |
michael@0 | 632 | } |
michael@0 | 633 | } |
michael@0 | 634 | |
michael@0 | 635 | // Collect the symbols into a vector |
michael@0 | 636 | symbol_order::vector_t ordering; |
michael@0 | 637 | elf_symbol_table::const_iterator end = symtab.end(); |
michael@0 | 638 | for (elf_symbol_table::const_iterator sym = symtab.begin(); sym != end; ++sym) { |
michael@0 | 639 | if (symtab.is_function(sym)) |
michael@0 | 640 | ordering.push_back(sym); |
michael@0 | 641 | } |
michael@0 | 642 | |
michael@0 | 643 | if (opt_verbose) { |
michael@0 | 644 | symbol_order initial; |
michael@0 | 645 | initial.m_ordering = ordering; |
michael@0 | 646 | cerr << "created initial ordering, score=" << symbol_order::compute_score(initial) << endl; |
michael@0 | 647 | |
michael@0 | 648 | if (opt_debug) |
michael@0 | 649 | cerr << initial; |
michael@0 | 650 | } |
michael@0 | 651 | |
michael@0 | 652 | // Create a population. |
michael@0 | 653 | if (opt_verbose) |
michael@0 | 654 | cerr << "creating population" << endl; |
michael@0 | 655 | |
michael@0 | 656 | symbol_order *population = new symbol_order[opt_population_size]; |
michael@0 | 657 | |
michael@0 | 658 | symbol_order::score_t total = 0, min = symbol_order::max_score, max = 0; |
michael@0 | 659 | |
michael@0 | 660 | // Score it. |
michael@0 | 661 | symbol_order *order = population; |
michael@0 | 662 | symbol_order *limit = population + opt_population_size; |
michael@0 | 663 | for (; order < limit; ++order) { |
michael@0 | 664 | order->m_ordering = ordering; |
michael@0 | 665 | order->shuffle(); |
michael@0 | 666 | |
michael@0 | 667 | symbol_order::score_t score = symbol_order::compute_score(*order); |
michael@0 | 668 | |
michael@0 | 669 | if (opt_debug) |
michael@0 | 670 | cerr << *order; |
michael@0 | 671 | |
michael@0 | 672 | if (min > score) |
michael@0 | 673 | min = score; |
michael@0 | 674 | if (max < score) |
michael@0 | 675 | max = score; |
michael@0 | 676 | |
michael@0 | 677 | total += score; |
michael@0 | 678 | } |
michael@0 | 679 | |
michael@0 | 680 | if (opt_verbose) { |
michael@0 | 681 | cerr << "Initial population"; |
michael@0 | 682 | cerr << ": min=" << min; |
michael@0 | 683 | cerr << ", max=" << max; |
michael@0 | 684 | cerr << " mean=" << (total / opt_population_size); |
michael@0 | 685 | cerr << endl; |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | |
michael@0 | 689 | // Run the GA. |
michael@0 | 690 | if (opt_verbose) |
michael@0 | 691 | cerr << "begininng ga" << endl; |
michael@0 | 692 | |
michael@0 | 693 | symbol_order::score_t best = 0; |
michael@0 | 694 | |
michael@0 | 695 | for (int generation = 1; generation <= opt_generations; ++generation) { |
michael@0 | 696 | // Create a new population. |
michael@0 | 697 | symbol_order *offspring = new symbol_order[opt_population_size]; |
michael@0 | 698 | |
michael@0 | 699 | symbol_order *kid = offspring; |
michael@0 | 700 | symbol_order *offspring_limit = offspring + opt_population_size; |
michael@0 | 701 | for (; kid < offspring_limit; ++kid) { |
michael@0 | 702 | // Pick parents. |
michael@0 | 703 | symbol_order *father, *mother; |
michael@0 | 704 | father = pick_parent(population, max, llrand() % total); |
michael@0 | 705 | mother = pick_parent(population, max, llrand() % total); |
michael@0 | 706 | |
michael@0 | 707 | // Create a kid. |
michael@0 | 708 | kid->crossover_from(father, mother); |
michael@0 | 709 | |
michael@0 | 710 | // Mutate, possibly. |
michael@0 | 711 | if (opt_mutate) { |
michael@0 | 712 | if (rand() % opt_mutate == 0) |
michael@0 | 713 | kid->mutate(); |
michael@0 | 714 | } |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | delete[] population; |
michael@0 | 718 | population = offspring; |
michael@0 | 719 | |
michael@0 | 720 | // Score the new population. |
michael@0 | 721 | total = 0; |
michael@0 | 722 | min = symbol_order::max_score; |
michael@0 | 723 | max = 0; |
michael@0 | 724 | |
michael@0 | 725 | symbol_order *fittest = 0; |
michael@0 | 726 | |
michael@0 | 727 | limit = offspring_limit; |
michael@0 | 728 | for (order = population; order < limit; ++order) { |
michael@0 | 729 | symbol_order::score_t score = symbol_order::compute_score(*order); |
michael@0 | 730 | |
michael@0 | 731 | if (opt_debug) |
michael@0 | 732 | cerr << *order; |
michael@0 | 733 | |
michael@0 | 734 | if (min > score) |
michael@0 | 735 | min = score; |
michael@0 | 736 | |
michael@0 | 737 | if (max < score) |
michael@0 | 738 | max = score; |
michael@0 | 739 | |
michael@0 | 740 | if (best < score) { |
michael@0 | 741 | best = score; |
michael@0 | 742 | fittest = order; |
michael@0 | 743 | } |
michael@0 | 744 | |
michael@0 | 745 | total += score; |
michael@0 | 746 | } |
michael@0 | 747 | |
michael@0 | 748 | if (opt_verbose) { |
michael@0 | 749 | cerr << "Generation " << generation; |
michael@0 | 750 | cerr << ": min=" << min; |
michael@0 | 751 | cerr << ", max=" << max; |
michael@0 | 752 | if (fittest) |
michael@0 | 753 | cerr << "*"; |
michael@0 | 754 | cerr << " mean=" << (total / opt_population_size); |
michael@0 | 755 | cerr << endl; |
michael@0 | 756 | } |
michael@0 | 757 | |
michael@0 | 758 | // If we've found a new ``best'' individual, dump it. |
michael@0 | 759 | if (fittest) |
michael@0 | 760 | fittest->dump_symbols(); |
michael@0 | 761 | } |
michael@0 | 762 | |
michael@0 | 763 | delete[] population; |
michael@0 | 764 | return 0; |
michael@0 | 765 | } |