Root/
Source at commit ec7cab4cbb721bff91ec924ec691efd8daf36579 created 12 years 8 months ago. By Maarten ter Huurne, MIPS: JZ4740: A320: Updated quickstart documentation. | |
---|---|
1 | #include "symbol.h" |
2 | #include <errno.h> |
3 | #include <inttypes.h> |
4 | #include <limits.h> |
5 | #include <stdlib.h> |
6 | #include <string.h> |
7 | #include <stdio.h> |
8 | #include <unistd.h> |
9 | #include "map.h" |
10 | |
11 | const char *map_type__name[MAP__NR_TYPES] = { |
12 | [MAP__FUNCTION] = "Functions", |
13 | [MAP__VARIABLE] = "Variables", |
14 | }; |
15 | |
16 | static inline int is_anon_memory(const char *filename) |
17 | { |
18 | return strcmp(filename, "//anon") == 0; |
19 | } |
20 | |
21 | void map__init(struct map *self, enum map_type type, |
22 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
23 | { |
24 | self->type = type; |
25 | self->start = start; |
26 | self->end = end; |
27 | self->pgoff = pgoff; |
28 | self->dso = dso; |
29 | self->map_ip = map__map_ip; |
30 | self->unmap_ip = map__unmap_ip; |
31 | RB_CLEAR_NODE(&self->rb_node); |
32 | self->groups = NULL; |
33 | self->referenced = false; |
34 | } |
35 | |
36 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
37 | u64 pgoff, u32 pid, char *filename, |
38 | enum map_type type) |
39 | { |
40 | struct map *self = malloc(sizeof(*self)); |
41 | |
42 | if (self != NULL) { |
43 | char newfilename[PATH_MAX]; |
44 | struct dso *dso; |
45 | int anon; |
46 | |
47 | anon = is_anon_memory(filename); |
48 | |
49 | if (anon) { |
50 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); |
51 | filename = newfilename; |
52 | } |
53 | |
54 | dso = __dsos__findnew(dsos__list, filename); |
55 | if (dso == NULL) |
56 | goto out_delete; |
57 | |
58 | map__init(self, type, start, start + len, pgoff, dso); |
59 | |
60 | if (anon) { |
61 | set_identity: |
62 | self->map_ip = self->unmap_ip = identity__map_ip; |
63 | } else if (strcmp(filename, "[vdso]") == 0) { |
64 | dso__set_loaded(dso, self->type); |
65 | goto set_identity; |
66 | } |
67 | } |
68 | return self; |
69 | out_delete: |
70 | free(self); |
71 | return NULL; |
72 | } |
73 | |
74 | void map__delete(struct map *self) |
75 | { |
76 | free(self); |
77 | } |
78 | |
79 | void map__fixup_start(struct map *self) |
80 | { |
81 | struct rb_root *symbols = &self->dso->symbols[self->type]; |
82 | struct rb_node *nd = rb_first(symbols); |
83 | if (nd != NULL) { |
84 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
85 | self->start = sym->start; |
86 | } |
87 | } |
88 | |
89 | void map__fixup_end(struct map *self) |
90 | { |
91 | struct rb_root *symbols = &self->dso->symbols[self->type]; |
92 | struct rb_node *nd = rb_last(symbols); |
93 | if (nd != NULL) { |
94 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
95 | self->end = sym->end; |
96 | } |
97 | } |
98 | |
99 | #define DSO__DELETED "(deleted)" |
100 | |
101 | int map__load(struct map *self, symbol_filter_t filter) |
102 | { |
103 | const char *name = self->dso->long_name; |
104 | int nr; |
105 | |
106 | if (dso__loaded(self->dso, self->type)) |
107 | return 0; |
108 | |
109 | nr = dso__load(self->dso, self, filter); |
110 | if (nr < 0) { |
111 | if (self->dso->has_build_id) { |
112 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
113 | |
114 | build_id__sprintf(self->dso->build_id, |
115 | sizeof(self->dso->build_id), |
116 | sbuild_id); |
117 | pr_warning("%s with build id %s not found", |
118 | name, sbuild_id); |
119 | } else |
120 | pr_warning("Failed to open %s", name); |
121 | |
122 | pr_warning(", continuing without symbols\n"); |
123 | return -1; |
124 | } else if (nr == 0) { |
125 | const size_t len = strlen(name); |
126 | const size_t real_len = len - sizeof(DSO__DELETED); |
127 | |
128 | if (len > sizeof(DSO__DELETED) && |
129 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
130 | pr_warning("%.*s was updated, restart the long " |
131 | "running apps that use it!\n", |
132 | (int)real_len, name); |
133 | } else { |
134 | pr_warning("no symbols found in %s, maybe install " |
135 | "a debug package?\n", name); |
136 | } |
137 | |
138 | return -1; |
139 | } |
140 | /* |
141 | * Only applies to the kernel, as its symtabs aren't relative like the |
142 | * module ones. |
143 | */ |
144 | if (self->dso->kernel) |
145 | map__reloc_vmlinux(self); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | struct symbol *map__find_symbol(struct map *self, u64 addr, |
151 | symbol_filter_t filter) |
152 | { |
153 | if (map__load(self, filter) < 0) |
154 | return NULL; |
155 | |
156 | return dso__find_symbol(self->dso, self->type, addr); |
157 | } |
158 | |
159 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, |
160 | symbol_filter_t filter) |
161 | { |
162 | if (map__load(self, filter) < 0) |
163 | return NULL; |
164 | |
165 | if (!dso__sorted_by_name(self->dso, self->type)) |
166 | dso__sort_by_name(self->dso, self->type); |
167 | |
168 | return dso__find_symbol_by_name(self->dso, self->type, name); |
169 | } |
170 | |
171 | struct map *map__clone(struct map *self) |
172 | { |
173 | struct map *map = malloc(sizeof(*self)); |
174 | |
175 | if (!map) |
176 | return NULL; |
177 | |
178 | memcpy(map, self, sizeof(*self)); |
179 | |
180 | return map; |
181 | } |
182 | |
183 | int map__overlap(struct map *l, struct map *r) |
184 | { |
185 | if (l->start > r->start) { |
186 | struct map *t = l; |
187 | l = r; |
188 | r = t; |
189 | } |
190 | |
191 | if (l->end > r->start) |
192 | return 1; |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | size_t map__fprintf(struct map *self, FILE *fp) |
198 | { |
199 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n", |
200 | self->start, self->end, self->pgoff, self->dso->name); |
201 | } |
202 | |
203 | /* |
204 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. |
205 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. |
206 | */ |
207 | u64 map__rip_2objdump(struct map *map, u64 rip) |
208 | { |
209 | u64 addr = map->dso->adjust_symbols ? |
210 | map->unmap_ip(map, rip) : /* RIP -> IP */ |
211 | rip; |
212 | return addr; |
213 | } |
214 | |
215 | u64 map__objdump_2ip(struct map *map, u64 addr) |
216 | { |
217 | u64 ip = map->dso->adjust_symbols ? |
218 | addr : |
219 | map->unmap_ip(map, addr); /* RIP -> IP */ |
220 | return ip; |
221 | } |
222 | |
223 | void map_groups__init(struct map_groups *self) |
224 | { |
225 | int i; |
226 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
227 | self->maps[i] = RB_ROOT; |
228 | INIT_LIST_HEAD(&self->removed_maps[i]); |
229 | } |
230 | self->machine = NULL; |
231 | } |
232 | |
233 | static void maps__delete(struct rb_root *self) |
234 | { |
235 | struct rb_node *next = rb_first(self); |
236 | |
237 | while (next) { |
238 | struct map *pos = rb_entry(next, struct map, rb_node); |
239 | |
240 | next = rb_next(&pos->rb_node); |
241 | rb_erase(&pos->rb_node, self); |
242 | map__delete(pos); |
243 | } |
244 | } |
245 | |
246 | static void maps__delete_removed(struct list_head *self) |
247 | { |
248 | struct map *pos, *n; |
249 | |
250 | list_for_each_entry_safe(pos, n, self, node) { |
251 | list_del(&pos->node); |
252 | map__delete(pos); |
253 | } |
254 | } |
255 | |
256 | void map_groups__exit(struct map_groups *self) |
257 | { |
258 | int i; |
259 | |
260 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
261 | maps__delete(&self->maps[i]); |
262 | maps__delete_removed(&self->removed_maps[i]); |
263 | } |
264 | } |
265 | |
266 | void map_groups__flush(struct map_groups *self) |
267 | { |
268 | int type; |
269 | |
270 | for (type = 0; type < MAP__NR_TYPES; type++) { |
271 | struct rb_root *root = &self->maps[type]; |
272 | struct rb_node *next = rb_first(root); |
273 | |
274 | while (next) { |
275 | struct map *pos = rb_entry(next, struct map, rb_node); |
276 | next = rb_next(&pos->rb_node); |
277 | rb_erase(&pos->rb_node, root); |
278 | /* |
279 | * We may have references to this map, for |
280 | * instance in some hist_entry instances, so |
281 | * just move them to a separate list. |
282 | */ |
283 | list_add_tail(&pos->node, &self->removed_maps[pos->type]); |
284 | } |
285 | } |
286 | } |
287 | |
288 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
289 | enum map_type type, u64 addr, |
290 | struct map **mapp, |
291 | symbol_filter_t filter) |
292 | { |
293 | struct map *map = map_groups__find(self, type, addr); |
294 | |
295 | if (map != NULL) { |
296 | if (mapp != NULL) |
297 | *mapp = map; |
298 | return map__find_symbol(map, map->map_ip(map, addr), filter); |
299 | } |
300 | |
301 | return NULL; |
302 | } |
303 | |
304 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, |
305 | enum map_type type, |
306 | const char *name, |
307 | struct map **mapp, |
308 | symbol_filter_t filter) |
309 | { |
310 | struct rb_node *nd; |
311 | |
312 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
313 | struct map *pos = rb_entry(nd, struct map, rb_node); |
314 | struct symbol *sym = map__find_symbol_by_name(pos, name, filter); |
315 | |
316 | if (sym == NULL) |
317 | continue; |
318 | if (mapp != NULL) |
319 | *mapp = pos; |
320 | return sym; |
321 | } |
322 | |
323 | return NULL; |
324 | } |
325 | |
326 | size_t __map_groups__fprintf_maps(struct map_groups *self, |
327 | enum map_type type, int verbose, FILE *fp) |
328 | { |
329 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); |
330 | struct rb_node *nd; |
331 | |
332 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
333 | struct map *pos = rb_entry(nd, struct map, rb_node); |
334 | printed += fprintf(fp, "Map:"); |
335 | printed += map__fprintf(pos, fp); |
336 | if (verbose > 2) { |
337 | printed += dso__fprintf(pos->dso, type, fp); |
338 | printed += fprintf(fp, "--\n"); |
339 | } |
340 | } |
341 | |
342 | return printed; |
343 | } |
344 | |
345 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) |
346 | { |
347 | size_t printed = 0, i; |
348 | for (i = 0; i < MAP__NR_TYPES; ++i) |
349 | printed += __map_groups__fprintf_maps(self, i, verbose, fp); |
350 | return printed; |
351 | } |
352 | |
353 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, |
354 | enum map_type type, |
355 | int verbose, FILE *fp) |
356 | { |
357 | struct map *pos; |
358 | size_t printed = 0; |
359 | |
360 | list_for_each_entry(pos, &self->removed_maps[type], node) { |
361 | printed += fprintf(fp, "Map:"); |
362 | printed += map__fprintf(pos, fp); |
363 | if (verbose > 1) { |
364 | printed += dso__fprintf(pos->dso, type, fp); |
365 | printed += fprintf(fp, "--\n"); |
366 | } |
367 | } |
368 | return printed; |
369 | } |
370 | |
371 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, |
372 | int verbose, FILE *fp) |
373 | { |
374 | size_t printed = 0, i; |
375 | for (i = 0; i < MAP__NR_TYPES; ++i) |
376 | printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); |
377 | return printed; |
378 | } |
379 | |
380 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) |
381 | { |
382 | size_t printed = map_groups__fprintf_maps(self, verbose, fp); |
383 | printed += fprintf(fp, "Removed maps:\n"); |
384 | return printed + map_groups__fprintf_removed_maps(self, verbose, fp); |
385 | } |
386 | |
387 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, |
388 | int verbose, FILE *fp) |
389 | { |
390 | struct rb_root *root = &self->maps[map->type]; |
391 | struct rb_node *next = rb_first(root); |
392 | int err = 0; |
393 | |
394 | while (next) { |
395 | struct map *pos = rb_entry(next, struct map, rb_node); |
396 | next = rb_next(&pos->rb_node); |
397 | |
398 | if (!map__overlap(pos, map)) |
399 | continue; |
400 | |
401 | if (verbose >= 2) { |
402 | fputs("overlapping maps:\n", fp); |
403 | map__fprintf(map, fp); |
404 | map__fprintf(pos, fp); |
405 | } |
406 | |
407 | rb_erase(&pos->rb_node, root); |
408 | /* |
409 | * Now check if we need to create new maps for areas not |
410 | * overlapped by the new map: |
411 | */ |
412 | if (map->start > pos->start) { |
413 | struct map *before = map__clone(pos); |
414 | |
415 | if (before == NULL) { |
416 | err = -ENOMEM; |
417 | goto move_map; |
418 | } |
419 | |
420 | before->end = map->start - 1; |
421 | map_groups__insert(self, before); |
422 | if (verbose >= 2) |
423 | map__fprintf(before, fp); |
424 | } |
425 | |
426 | if (map->end < pos->end) { |
427 | struct map *after = map__clone(pos); |
428 | |
429 | if (after == NULL) { |
430 | err = -ENOMEM; |
431 | goto move_map; |
432 | } |
433 | |
434 | after->start = map->end + 1; |
435 | map_groups__insert(self, after); |
436 | if (verbose >= 2) |
437 | map__fprintf(after, fp); |
438 | } |
439 | move_map: |
440 | /* |
441 | * If we have references, just move them to a separate list. |
442 | */ |
443 | if (pos->referenced) |
444 | list_add_tail(&pos->node, &self->removed_maps[map->type]); |
445 | else |
446 | map__delete(pos); |
447 | |
448 | if (err) |
449 | return err; |
450 | } |
451 | |
452 | return 0; |
453 | } |
454 | |
455 | /* |
456 | * XXX This should not really _copy_ te maps, but refcount them. |
457 | */ |
458 | int map_groups__clone(struct map_groups *self, |
459 | struct map_groups *parent, enum map_type type) |
460 | { |
461 | struct rb_node *nd; |
462 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { |
463 | struct map *map = rb_entry(nd, struct map, rb_node); |
464 | struct map *new = map__clone(map); |
465 | if (new == NULL) |
466 | return -ENOMEM; |
467 | map_groups__insert(self, new); |
468 | } |
469 | return 0; |
470 | } |
471 | |
472 | static u64 map__reloc_map_ip(struct map *map, u64 ip) |
473 | { |
474 | return ip + (s64)map->pgoff; |
475 | } |
476 | |
477 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) |
478 | { |
479 | return ip - (s64)map->pgoff; |
480 | } |
481 | |
482 | void map__reloc_vmlinux(struct map *self) |
483 | { |
484 | struct kmap *kmap = map__kmap(self); |
485 | s64 reloc; |
486 | |
487 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) |
488 | return; |
489 | |
490 | reloc = (kmap->ref_reloc_sym->unrelocated_addr - |
491 | kmap->ref_reloc_sym->addr); |
492 | |
493 | if (!reloc) |
494 | return; |
495 | |
496 | self->map_ip = map__reloc_map_ip; |
497 | self->unmap_ip = map__reloc_unmap_ip; |
498 | self->pgoff = reloc; |
499 | } |
500 | |
501 | void maps__insert(struct rb_root *maps, struct map *map) |
502 | { |
503 | struct rb_node **p = &maps->rb_node; |
504 | struct rb_node *parent = NULL; |
505 | const u64 ip = map->start; |
506 | struct map *m; |
507 | |
508 | while (*p != NULL) { |
509 | parent = *p; |
510 | m = rb_entry(parent, struct map, rb_node); |
511 | if (ip < m->start) |
512 | p = &(*p)->rb_left; |
513 | else |
514 | p = &(*p)->rb_right; |
515 | } |
516 | |
517 | rb_link_node(&map->rb_node, parent, p); |
518 | rb_insert_color(&map->rb_node, maps); |
519 | } |
520 | |
521 | void maps__remove(struct rb_root *self, struct map *map) |
522 | { |
523 | rb_erase(&map->rb_node, self); |
524 | } |
525 | |
526 | struct map *maps__find(struct rb_root *maps, u64 ip) |
527 | { |
528 | struct rb_node **p = &maps->rb_node; |
529 | struct rb_node *parent = NULL; |
530 | struct map *m; |
531 | |
532 | while (*p != NULL) { |
533 | parent = *p; |
534 | m = rb_entry(parent, struct map, rb_node); |
535 | if (ip < m->start) |
536 | p = &(*p)->rb_left; |
537 | else if (ip > m->end) |
538 | p = &(*p)->rb_right; |
539 | else |
540 | return m; |
541 | } |
542 | |
543 | return NULL; |
544 | } |
545 | |
546 | int machine__init(struct machine *self, const char *root_dir, pid_t pid) |
547 | { |
548 | map_groups__init(&self->kmaps); |
549 | RB_CLEAR_NODE(&self->rb_node); |
550 | INIT_LIST_HEAD(&self->user_dsos); |
551 | INIT_LIST_HEAD(&self->kernel_dsos); |
552 | |
553 | self->kmaps.machine = self; |
554 | self->pid = pid; |
555 | self->root_dir = strdup(root_dir); |
556 | return self->root_dir == NULL ? -ENOMEM : 0; |
557 | } |
558 | |
559 | static void dsos__delete(struct list_head *self) |
560 | { |
561 | struct dso *pos, *n; |
562 | |
563 | list_for_each_entry_safe(pos, n, self, node) { |
564 | list_del(&pos->node); |
565 | dso__delete(pos); |
566 | } |
567 | } |
568 | |
569 | void machine__exit(struct machine *self) |
570 | { |
571 | map_groups__exit(&self->kmaps); |
572 | dsos__delete(&self->user_dsos); |
573 | dsos__delete(&self->kernel_dsos); |
574 | free(self->root_dir); |
575 | self->root_dir = NULL; |
576 | } |
577 | |
578 | void machine__delete(struct machine *self) |
579 | { |
580 | machine__exit(self); |
581 | free(self); |
582 | } |
583 | |
584 | struct machine *machines__add(struct rb_root *self, pid_t pid, |
585 | const char *root_dir) |
586 | { |
587 | struct rb_node **p = &self->rb_node; |
588 | struct rb_node *parent = NULL; |
589 | struct machine *pos, *machine = malloc(sizeof(*machine)); |
590 | |
591 | if (!machine) |
592 | return NULL; |
593 | |
594 | if (machine__init(machine, root_dir, pid) != 0) { |
595 | free(machine); |
596 | return NULL; |
597 | } |
598 | |
599 | while (*p != NULL) { |
600 | parent = *p; |
601 | pos = rb_entry(parent, struct machine, rb_node); |
602 | if (pid < pos->pid) |
603 | p = &(*p)->rb_left; |
604 | else |
605 | p = &(*p)->rb_right; |
606 | } |
607 | |
608 | rb_link_node(&machine->rb_node, parent, p); |
609 | rb_insert_color(&machine->rb_node, self); |
610 | |
611 | return machine; |
612 | } |
613 | |
614 | struct machine *machines__find(struct rb_root *self, pid_t pid) |
615 | { |
616 | struct rb_node **p = &self->rb_node; |
617 | struct rb_node *parent = NULL; |
618 | struct machine *machine; |
619 | struct machine *default_machine = NULL; |
620 | |
621 | while (*p != NULL) { |
622 | parent = *p; |
623 | machine = rb_entry(parent, struct machine, rb_node); |
624 | if (pid < machine->pid) |
625 | p = &(*p)->rb_left; |
626 | else if (pid > machine->pid) |
627 | p = &(*p)->rb_right; |
628 | else |
629 | return machine; |
630 | if (!machine->pid) |
631 | default_machine = machine; |
632 | } |
633 | |
634 | return default_machine; |
635 | } |
636 | |
637 | struct machine *machines__findnew(struct rb_root *self, pid_t pid) |
638 | { |
639 | char path[PATH_MAX]; |
640 | const char *root_dir; |
641 | struct machine *machine = machines__find(self, pid); |
642 | |
643 | if (!machine || machine->pid != pid) { |
644 | if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) |
645 | root_dir = ""; |
646 | else { |
647 | if (!symbol_conf.guestmount) |
648 | goto out; |
649 | sprintf(path, "%s/%d", symbol_conf.guestmount, pid); |
650 | if (access(path, R_OK)) { |
651 | pr_err("Can't access file %s\n", path); |
652 | goto out; |
653 | } |
654 | root_dir = path; |
655 | } |
656 | machine = machines__add(self, pid, root_dir); |
657 | } |
658 | |
659 | out: |
660 | return machine; |
661 | } |
662 | |
663 | void machines__process(struct rb_root *self, machine__process_t process, void *data) |
664 | { |
665 | struct rb_node *nd; |
666 | |
667 | for (nd = rb_first(self); nd; nd = rb_next(nd)) { |
668 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
669 | process(pos, data); |
670 | } |
671 | } |
672 | |
673 | char *machine__mmap_name(struct machine *self, char *bf, size_t size) |
674 | { |
675 | if (machine__is_host(self)) |
676 | snprintf(bf, size, "[%s]", "kernel.kallsyms"); |
677 | else if (machine__is_default_guest(self)) |
678 | snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); |
679 | else |
680 | snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid); |
681 | |
682 | return bf; |
683 | } |
684 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9