summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvik Sil <avik.sil@linaro.org>2011-06-16 12:13:28 +0530
committerAvik Sil <avik.sil@linaro.org>2011-06-16 12:13:28 +0530
commita36795846bf0de4ec0d7344b2f41c94b7317bb42 (patch)
treeb70243cbb1aa27e6efa2601f556c33f1e40bb6e4
SMARTT Release 0.5HEADmaster
-rw-r--r--smartt-client/Makefile10
-rw-r--r--smartt-client/README39
-rw-r--r--smartt-client/smartt-client.c370
-rw-r--r--smartt-client/smartt-client.conf6
-rw-r--r--smartt-client/smartt-client.h89
-rw-r--r--smartt-client/smartt-network.c102
-rw-r--r--smartt-client/smartt-network.h30
-rw-r--r--smartt-client/smartt-networks.conf4
-rw-r--r--smartt-client/smartt-userconfig.c214
-rw-r--r--smartt-client/smartt-userconfig.h59
-rw-r--r--smartt-client/smartt-utils.c58
-rw-r--r--smartt-client/smartt-utils.h58
-rw-r--r--smartt-perf/.gitignore22
-rw-r--r--smartt-perf/CREDITS30
-rw-r--r--smartt-perf/Documentation/Makefile302
-rw-r--r--smartt-perf/Documentation/asciidoc.conf91
-rw-r--r--smartt-perf/Documentation/examples.txt225
-rw-r--r--smartt-perf/Documentation/manpage-1.72.xsl14
-rw-r--r--smartt-perf/Documentation/manpage-base.xsl35
-rw-r--r--smartt-perf/Documentation/manpage-bold-literal.xsl17
-rw-r--r--smartt-perf/Documentation/manpage-normal.xsl13
-rw-r--r--smartt-perf/Documentation/manpage-suppress-sp.xsl21
-rw-r--r--smartt-perf/Documentation/perf-annotate.txt71
-rw-r--r--smartt-perf/Documentation/perf-archive.txt22
-rw-r--r--smartt-perf/Documentation/perf-bench.txt120
-rw-r--r--smartt-perf/Documentation/perf-buildid-cache.txt33
-rw-r--r--smartt-perf/Documentation/perf-buildid-list.txt37
-rw-r--r--smartt-perf/Documentation/perf-diff.txt74
-rw-r--r--smartt-perf/Documentation/perf-help.txt38
-rw-r--r--smartt-perf/Documentation/perf-inject.txt35
-rw-r--r--smartt-perf/Documentation/perf-kmem.txt47
-rw-r--r--smartt-perf/Documentation/perf-kvm.txt74
-rw-r--r--smartt-perf/Documentation/perf-list.txt73
-rw-r--r--smartt-perf/Documentation/perf-lock.txt44
-rw-r--r--smartt-perf/Documentation/perf-probe.txt166
-rw-r--r--smartt-perf/Documentation/perf-record.txt142
-rw-r--r--smartt-perf/Documentation/perf-report.txt124
-rw-r--r--smartt-perf/Documentation/perf-sched.txt55
-rw-r--r--smartt-perf/Documentation/perf-script-perl.txt217
-rw-r--r--smartt-perf/Documentation/perf-script-python.txt623
-rw-r--r--smartt-perf/Documentation/perf-script.txt118
-rw-r--r--smartt-perf/Documentation/perf-stat.txt106
-rw-r--r--smartt-perf/Documentation/perf-test.txt22
-rw-r--r--smartt-perf/Documentation/perf-timechart.txt46
-rw-r--r--smartt-perf/Documentation/perf-top.txt147
-rw-r--r--smartt-perf/Documentation/perf-trace-perl.txt217
-rw-r--r--smartt-perf/Documentation/perf-trace-python.txt623
-rw-r--r--smartt-perf/Documentation/perf-trace.txt70
-rw-r--r--smartt-perf/Documentation/perf.txt24
-rw-r--r--smartt-perf/MANIFEST13
-rw-r--r--smartt-perf/Makefile1309
-rw-r--r--smartt-perf/arch/arm/Makefile4
-rw-r--r--smartt-perf/arch/arm/util/dwarf-regs.c64
-rw-r--r--smartt-perf/arch/powerpc/Makefile4
-rw-r--r--smartt-perf/arch/powerpc/util/dwarf-regs.c88
-rw-r--r--smartt-perf/arch/s390/Makefile4
-rw-r--r--smartt-perf/arch/s390/util/dwarf-regs.c22
-rw-r--r--smartt-perf/arch/sh/Makefile4
-rw-r--r--smartt-perf/arch/sh/util/dwarf-regs.c55
-rw-r--r--smartt-perf/arch/sparc/Makefile4
-rw-r--r--smartt-perf/arch/sparc/util/dwarf-regs.c43
-rw-r--r--smartt-perf/arch/x86/Makefile4
-rw-r--r--smartt-perf/arch/x86/util/dwarf-regs.c75
-rw-r--r--smartt-perf/bench/bench.h17
-rw-r--r--smartt-perf/bench/mem-memcpy-arch.h12
-rw-r--r--smartt-perf/bench/mem-memcpy-x86-64-asm-def.h4
-rw-r--r--smartt-perf/bench/mem-memcpy-x86-64-asm.S2
-rw-r--r--smartt-perf/bench/mem-memcpy.c297
-rw-r--r--smartt-perf/bench/sched-messaging.c336
-rw-r--r--smartt-perf/bench/sched-pipe.c127
-rw-r--r--smartt-perf/builtin-annotate.c481
-rw-r--r--smartt-perf/builtin-bench.c245
-rw-r--r--smartt-perf/builtin-buildid-cache.c132
-rw-r--r--smartt-perf/builtin-buildid-list.c60
-rw-r--r--smartt-perf/builtin-diff.c232
-rw-r--r--smartt-perf/builtin-help.c459
-rw-r--r--smartt-perf/builtin-inject.c237
-rw-r--r--smartt-perf/builtin-kmem.c777
-rw-r--r--smartt-perf/builtin-kvm.c144
-rw-r--r--smartt-perf/builtin-list.c21
-rw-r--r--smartt-perf/builtin-lock.c1004
-rw-r--r--smartt-perf/builtin-probe.c330
-rw-r--r--smartt-perf/builtin-record.c975
-rw-r--r--smartt-perf/builtin-report.c551
-rw-r--r--smartt-perf/builtin-sched.c1921
-rw-r--r--smartt-perf/builtin-script.c821
-rw-r--r--smartt-perf/builtin-stat.c758
-rw-r--r--smartt-perf/builtin-test.c507
-rw-r--r--smartt-perf/builtin-timechart.c1104
-rw-r--r--smartt-perf/builtin-top.c1497
-rw-r--r--smartt-perf/builtin-trace.c717
-rw-r--r--smartt-perf/builtin.h39
-rw-r--r--smartt-perf/command-list.txt24
-rw-r--r--smartt-perf/design.txt462
-rw-r--r--smartt-perf/feature-tests.mak130
-rw-r--r--smartt-perf/perf-archive.sh46
-rw-r--r--smartt-perf/perf.c508
-rw-r--r--smartt-perf/perf.h149
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/Context.c135
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/Context.xs42
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/Makefile.PL17
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/README59
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm55
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm192
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm94
-rw-r--r--smartt-perf/scripts/perl/Perf-Trace-Util/typemap1
-rw-r--r--smartt-perf/scripts/perl/bin/check-perf-trace-record2
-rw-r--r--smartt-perf/scripts/perl/bin/failed-syscalls-record2
-rw-r--r--smartt-perf/scripts/perl/bin/failed-syscalls-report10
-rw-r--r--smartt-perf/scripts/perl/bin/rw-by-file-record3
-rw-r--r--smartt-perf/scripts/perl/bin/rw-by-file-report10
-rw-r--r--smartt-perf/scripts/perl/bin/rw-by-pid-record2
-rw-r--r--smartt-perf/scripts/perl/bin/rw-by-pid-report3
-rw-r--r--smartt-perf/scripts/perl/bin/rwtop-record2
-rw-r--r--smartt-perf/scripts/perl/bin/rwtop-report20
-rw-r--r--smartt-perf/scripts/perl/bin/wakeup-latency-record6
-rw-r--r--smartt-perf/scripts/perl/bin/wakeup-latency-report3
-rw-r--r--smartt-perf/scripts/perl/bin/workqueue-stats-record2
-rw-r--r--smartt-perf/scripts/perl/bin/workqueue-stats-report3
-rw-r--r--smartt-perf/scripts/perl/check-perf-trace.pl106
-rw-r--r--smartt-perf/scripts/perl/failed-syscalls.pl42
-rw-r--r--smartt-perf/scripts/perl/rw-by-file.pl106
-rw-r--r--smartt-perf/scripts/perl/rw-by-pid.pl184
-rw-r--r--smartt-perf/scripts/perl/rwtop.pl199
-rw-r--r--smartt-perf/scripts/perl/wakeup-latency.pl107
-rw-r--r--smartt-perf/scripts/perl/workqueue-stats.pl129
-rw-r--r--smartt-perf/scripts/python/Perf-Trace-Util/Context.c88
-rw-r--r--smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py121
-rw-r--r--smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py184
-rw-r--r--smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py86
-rw-r--r--smartt-perf/scripts/python/bin/failed-syscalls-by-pid-record2
-rw-r--r--smartt-perf/scripts/python/bin/failed-syscalls-by-pid-report10
-rw-r--r--smartt-perf/scripts/python/bin/futex-contention-record2
-rw-r--r--smartt-perf/scripts/python/bin/futex-contention-report4
-rw-r--r--smartt-perf/scripts/python/bin/netdev-times-record8
-rw-r--r--smartt-perf/scripts/python/bin/netdev-times-report5
-rw-r--r--smartt-perf/scripts/python/bin/sched-migration-record2
-rw-r--r--smartt-perf/scripts/python/bin/sched-migration-report3
-rw-r--r--smartt-perf/scripts/python/bin/sctop-record2
-rw-r--r--smartt-perf/scripts/python/bin/sctop-report24
-rw-r--r--smartt-perf/scripts/python/bin/syscall-counts-by-pid-record2
-rw-r--r--smartt-perf/scripts/python/bin/syscall-counts-by-pid-report10
-rw-r--r--smartt-perf/scripts/python/bin/syscall-counts-record2
-rw-r--r--smartt-perf/scripts/python/bin/syscall-counts-report10
-rw-r--r--smartt-perf/scripts/python/check-perf-trace.py82
-rw-r--r--smartt-perf/scripts/python/failed-syscalls-by-pid.py73
-rw-r--r--smartt-perf/scripts/python/futex-contention.py50
-rw-r--r--smartt-perf/scripts/python/netdev-times.py464
-rw-r--r--smartt-perf/scripts/python/sched-migration.py461
-rw-r--r--smartt-perf/scripts/python/sctop.py75
-rw-r--r--smartt-perf/scripts/python/syscall-counts-by-pid.py69
-rw-r--r--smartt-perf/scripts/python/syscall-counts.py59
-rwxr-xr-xsmartt-perf/util/PERF-VERSION-GEN47
-rw-r--r--smartt-perf/util/abspath.c37
-rw-r--r--smartt-perf/util/alias.c77
-rw-r--r--smartt-perf/util/bitmap.c21
-rw-r--r--smartt-perf/util/build-id.c80
-rw-r--r--smartt-perf/util/build-id.h10
-rw-r--r--smartt-perf/util/cache.h89
-rw-r--r--smartt-perf/util/callchain.c464
-rw-r--r--smartt-perf/util/callchain.h75
-rw-r--r--smartt-perf/util/color.c324
-rw-r--r--smartt-perf/util/color.h46
-rw-r--r--smartt-perf/util/config.c492
-rw-r--r--smartt-perf/util/connect.c54
-rw-r--r--smartt-perf/util/connect.h24
-rw-r--r--smartt-perf/util/cpumap.c179
-rw-r--r--smartt-perf/util/cpumap.h13
-rw-r--r--smartt-perf/util/ctype.c39
-rw-r--r--smartt-perf/util/debug.c94
-rw-r--r--smartt-perf/util/debug.h40
-rw-r--r--smartt-perf/util/debugfs.c240
-rw-r--r--smartt-perf/util/debugfs.h25
-rw-r--r--smartt-perf/util/environment.c9
-rw-r--r--smartt-perf/util/event.c961
-rw-r--r--smartt-perf/util/event.h179
-rw-r--r--smartt-perf/util/evsel.c201
-rw-r--r--smartt-perf/util/evsel.h115
-rw-r--r--smartt-perf/util/exec_cmd.c167
-rw-r--r--smartt-perf/util/exec_cmd.h12
-rwxr-xr-xsmartt-perf/util/generate-cmdlist.sh24
-rw-r--r--smartt-perf/util/header.c1237
-rw-r--r--smartt-perf/util/header.h128
-rw-r--r--smartt-perf/util/help.c338
-rw-r--r--smartt-perf/util/help.h29
-rw-r--r--smartt-perf/util/hist.c1193
-rw-r--r--smartt-perf/util/hist.h150
-rw-r--r--smartt-perf/util/hweight.c31
-rw-r--r--smartt-perf/util/include/arch/arm/include/asm/unistd.h479
-rw-r--r--smartt-perf/util/include/arch/ia64/include/asm/break.h32
-rw-r--r--smartt-perf/util/include/arch/ia64/include/asm/unistd.h377
-rw-r--r--smartt-perf/util/include/arch/powerpc/include/asm/unistd.h426
-rw-r--r--smartt-perf/util/include/arch/x86/include/asm/unistd.h13
-rw-r--r--smartt-perf/util/include/arch/x86/include/asm/unistd_32.h393
-rw-r--r--smartt-perf/util/include/arch/x86/include/asm/unistd_64.h715
-rw-r--r--smartt-perf/util/include/arch/x86/lib/memcpy_64.S191
-rw-r--r--smartt-perf/util/include/asm/arm/unistd.h475
-rw-r--r--smartt-perf/util/include/asm/asm-offsets.h1
-rw-r--r--smartt-perf/util/include/asm/bug.h22
-rw-r--r--smartt-perf/util/include/asm/byteorder.h2
-rw-r--r--smartt-perf/util/include/asm/cpufeature.h9
-rw-r--r--smartt-perf/util/include/asm/dwarf2.h11
-rw-r--r--smartt-perf/util/include/asm/hweight.h8
-rw-r--r--smartt-perf/util/include/asm/ia64/unistd.h376
-rw-r--r--smartt-perf/util/include/asm/swab.h1
-rw-r--r--smartt-perf/util/include/asm/system.h1
-rw-r--r--smartt-perf/util/include/asm/uaccess.h14
-rw-r--r--smartt-perf/util/include/asm/x86/unistd.h13
-rw-r--r--smartt-perf/util/include/asm/x86/unistd_32.h390
-rw-r--r--smartt-perf/util/include/asm/x86/unistd_64.h709
-rw-r--r--smartt-perf/util/include/dwarf-regs.h8
-rw-r--r--smartt-perf/util/include/linux/bitmap.h35
-rw-r--r--smartt-perf/util/include/linux/bitops.h33
-rw-r--r--smartt-perf/util/include/linux/compiler.h12
-rw-r--r--smartt-perf/util/include/linux/ctype.h1
-rw-r--r--smartt-perf/util/include/linux/hash.h70
-rw-r--r--smartt-perf/util/include/linux/hw_breakpoint.h150
-rw-r--r--smartt-perf/util/include/linux/kernel.h111
-rw-r--r--smartt-perf/util/include/linux/linkage.h13
-rw-r--r--smartt-perf/util/include/linux/list.h751
-rw-r--r--smartt-perf/util/include/linux/magic.h63
-rw-r--r--smartt-perf/util/include/linux/module.h6
-rw-r--r--smartt-perf/util/include/linux/perf_event.h1180
-rw-r--r--smartt-perf/util/include/linux/poison.h89
-rw-r--r--smartt-perf/util/include/linux/prefetch.h6
-rw-r--r--smartt-perf/util/include/linux/rbtree.h169
-rw-r--r--smartt-perf/util/include/linux/string.h1
-rw-r--r--smartt-perf/util/include/linux/stringify.h12
-rw-r--r--smartt-perf/util/include/linux/swab.h299
-rw-r--r--smartt-perf/util/include/linux/types.h21
-rw-r--r--smartt-perf/util/levenshtein.c84
-rw-r--r--smartt-perf/util/levenshtein.h8
-rw-r--r--smartt-perf/util/map.c683
-rw-r--r--smartt-perf/util/map.h237
-rw-r--r--smartt-perf/util/newt.c1178
-rw-r--r--smartt-perf/util/pager.c96
-rw-r--r--smartt-perf/util/parse-events.c1013
-rw-r--r--smartt-perf/util/parse-events.h44
-rw-r--r--smartt-perf/util/parse-options.c577
-rw-r--r--smartt-perf/util/parse-options.h192
-rw-r--r--smartt-perf/util/path.c157
-rw-r--r--smartt-perf/util/probe-event.c1915
-rw-r--r--smartt-perf/util/probe-event.h135
-rw-r--r--smartt-perf/util/probe-finder.c1861
-rw-r--r--smartt-perf/util/probe-finder.h91
-rw-r--r--smartt-perf/util/pstack.c75
-rw-r--r--smartt-perf/util/pstack.h14
-rw-r--r--smartt-perf/util/quote.c54
-rw-r--r--smartt-perf/util/quote.h29
-rw-r--r--smartt-perf/util/rbtree.c462
-rw-r--r--smartt-perf/util/run-command.c214
-rw-r--r--smartt-perf/util/run-command.h58
-rw-r--r--smartt-perf/util/scripting-engines/trace-event-perl.c565
-rw-r--r--smartt-perf/util/scripting-engines/trace-event-python.c594
-rw-r--r--smartt-perf/util/session.c1136
-rw-r--r--smartt-perf/util/session.h157
-rw-r--r--smartt-perf/util/sigchain.c52
-rw-r--r--smartt-perf/util/sigchain.h10
-rw-r--r--smartt-perf/util/sort.c345
-rw-r--r--smartt-perf/util/sort.h124
-rw-r--r--smartt-perf/util/strbuf.c133
-rw-r--r--smartt-perf/util/strbuf.h92
-rw-r--r--smartt-perf/util/string.c296
-rw-r--r--smartt-perf/util/strlist.c200
-rw-r--r--smartt-perf/util/strlist.h78
-rw-r--r--smartt-perf/util/svghelper.c501
-rw-r--r--smartt-perf/util/svghelper.h28
-rw-r--r--smartt-perf/util/symbol.c2603
-rw-r--r--smartt-perf/util/symbol.h236
-rw-r--r--smartt-perf/util/thread.c195
-rw-r--r--smartt-perf/util/thread.h62
-rw-r--r--smartt-perf/util/trace-event-info.c565
-rw-r--r--smartt-perf/util/trace-event-parse.c3233
-rw-r--r--smartt-perf/util/trace-event-read.c539
-rw-r--r--smartt-perf/util/trace-event-scripting.c167
-rw-r--r--smartt-perf/util/trace-event.h299
-rw-r--r--smartt-perf/util/types.h19
-rw-r--r--smartt-perf/util/ui/browser.c337
-rw-r--r--smartt-perf/util/ui/browser.h51
-rw-r--r--smartt-perf/util/ui/browsers/annotate.c237
-rw-r--r--smartt-perf/util/ui/browsers/hists.c1013
-rw-r--r--smartt-perf/util/ui/browsers/map.c156
-rw-r--r--smartt-perf/util/ui/browsers/map.h6
-rw-r--r--smartt-perf/util/ui/helpline.c69
-rw-r--r--smartt-perf/util/ui/helpline.h11
-rw-r--r--smartt-perf/util/ui/libslang.h27
-rw-r--r--smartt-perf/util/ui/progress.c60
-rw-r--r--smartt-perf/util/ui/progress.h11
-rw-r--r--smartt-perf/util/ui/setup.c42
-rw-r--r--smartt-perf/util/ui/util.c127
-rw-r--r--smartt-perf/util/ui/util.h10
-rw-r--r--smartt-perf/util/usage.c80
-rw-r--r--smartt-perf/util/util.c133
-rw-r--r--smartt-perf/util/util.h273
-rw-r--r--smartt-perf/util/values.c231
-rw-r--r--smartt-perf/util/values.h27
-rw-r--r--smartt-perf/util/wrapper.c40
-rw-r--r--smartt-perf/util/xyarray.c20
-rw-r--r--smartt-perf/util/xyarray.h20
-rw-r--r--smartt-player/Makefile26
-rw-r--r--smartt-player/README12
-rw-r--r--smartt-player/main.c89
-rw-r--r--smartt-player/media_player.c246
-rw-r--r--smartt-player/media_player.h49
-rw-r--r--smartt-server/Makefile10
-rw-r--r--smartt-server/README21
l---------smartt-server/perf1
-rw-r--r--smartt-server/smartt-network-services.c146
-rw-r--r--smartt-server/smartt-network-services.h24
-rw-r--r--smartt-server/smartt-server-utils.c74
-rw-r--r--smartt-server/smartt-server-utils.h43
-rw-r--r--smartt-server/smartt-server.c249
-rw-r--r--smartt-server/smartt-server.conf1
-rw-r--r--smartt-server/smartt-server.config1
-rw-r--r--smartt-server/smartt-server.h89
l---------smartt-server/top1
-rw-r--r--smartt-top/AUTHORS51
-rw-r--r--smartt-top/BUGS74
-rw-r--r--smartt-top/COPYING340
-rw-r--r--smartt-top/COPYING.LIB481
-rw-r--r--smartt-top/CodingStyle101
-rw-r--r--smartt-top/Makefile262
-rw-r--r--smartt-top/NEWS368
-rw-r--r--smartt-top/README72
-rw-r--r--smartt-top/README.top545
-rw-r--r--smartt-top/TODO151
-rw-r--r--smartt-top/debian/NEWS8
-rw-r--r--smartt-top/debian/README.Debian35
-rw-r--r--smartt-top/debian/README.sysctl13
-rw-r--r--smartt-top/debian/changelog1534
-rw-r--r--smartt-top/debian/compat1
-rw-r--r--smartt-top/debian/control36
-rw-r--r--smartt-top/debian/copyright63
-rw-r--r--smartt-top/debian/docs4
-rw-r--r--smartt-top/debian/examples2
-rw-r--r--smartt-top/debian/libproc-dev.README12
-rw-r--r--smartt-top/debian/libproc-dev.dirs2
-rw-r--r--smartt-top/debian/libproc-dev.install3
-rw-r--r--smartt-top/debian/patches/00list53
-rw-r--r--smartt-top/debian/patches/10_ps.1.patch62
-rw-r--r--smartt-top/debian/patches/complain_unmounted_proc.patch19
-rw-r--r--smartt-top/debian/patches/free.1.patch108
-rw-r--r--smartt-top/debian/patches/gnu-kbsd-version.patch36
-rw-r--r--smartt-top/debian/patches/kill.1.patch147
-rw-r--r--smartt-top/debian/patches/kill_warncr.patch25
-rw-r--r--smartt-top/debian/patches/library_map_freeproc.patch15
-rw-r--r--smartt-top/debian/patches/makefile_dev_null.patch13
-rw-r--r--smartt-top/debian/patches/module_mk_shared.patch15
-rw-r--r--smartt-top/debian/patches/output_sort_time.patch93
-rw-r--r--smartt-top/debian/patches/path_max.patch76
-rw-r--r--smartt-top/debian/patches/pgrep.1.patch208
-rw-r--r--smartt-top/debian/patches/pgrep_c_option.patch85
-rw-r--r--smartt-top/debian/patches/pgrep_start_time.patch16
-rw-r--r--smartt-top/debian/patches/pgrep_usage_exitcode.patch15
-rw-r--r--smartt-top/debian/patches/pmap.1.patch48
-rw-r--r--smartt-top/debian/patches/pmaps_smaps.patch141
-rw-r--r--smartt-top/debian/patches/proc_version_constructor.patch37
-rw-r--r--smartt-top/debian/patches/ps_1_flt_output.patch30
-rw-r--r--smartt-top/debian/patches/ps_1_options.patch548
-rw-r--r--smartt-top/debian/patches/ps_cgroup_display.patch157
-rw-r--r--smartt-top/debian/patches/ps_size_sz.patch29
-rw-r--r--smartt-top/debian/patches/ps_supgid_display.patch446
-rw-r--r--smartt-top/debian/patches/readproc_c.patch25
-rw-r--r--smartt-top/debian/patches/readproc_double_free.patch21
-rw-r--r--smartt-top/debian/patches/series68
-rw-r--r--smartt-top/debian/patches/skill.1.patch116
-rw-r--r--smartt-top/debian/patches/skill_multiarg.patch18
-rw-r--r--smartt-top/debian/patches/skill_null_argv.patch14
-rw-r--r--smartt-top/debian/patches/skill_perror.patch16
-rw-r--r--smartt-top/debian/patches/slabtop_1.patch97
-rw-r--r--smartt-top/debian/patches/slabtop_once.patch116
-rw-r--r--smartt-top/debian/patches/sysctl.8.patch147
-rw-r--r--smartt-top/debian/patches/sysctl_options.patch46
-rw-r--r--smartt-top/debian/patches/sysinfo_7_numbers.patch36
-rw-r--r--smartt-top/debian/patches/sysinfo_elfnote.patch23
-rw-r--r--smartt-top/debian/patches/sysinfo_kfreebsd_hertz.patch20
-rw-r--r--smartt-top/debian/patches/tload.1.patch15
-rw-r--r--smartt-top/debian/patches/tload_no_optargs.patch16
-rw-r--r--smartt-top/debian/patches/top.1.patch416
-rw-r--r--smartt-top/debian/patches/top.1_cpustates.patch50
-rw-r--r--smartt-top/debian/patches/top_1_swap.patch23
-rw-r--r--smartt-top/debian/patches/top_c_resize.patch75
-rw-r--r--smartt-top/debian/patches/top_highlight.patch19
-rw-r--r--smartt-top/debian/patches/top_irix.patch44
-rw-r--r--smartt-top/debian/patches/top_mintime.patch16
-rw-r--r--smartt-top/debian/patches/top_no_openproc.patch21
-rw-r--r--smartt-top/debian/patches/top_nohz.patch14
-rw-r--r--smartt-top/debian/patches/top_numeric_args.patch98
-rw-r--r--smartt-top/debian/patches/top_stdin_eof.patch20
-rw-r--r--smartt-top/debian/patches/top_uid_length.patch16
-rw-r--r--smartt-top/debian/patches/top_username_parse.patch15
-rw-r--r--smartt-top/debian/patches/uptime.1.patch48
-rw-r--r--smartt-top/debian/patches/vmstat.8.patch109
-rw-r--r--smartt-top/debian/patches/vmstat_headers.patch148
-rw-r--r--smartt-top/debian/patches/vmstat_part_format.patch77
-rw-r--r--smartt-top/debian/patches/vmstat_units41
-rw-r--r--smartt-top/debian/patches/vmstat_units.patch52
-rw-r--r--smartt-top/debian/patches/w-bassman.patch88
-rw-r--r--smartt-top/debian/patches/w.1.patch70
-rw-r--r--smartt-top/debian/patches/w_columns.patch30
-rw-r--r--smartt-top/debian/patches/w_envlength.patch173
-rw-r--r--smartt-top/debian/patches/w_time.patch16
-rw-r--r--smartt-top/debian/patches/w_userproc.patch15
-rw-r--r--smartt-top/debian/patches/watch.1.patch103
-rw-r--r--smartt-top/debian/patches/watch_8bitchar.patch15
-rw-r--r--smartt-top/debian/patches/watch_ansi_colour.patch180
-rw-r--r--smartt-top/debian/patches/watch_exec_beep.patch259
-rw-r--r--smartt-top/debian/patches/watch_precision_time.patch216
-rw-r--r--smartt-top/debian/patches/watch_unicode.patch292
-rw-r--r--smartt-top/debian/postinst104
-rw-r--r--smartt-top/debian/postrm51
-rw-r--r--smartt-top/debian/preinst72
-rw-r--r--smartt-top/debian/prerm15
-rw-r--r--smartt-top/debian/procps.bug-presubj5
-rw-r--r--smartt-top/debian/procps.init.linux58
-rw-r--r--smartt-top/debian/procps.install8
-rw-r--r--smartt-top/debian/procps.install.hurd5
-rw-r--r--smartt-top/debian/procps.install.kfreebsd5
-rw-r--r--smartt-top/debian/procps.links2
-rw-r--r--smartt-top/debian/procps.lintian-overrides1
-rw-r--r--smartt-top/debian/procps.manpages2
-rw-r--r--smartt-top/debian/procps.menu4
-rwxr-xr-xsmartt-top/debian/rules109
-rw-r--r--smartt-top/debian/source/format1
-rw-r--r--smartt-top/debian/sysctl.conf60
-rw-r--r--smartt-top/debian/sysctl.d/10-console-messages.conf3
-rw-r--r--smartt-top/debian/sysctl.d/10-keyboard.conf.powerpc6
-rw-r--r--smartt-top/debian/sysctl.d/10-network-security.conf12
-rw-r--r--smartt-top/debian/sysctl.d/10-ptrace.conf22
-rw-r--r--smartt-top/debian/sysctl.d/10-zeropage.conf9
-rw-r--r--smartt-top/debian/sysctl.d/10-zeropage.conf.armel11
-rw-r--r--smartt-top/debian/sysctl.d/README8
-rw-r--r--smartt-top/debian/upstart13
-rw-r--r--smartt-top/debian/w.de.173
-rw-r--r--smartt-top/debian/watch6
-rw-r--r--smartt-top/dummy.c31
-rw-r--r--smartt-top/free.167
-rw-r--r--smartt-top/free.c122
-rw-r--r--smartt-top/kill.1106
-rw-r--r--smartt-top/minimal.c670
-rw-r--r--smartt-top/pgrep.1183
-rw-r--r--smartt-top/pgrep.c741
-rw-r--r--smartt-top/pkill.11
-rw-r--r--smartt-top/pmap.143
-rw-r--r--smartt-top/pmap.c422
-rw-r--r--smartt-top/proc/COPYING481
-rw-r--r--smartt-top/proc/alloc.c49
-rw-r--r--smartt-top/proc/alloc.h14
-rw-r--r--smartt-top/proc/devname.c329
-rw-r--r--smartt-top/proc/devname.h17
-rw-r--r--smartt-top/proc/escape.c216
-rw-r--r--smartt-top/proc/escape.h22
-rw-r--r--smartt-top/proc/ksym.c631
-rw-r--r--smartt-top/proc/library.map24
-rw-r--r--smartt-top/proc/module.mk130
-rw-r--r--smartt-top/proc/procps.h112
-rw-r--r--smartt-top/proc/pwcache.c77
-rw-r--r--smartt-top/proc/pwcache.h17
-rw-r--r--smartt-top/proc/readproc.c1128
-rw-r--r--smartt-top/proc/readproc.h265
-rw-r--r--smartt-top/proc/sig.c240
-rw-r--r--smartt-top/proc/sig.h30
-rw-r--r--smartt-top/proc/slab.c337
-rw-r--r--smartt-top/proc/slab.h39
-rw-r--r--smartt-top/proc/smaps.c171
-rw-r--r--smartt-top/proc/sysinfo.c942
-rw-r--r--smartt-top/proc/sysinfo.h135
-rw-r--r--smartt-top/proc/version.c58
-rw-r--r--smartt-top/proc/version.h32
-rw-r--r--smartt-top/proc/wchan.h16
-rw-r--r--smartt-top/proc/whattime.c87
-rw-r--r--smartt-top/proc/whattime.h13
-rw-r--r--smartt-top/procps.lsm15
-rw-r--r--smartt-top/procps.spec52
-rw-r--r--smartt-top/ps/COPYING481
-rw-r--r--smartt-top/ps/HACKING53
-rw-r--r--smartt-top/ps/TRANSLATION39
-rw-r--r--smartt-top/ps/common.h337
-rw-r--r--smartt-top/ps/display.c623
-rw-r--r--smartt-top/ps/global.c498
-rw-r--r--smartt-top/ps/help.c48
-rw-r--r--smartt-top/ps/it35
-rwxr-xr-xsmartt-top/ps/module.mk40
-rw-r--r--smartt-top/ps/output.c2054
-rwxr-xr-xsmartt-top/ps/p6
-rw-r--r--smartt-top/ps/parser.c1246
-rw-r--r--smartt-top/ps/ps.11541
-rw-r--r--smartt-top/ps/regression26
-rw-r--r--smartt-top/ps/select.c146
-rw-r--r--smartt-top/ps/sortformat.c955
-rw-r--r--smartt-top/pwdx.137
-rw-r--r--smartt-top/pwdx.c107
-rw-r--r--smartt-top/skill.1128
-rw-r--r--smartt-top/skill.c588
-rw-r--r--smartt-top/slabtop.1129
-rw-r--r--smartt-top/slabtop.c404
-rw-r--r--smartt-top/snice.11
-rw-r--r--smartt-top/sysctl.8117
-rw-r--r--smartt-top/sysctl.c524
-rw-r--r--smartt-top/sysctl.conf60
-rw-r--r--smartt-top/sysctl.conf.551
-rwxr-xr-xsmartt-top/t6
-rw-r--r--smartt-top/tload.150
-rw-r--r--smartt-top/tload.c153
-rw-r--r--smartt-top/top.11651
-rw-r--r--smartt-top/top.c3509
-rw-r--r--smartt-top/top.h586
-rw-r--r--smartt-top/uptime.147
-rw-r--r--smartt-top/uptime.c17
-rwxr-xr-xsmartt-top/v6
-rw-r--r--smartt-top/vmstat.8202
-rw-r--r--smartt-top/vmstat.c677
-rw-r--r--smartt-top/w.1108
-rw-r--r--smartt-top/w.c344
-rw-r--r--smartt-top/watch.1184
-rw-r--r--smartt-top/watch.c619
-rw-r--r--smartt-trace/README12
-rw-r--r--smartt-trace/smartt-trace.py271
518 files changed, 103410 insertions, 0 deletions
diff --git a/smartt-client/Makefile b/smartt-client/Makefile
new file mode 100644
index 0000000..f4b0710
--- /dev/null
+++ b/smartt-client/Makefile
@@ -0,0 +1,10 @@
+
+CC = gcc
+
+CFLAGS = -Wall
+
+smartt-client: smartt-client.c
+ $(CC) smartt-client.c -g -o smartt-client $(CFLAGS)
+
+clean:
+ rm -f *.o smartt-client
diff --git a/smartt-client/README b/smartt-client/README
new file mode 100644
index 0000000..18ead3e
--- /dev/null
+++ b/smartt-client/README
@@ -0,0 +1,39 @@
+DESCRIPTION: SMARTT-CLIENT receives the metrics from the smartt-server and displays on the standard
+ output. The client can be configured for the stat counter, samples and watermark levels
+ for each event. The tool creates the stat_table which is sent to the client for
+ tracing
+
+USAGE: smartt-client
+PROMPT [SMARTT-CLIENT] #
+
+COMMAND: r or R (run the program)
+OPTIONS: None
+
+COMMAND: conf [event] [config-param] [value]
+
+OPTION: {event} = cpu-cycles or cc
+ instructions or in
+ bus-cycles or bc
+ branches or br
+ cache-misses
+ all
+
+ {config-param} = -e (even_enable)
+ -s (samples)
+ -d (data counter)
+ -w (watermark level)
+
+ {value} = <value of the config-param>
+
+COMMAND stat -p user <pid_of_application>
+ stat -p player
+
+[SMARTT-CLIENT] # r
+
+[I] SMARTT Client Started : Success
+[I] Waiting SMARTT Server to Accept : Waiting
+[I] Accepted Connection from SMARTT Server : Success
+[I] Waiting PID from MM Player : Waiting
+
+
+
diff --git a/smartt-client/smartt-client.c b/smartt-client/smartt-client.c
new file mode 100644
index 0000000..416a405
--- /dev/null
+++ b/smartt-client/smartt-client.c
@@ -0,0 +1,370 @@
+/*
+* smartt-client.c
+*
+* System Metrics - Annotation Recording and Tracing Tool (SMARTT)
+*
+* This is a client application that displays cpu-cycles, bus-cycles, instructions,
+* cache-misses, branches along with the player data such as FPS, Drop Rate etc
+* The watemark levels for all the metrics can be set and are exclusively marked as
+* watermark highligted regions.
+*
+* Sample output:
+*
+*[ PID(s) ][ TIME-STAMP ][ CPU-CYCLES (w) ][ CACHE-M'SS (w) ][ BRANCHES (w) ]
+*
+* 3149 1298458764 5186403 3743842 (+) 1369802 (+)
+* 3149 1298458765 19817424 (+) 22701574 345118 (+)
+* 3149 1298458766 4727479 1447752 (+) 1168918
+*
+* Copyright (C) 2010>
+
+* Author(s):
+*
+* Sudip Jain <sudip.jain@linaro.org>
+* <sudip.jain@stericsson.com>
+*
+* Released under GPL v2
+*/
+
+#include "smartt-userconfig.c"
+#include "smartt-network.c"
+#include "smartt-utils.c"
+
+#include "smartt-client.h"
+#include "smartt-userconfig.h"
+#include "smartt-network.h"
+#include "smartt-utils.h"
+
+
+struct s_client_handler client_handler;
+struct s_report_manager report_manager;
+struct s_system_data system_data;
+//struct s_mm_data mm_data;
+//struct s_mem_info mem_info;
+
+int main()
+{
+
+ char line[1024]; /* the input line */
+ char *argv[64]; /* the command line argument */
+
+ SMARTTclient_Init();
+
+ while (1)
+ { /* repeat until done .... */
+ fprintf(stderr,"\n[SMARTT-CLIENT] # "); /* display a prompt */
+ fgets(line, sizeof(line),stdin); /* read in the command line */
+ smartt_util_parse(line, argv); /* parse the line */
+ if (strcmp(argv[0],"run")==0 || strcmp(argv[0],"r")==0)
+ SMARTTclient_Run();
+ if (strcmp(argv[0],"configure")==0 || strcmp(argv[0],"conf")==0)
+ SMARTTclient_SetUserConfig(&argv[1]);
+ if (strcmp(argv[0],"stat")==0)
+ SMARTTclient_SetUserPid(&argv[1]);
+ if (strcmp(argv[0], "exit") == 0 || strcmp(argv[0], "x") == 0 || strcmp(argv[0], "q") == 0)
+ exit(0); /* exit if it is */
+ }
+}
+
+void SMARTTclient_Run()
+{
+
+ char ack_msg[64];
+ int status, n;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "[I] SMARTT Client Started\t\t\t\t: Success\n");
+
+ sprintf(client_handler.msg_id, "REQ:CONNECT");
+ sendto(tx_sock_desc, (void *)&client_handler, sizeof(client_handler), 0,(struct sockaddr *)&Tx_server_addr, sizeof(Tx_server_addr));
+
+ fprintf(stderr, "[I] Waiting SMARTT Server to Accept \t\t\t: Waiting\n");
+ fflush(stdout);
+
+ n = recvfrom(rx_sock_desc, ack_msg, sizeof(ack_msg), 0, (struct sockaddr *)&Rx_client_addr, &addr_len);
+ if (strcmp(ack_msg,"ACK:ACCEPT") == 0)
+ fprintf(stderr, "[I] Accepted Connection from SMARTT Server\t\t: Success\n");
+ else
+ fprintf(stderr, "[I] Connection from SMARTT Server\t\t\t: Failed\n");
+
+ status = SMARTTclient_FilterSystemEvents();
+
+ fprintf(stderr, "[I] Enter the Pid to track (PID=0 for smartt-player)\t: ");
+ scanf("%u", &g_usr_pid);
+ if (g_usr_pid == 0)
+ client_handler.pid = 0;
+ else
+ client_handler.pid = g_usr_pid;
+
+ if (status == 1)
+ {
+ sprintf(client_handler.msg_id, "REQ:USER-CONFIG");
+ sendto(tx_sock_desc, (void *)&client_handler, sizeof(client_handler),0,(struct sockaddr *) &Tx_server_addr, sizeof(Tx_server_addr));
+ } else
+ {
+ fprintf(stderr, "[E] Error in user configuration\t\t: Failed\n");
+ }
+
+ n = recvfrom(rx_sock_desc, ack_msg, sizeof(ack_msg), 0, (struct sockaddr *)&Rx_client_addr, &addr_len);
+ if (strcmp(ack_msg,"ACK:USER-CONFIG") == 0)
+ fprintf(stderr, "[I] Configuring SMARTT Server\t\t\t\t: Success\n");
+ else
+ fprintf(stderr, "[I] Configuring SMARTT Server\t\t\t\t: Failed\n");
+
+ sleep(1);
+
+ SMARTTclient_ShowDisplay();
+ SMARTTclient_PrintHeaders();
+ sendto(tx_sock_desc, "REQ:CAPTURE", 64, 0,(struct sockaddr *)&Tx_server_addr, sizeof(Tx_server_addr));
+ SMARTTclient_PrintStats (client_handler.event_config.samples);
+ SMARTTclient_GenerateReport();
+}
+
+void SMARTTclient_ShowDisplay()
+{
+
+ smartt_util_exec("clear");
+ fprintf(stderr,"\n\t\t\t*************************************************************************************************************");
+ fprintf(stderr,"\n\t\t\t* *");
+ fprintf(stderr,"\n\t\t\t* System Metrics - Annotation Recording & Tracing Tool (SMARTT) *");
+ fprintf(stderr,"\n\t\t\t* *");
+ fprintf(stderr,"\n\t\t\t*************************************************************************************************************\n");
+ fprintf(stderr,"\n");
+}
+
+void SMARTTclient_PrintHeaders()
+{
+ char TableCols[1024];
+
+ TableCols[0] = '\0';
+ strcat(TableCols,"[ PID(s) ][ TIME-STAMP ]");
+
+ if (event_handler[0].event_enable)
+ strcat(TableCols,"[ CPU-CYCLES (w) ]");
+ if (event_handler[1].event_enable)
+ strcat(TableCols,"[ INSTRUCN'S (w) ]");
+ if (event_handler[2].event_enable)
+ strcat(TableCols,"[ BUS-CYCLES (w) ]");
+ if (event_handler[3].event_enable)
+ strcat(TableCols,"[ CACHE-M'SS (w) ]");
+ if (event_handler[4].event_enable)
+ strcat(TableCols,"[ BRANCHES (w) ]");
+ if (event_handler[5].event_enable)
+ strcat(TableCols,"[ MEM-USAGE (w) ]");
+
+ strcat(TableCols,"[ CU-FPS ]");
+ strcat(TableCols,"[ AV-FPS ]");
+ strcat(TableCols,"[ REND'D ]");
+ strcat(TableCols,"[ DROP'D ]");
+
+ fprintf(stderr, "%s\n",TableCols);
+ fflush(stdout);
+}
+
+
+void SMARTTclient_PrintStats(unsigned int sample_counter)
+{
+ unsigned int n;
+ char temp_str[64];
+ char stats_str[1024];
+
+ while (sample_counter)
+ {
+ n = recvfrom(rx_sock_desc, &system_data, sizeof(system_data), 0, (struct sockaddr *)&Rx_client_addr, &addr_len);
+
+ stats_str[0] = '\0';
+ fprintf(stderr,"\n");
+
+ strcat(stats_str, ADD_SPACES_2);
+ sprintf(temp_str,"%-6d",system_data.sys_info.pid);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+
+ sprintf(temp_str,"%-10ld",system_data.sys_info.time_stamp);
+ strcat(stats_str,temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+
+ if (event_handler[0].event_enable)
+ {
+ if ( system_data.sys_info.cpu_cycles < event_handler[0].watermark_level)
+ {
+ sprintf(temp_str,"cc %-10ld (.)",system_data.sys_info.cpu_cycles);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ } else
+ {
+ sprintf(temp_str,"cc %-10ld (+)",system_data.sys_info.cpu_cycles);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ }
+ }
+
+ if (event_handler[1].event_enable)
+ {
+ if (system_data.sys_info.instructions < event_handler[1].watermark_level)
+ {
+ sprintf(temp_str,"in %-10ld (.)",system_data.sys_info.instructions);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ } else
+ {
+ sprintf(temp_str,"in %-10ld (+)",system_data.sys_info.instructions);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ }
+
+ }
+
+ if (event_handler[2].event_enable)
+ {
+ if (system_data.sys_info.bus_cycles < event_handler[2].watermark_level)
+ {
+ sprintf(temp_str,"bc %-10ld (.)",system_data.sys_info.bus_cycles);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ } else
+ {
+ sprintf(temp_str,"bc %-10ld (+)",system_data.sys_info.bus_cycles);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ }
+ }
+
+ if (event_handler[3].event_enable)
+ {
+ if (system_data.sys_info.cache_misses < event_handler[3].watermark_level)
+ {
+ sprintf(temp_str,"cm %-10ld (.)",system_data.sys_info.cache_misses);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ } else
+ {
+ sprintf(temp_str,"cm %-10ld (+)",system_data.sys_info.cache_misses);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ }
+ }
+
+ if (event_handler[4].event_enable)
+ {
+ if (system_data.sys_info.branches < event_handler[4].watermark_level)
+ {
+ sprintf(temp_str,"br %-10ld (.)",system_data.sys_info.branches);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ } else
+ {
+ sprintf(temp_str,"br %-10ld (+)",system_data.sys_info.branches);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ }
+ }
+
+ if (event_handler[5].event_enable)
+ {
+ if (system_data.mem_info.main_used < event_handler[5].watermark_level)
+ {
+ sprintf(temp_str,"mu %-10ld (.)",system_data.mem_info.main_used);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ } else
+ {
+ sprintf(temp_str,"mu %-10ld (+)",system_data.mem_info.main_used);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+ }
+ }
+
+ sprintf(temp_str,"cf %-6.2lf",system_data.player_info.curfps);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_3);
+
+ sprintf(temp_str,"af %-6.2lf",system_data.player_info.avgfps);
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_4);
+
+ sprintf(temp_str,"re %-6lu",system_data.player_info.renderred );
+ strcat(stats_str, temp_str);
+ strcat(stats_str,ADD_SPACES_3);
+
+ sprintf(temp_str,"dr %-6lu",system_data.player_info.dropped);
+ strcat(stats_str, temp_str);
+
+ fprintf(stderr, "%s",stats_str);
+ sendto(trace_sock, stats_str, strlen(stats_str), 0, (struct sockaddr *)&TRACE_server_addr, sizeof(struct sockaddr));
+ fflush(stdout);
+ sample_counter--;
+ }
+}
+
+void SMARTTclient_GenerateReport()
+{
+
+ report_manager.stat_counter = client_handler.event_config.stat_counter;
+ report_manager.total_samples = client_handler.event_config.samples;
+
+ fprintf(stderr,"\n ########################################### REPORT GENERATION #############################################");
+ fprintf(stderr,"\n Report No : %04d Events Enabled : %04d ", ++report_manager.report_counter, report_manager.events_enabled);
+ fprintf(stderr,"\n Start Time : Total Samples : %04d ", report_manager.total_samples);
+ fprintf(stderr,"\n End Time : Stat Counter(secs) : %04.2lf ", report_manager.stat_counter);
+ fprintf(stderr,"\n Total Time : Watermark levels : ");
+ fprintf(stderr,"\n ###########################################################################################################\n");
+ fflush(stdout);
+}
+
+int SMARTTclient_FilterSystemEvents()
+{
+ int i;
+
+ for (i=0;i < NO_OF_EVENTS; i++)
+ {
+ if (event_handler[i].event_enable == 1)
+ {
+ client_handler.event_config.stat_counter = event_handler[i].stat_counter;
+ client_handler.event_config.samples = event_handler[i].sample_count;
+ break;
+ }
+ }
+
+ for (i = 0;i < NO_OF_EVENTS; i++)
+ {
+ client_handler.event_config.event_enable[i] = event_handler[i].event_enable;
+
+ if (client_handler.event_config.event_enable[i] == 1)
+ {
+ report_manager.events_enabled++;
+ if (client_handler.event_config.stat_counter > event_handler[i].stat_counter)
+ client_handler.event_config.stat_counter = event_handler[i].stat_counter;
+
+ if (client_handler.event_config.samples < event_handler[i].sample_count)
+ client_handler.event_config.samples = event_handler[i].sample_count;
+ }
+ }
+
+ return(1);
+}
+
+void SMARTTclient_Init()
+{
+ int i;
+
+ smartt_util_exec("clear");
+
+ for (i=0;i < NO_OF_EVENTS;i++)
+ {
+ event_handler[i].event_name = *(sys_events+i);
+ }
+ SMARTTclient_GetUserConfig();
+ SMARTTclient_GetNetworkConfig();
+
+ for (i=0;i < sizeof(local_host.host_ip) ;i++)
+ client_handler.local_client.host_ip[i] = local_host.host_ip[i];
+ client_handler.local_client.port_no = local_host.port_no;
+
+
+ SMARTTclient_OpenLocalServer();
+ SMARTTclient_OpenRemoteServer();
+ SMARTTclient_OpenTracePort();
+}
+
+
diff --git a/smartt-client/smartt-client.conf b/smartt-client/smartt-client.conf
new file mode 100644
index 0000000..f08f929
--- /dev/null
+++ b/smartt-client/smartt-client.conf
@@ -0,0 +1,6 @@
+CPU-CYCLES 1 1.00 10 100000000
+INSTRUCTIONS 1 1.00 10 50000000
+BUS-CYCLES 0 1.00 10 1000000
+CACHE-MISSES 1 1.00 10 100000000
+BRANCHES 0 1.00 10 5000000
+MEM-USAGE 1 1.00 10 950000
diff --git a/smartt-client/smartt-client.h b/smartt-client/smartt-client.h
new file mode 100644
index 0000000..f54d66a
--- /dev/null
+++ b/smartt-client/smartt-client.h
@@ -0,0 +1,89 @@
+#ifndef __SMARTT_CLIENT_H
+#define __SMARTT_CLIENT_H
+
+
+struct s_event_config
+{
+ int event_enable[NO_OF_EVENTS];
+ double stat_counter;
+ unsigned int samples;
+};
+
+struct s_channel_config
+{
+ char host_ip[16];
+ unsigned int port_no;
+};
+
+struct s_client_handler
+{
+ char msg_id[64];
+ unsigned int pid;
+ struct s_event_config event_config;
+ struct s_channel_config local_client;
+};
+
+struct s_report_manager
+{
+ unsigned int report_counter;
+ unsigned int events_enabled;
+ double stat_counter;
+ unsigned int total_samples;
+};
+
+struct s_sys_info
+{
+ unsigned int pid;
+ unsigned long int cpu_cycles;
+ unsigned long int instructions;
+ unsigned long int bus_cycles;
+ unsigned long int cache_misses;
+ unsigned long int branches;
+ unsigned long int time_stamp;
+};
+
+struct s_mem_info {
+ unsigned long int main_total;
+ unsigned long int main_used;
+ unsigned long int main_free;
+ unsigned long int main_buffers;
+ unsigned long int main_cached;
+ unsigned long int swap_total;
+ unsigned long int swap_used;
+ unsigned long int swap_free;
+};
+
+struct s_player_info
+{
+ unsigned int pid;
+ unsigned int rate;
+ unsigned int width ;
+ unsigned int height;
+ double curfps;
+ double dps;
+ double avgfps;
+ unsigned long int dropped;
+ unsigned long int renderred;
+ char vcodec[64];
+ char acodec[64];
+};
+
+struct s_system_data
+{
+ struct s_sys_info sys_info;
+ struct s_mem_info mem_info;
+ struct s_player_info player_info;
+};
+
+void SMARTTclient_Run();
+void SMARTTclient_ShowDisplay();
+void SMARTTclient_PrintHeaders();
+void SMARTTclient_PrintStats(unsigned int);
+void SMARTTclient_GenerateReport();
+int SMARTTclient_FilterSystemEvents();
+void SMARTTclient_Init();
+
+
+#endif /* __SMARTT-CLIENT_H */
+
+
diff --git a/smartt-client/smartt-network.c b/smartt-client/smartt-network.c
new file mode 100644
index 0000000..14a4ea0
--- /dev/null
+++ b/smartt-client/smartt-network.c
@@ -0,0 +1,102 @@
+/*
+ * smartt-network.c
+ *
+ * Perf manger
+*/
+
+#include "smartt-network.h"
+#include "smartt-utils.h"
+
+struct s_network_config local_host;
+struct s_network_config remote_server;
+struct s_network_config trace_server;
+
+int SMARTTclient_OpenLocalServer()
+{
+ bool status;
+
+ rx_sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ if (rx_sock_desc == -1)
+ {
+ fprintf(stderr,"[E] Error opening Rx Server\t\t: Error\n");
+ return(0);
+ }
+
+ Rx_server_addr.sin_family = AF_INET;
+ Rx_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ Rx_server_addr.sin_port = (local_host.port_no);
+
+ status = bind(rx_sock_desc,(struct sockaddr *)&Rx_server_addr, sizeof(Rx_server_addr));
+ if (status == -1)
+ {
+ fprintf(stderr, "[E] Failed to bind the client\t\t: Error\n");
+ return(0);
+ }
+
+ addr_len = sizeof(struct sockaddr);
+
+ return(1);
+}
+
+int SMARTTclient_OpenRemoteServer()
+{
+ struct hostent *hp;
+
+ tx_sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ if (tx_sock_desc == -1)
+ {
+ fprintf(stderr,"[E] Error opening Tx Server\t\t: Error\n");
+ return(0);
+ }
+
+ Tx_server_addr.sin_family = AF_INET;
+ Tx_server_addr.sin_port = (remote_server.port_no);
+
+ hp = gethostbyname(remote_server.host_ip);
+ bcopy (hp->h_addr,&(Tx_server_addr.sin_addr),hp->h_length);
+
+ return(1);
+}
+
+
+int SMARTTclient_OpenTracePort()
+{
+ struct hostent *hp;
+
+
+ if ((trace_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ {
+ perror("socket");
+ exit(1);
+ }
+
+ TRACE_server_addr.sin_family = AF_INET;
+ TRACE_server_addr.sin_port = (trace_server.port_no);
+
+ hp = gethostbyname(trace_server.host_ip);
+ bcopy (hp->h_addr,&(TRACE_server_addr.sin_addr),hp->h_length);
+
+ return(1);
+}
+
+int SMARTTclient_GetNetworkConfig()
+{
+ char param_type[64];
+
+ FILE *f1 = fopen("/etc/smartt/smartt-networks.conf","r");
+ if (f1== NULL)
+ {
+ fprintf(stderr, "[E] Cannot open /etc/smartt/smartt-networks.conf \t\t: Error\n");
+ return (0);
+ }
+
+ fscanf(f1,"%s %s\n",param_type, local_host.host_ip);
+ fscanf(f1,"%s %d\n",param_type, &local_host.port_no);
+ fscanf(f1,"%s %s\n",param_type, remote_server.host_ip);
+ fscanf(f1,"%s %d\n",param_type, &remote_server.port_no);
+ fscanf(f1,"%s %s\n",param_type, trace_server.host_ip);
+ fscanf(f1,"%s %d\n",param_type, &trace_server.port_no);
+
+ return(1);
+}
+
diff --git a/smartt-client/smartt-network.h b/smartt-client/smartt-network.h
new file mode 100644
index 0000000..ebe33a1
--- /dev/null
+++ b/smartt-client/smartt-network.h
@@ -0,0 +1,30 @@
+#ifndef __SMARTT_NETWORK_H
+#define __SMARTT_NETWORK_H
+
+#define SMARTT_TRACE_PORT 6000
+
+struct sockaddr_in Rx_server_addr, Rx_client_addr;
+struct sockaddr_in Tx_server_addr;
+struct sockaddr_in TRACE_server_addr;
+struct sockaddr_in MMP_Rx_server_addr;
+struct sockaddr_in MEMINFO_Rx_server_addr;
+
+unsigned int rx_sock_desc, tx_sock_desc;
+unsigned int mmp_rx_sock_desc;
+unsigned int mem_info_rx_sock_desc;
+unsigned int trace_sock;
+unsigned int addr_len;
+
+
+struct s_network_config
+{
+ char host_ip[16];
+ unsigned int port_no;
+};
+
+int SMARTTclient_OpenLocalServer();
+int SMARTTclient_OpenRemoteServer();
+int SMARTTclient_OpenTracePort();
+int SMARTTclient_GetNetworkConfig();
+
+#endif /*__SMARTT_NETWORK_H*/
diff --git a/smartt-client/smartt-networks.conf b/smartt-client/smartt-networks.conf
new file mode 100644
index 0000000..f4a69d7
--- /dev/null
+++ b/smartt-client/smartt-networks.conf
@@ -0,0 +1,4 @@
+SMARTT_Client_IP 10.199.210.120
+SMARTT_Client_Port 5000
+SMARTT_Server_IP 10.199.210.120
+SMARTT_Server_Port 5100
diff --git a/smartt-client/smartt-userconfig.c b/smartt-client/smartt-userconfig.c
new file mode 100644
index 0000000..0427a38
--- /dev/null
+++ b/smartt-client/smartt-userconfig.c
@@ -0,0 +1,214 @@
+/*
+ * smartt-userconfig.c
+ *
+ * User Configuration
+*/
+
+#include "smartt-userconfig.h"
+#include "smartt-utils.h"
+
+struct s_event_handler event_handler[NO_OF_EVENTS];
+unsigned int g_usr_pid;
+
+int SMARTTclient_SetUserConfig(char** conf_args)
+{
+ int i, args_error = 0;
+ char *event_name, *event_param;
+ double d_counter;
+ unsigned int event_id, event_enable;
+ unsigned int s_counter, wm_level;
+
+ event_name = *conf_args++;
+ event_param = *conf_args++;
+
+ if (strcmp(event_name,"default")==0)
+ {
+ SMARTTclient_SetConfig(CPU_CYCLES, EVENT_ENABLE, 2.0, 25, 40000);
+ SMARTTclient_SetConfig(INSTRUCTIONS, EVENT_ENABLE, 3.0, 25, 40000);
+ SMARTTclient_SetConfig(CACHE_MISSES, EVENT_ENABLE, 2.0, 25, 40000);
+ SMARTTclient_SetConfig(BRANCHES, EVENT_ENABLE, 2.0, 25, 40000);
+ SMARTTclient_SetConfig(BUS_CYCLES, EVENT_ENABLE, 2.0, 25, 40000);
+ } else
+ {
+
+ if ((strcmp(event_name,"cpu-cycles")==0) || (strcmp(event_name,"cc")==0))
+ event_id = 0;
+ else if ((strcmp(event_name,"instructions")==0) || (strcmp(event_name,"in")==0))
+ event_id = 1;
+ else if ((strcmp(event_name,"bus-cycles")==0) || (strcmp(event_name,"bc")==0))
+ event_id = 2;
+ else if ((strcmp(event_name,"cache-misses")==0) || (strcmp(event_name,"cm")==0))
+ event_id = 3;
+ else if ((strcmp(event_name,"branches")==0) || (strcmp(event_name,"br")==0))
+ event_id = 4;
+ else if (strcmp(event_name,"all")==0)
+ event_id = 64;
+ else
+ args_error = -1;
+
+ if (strcmp(event_param,"-e")==0)
+ {
+ sscanf(*conf_args,"%d", &event_enable);
+ if ((event_id==0)||(event_id==1)||(event_id==2)||(event_id==3)||(event_id==4))
+ event_handler[event_id].event_enable = event_enable;
+ else if ((strcmp(event_name,"all")==0))
+ {
+ for (i=0;i<=NO_OF_EVENTS;i++)
+ event_handler[i].event_enable = event_enable;
+ } else
+ args_error = -1;
+ }
+
+ if (strcmp(event_param,"-d")==0)
+ {
+ sscanf(*conf_args,"%lf", &d_counter);
+ if ((event_id==0) || (event_id==1)||(event_id==2)||(event_id==3)||(event_id==4))
+ event_handler[event_id].stat_counter = d_counter;
+ else if ((strcmp(event_name,"all")==0))
+ {
+ for (i=0;i<=NO_OF_EVENTS;i++)
+ event_handler[i].stat_counter = d_counter;
+ } else
+ args_error = -1;
+ }
+
+ if (strcmp(event_param,"-s")==0)
+ {
+ sscanf(*conf_args,"%d", &s_counter);
+ if ((event_id==0) || (event_id==1)||(event_id==2)||(event_id==3)||(event_id==4))
+ event_handler[event_id].sample_count = s_counter;
+ else if ((strcmp(event_name,"all")==0))
+ {
+ for (i=0;i<=NO_OF_EVENTS;i++)
+ event_handler[i].sample_count = s_counter;
+ } else
+ args_error = -1;
+ }
+ if (strcmp(event_param,"-w")==0)
+ {
+ sscanf(*conf_args,"%d", &wm_level);
+ if ((event_id==0) || (event_id==1)||(event_id==2)||(event_id==3)||(event_id==4))
+ event_handler[event_id].watermark_level = wm_level;
+ else if ((strcmp(event_name,"all")==0))
+ {
+ for (i=0;i<=NO_OF_EVENTS;i++)
+ event_handler[i].watermark_level = wm_level;
+ } else
+ args_error = -1;
+ }
+ }
+
+ if (args_error == -1)
+ {
+ fprintf(stderr, "[E] Incorrect command argument(s)\t\t: Error\n");
+ return(0);
+ }
+
+ SMARTTclient_SaveUserConfig();
+ fprintf(stderr,"[I] Configuration file 'smartt-user.config' saved\t\t: Success\n");
+ return(1);
+}
+
+void SMARTTclient_GetUserConfig()
+{
+ int i=0;
+ char event_name[40];
+ double d_counter;
+ int event_flag, s_count, wm_level;
+
+ FILE *f1 = fopen("/etc/smartt/smartt-client.conf","r");
+ if (f1== NULL)
+ {
+ fprintf(stderr, "[E] Cannot open /etc/smartt/smartt-client.conf \t\t: Error\n");
+ exit(1);
+ }
+ while (!feof(f1))
+ {
+ fscanf(f1,"%s %d %lf %d %d\n",event_name,&event_flag,&d_counter,&s_count, &wm_level);
+ event_handler[i].event_enable = (bool)event_flag;
+ event_handler[i].stat_counter = d_counter;
+ event_handler[i].sample_count = s_count;
+ event_handler[i].watermark_level = wm_level;
+ i++;
+ }
+
+ fclose(f1);
+}
+
+int SMARTTclient_SaveUserConfig()
+{
+ int i;
+
+ FILE *f1 = fopen("/etc/smartt/smartt-client.conf", "w");
+ if (f1== NULL)
+ {
+ fprintf(stderr, "[E] Cannot open /etc/smartt/smartt-client.conf file\t\t: Error\n");
+ exit (1);
+ }
+
+ for (i= 0;i < NO_OF_EVENTS;i++)
+ {
+ fprintf(f1,"%s%4d%8.2f%6d%12d\n",event_handler[i].event_name, event_handler[i].event_enable,
+ event_handler[i].stat_counter, event_handler[i].sample_count,
+ event_handler[i].watermark_level);
+ }
+
+ fclose(f1);
+
+ return(1);
+}
+
+int SMARTTclient_SetUserPid(char** conf_args)
+{
+ char *cmdline_idf, *cmdline_opt;
+
+ cmdline_idf = *conf_args++;
+ cmdline_opt = *conf_args++;
+
+ if (strcmp(cmdline_idf,"-p")==0)
+ {
+ if (strcmp(cmdline_opt,"player")==0)
+ g_usr_pid = 0;
+ else if (strcmp(cmdline_opt,"user")==0)
+ sscanf(*conf_args,"%d", &g_usr_pid);
+ }
+ else
+ {
+ fprintf(stderr, "[E] Incorrect command argument %s\t\t: Error\n",cmdline_idf);
+ return(0);
+ }
+
+ fflush(stdout);
+ return(1);
+
+
+
+//FILE *f1 = fopen("smartt-pid.list", "r");
+// if (f1== NULL)
+//{
+// fprintf(stderr, "[E] Can't open 'smartt-pid.list' file\t\t: Error\n");
+// return(1);
+//}
+//while (!feof(f1))
+//{
+// fscanf(f1,"%d\n",&user_pid);
+// i++;
+//}
+//
+//fclose(f1);
+
+}
+
+void SMARTTclient_SetConfig(t_event_id event, t_event_enable evt_enable,
+ t_stat_counter d_counter, t_sample_count s_count,
+ t_watermark_level wm_level)
+{
+ unsigned char event_id;
+ event_id = (t_uint8)event;
+ event_handler[event_id].event_enable = (bool)evt_enable;
+ event_handler[event_id].stat_counter = d_counter;
+ event_handler[event_id].sample_count = s_count;
+ event_handler[event_id].watermark_level = wm_level;
+}
+
+
diff --git a/smartt-client/smartt-userconfig.h b/smartt-client/smartt-userconfig.h
new file mode 100644
index 0000000..9444b6c
--- /dev/null
+++ b/smartt-client/smartt-userconfig.h
@@ -0,0 +1,59 @@
+
+#ifndef __SMARTT_USERCONFIG_H
+#define __SMARTT_USERCONFIG_H
+
+
+#define NO_OF_EVENTS 6
+
+typedef unsigned int t_pid;
+typedef double t_stat_counter;
+typedef unsigned int t_sample_count;
+typedef unsigned int t_watermark_level;
+
+struct s_event_handler
+{
+ const char *event_name;
+ int event_enable;
+ t_stat_counter stat_counter;
+ t_sample_count sample_count;
+ t_watermark_level watermark_level;
+};
+
+const char *sys_events[] = {
+ "CPU-CYCLES ",
+ "INSTRUCTIONS ",
+ "BUS-CYCLES ",
+ "CACHE-MISSES ",
+ "BRANCHES ",
+ "MEM-USAGE "
+};
+
+
+typedef enum
+{
+ CPU_CYCLES = 0,
+ INSTRUCTIONS = 1,
+ BUS_CYCLES = 2,
+ CACHE_MISSES = 3,
+ BRANCHES = 4,
+ MEM_USAGE = 5
+}t_event_id;
+
+
+typedef enum
+{
+ EVENT_DISABLE = 0,
+ EVENT_ENABLE = 1
+}t_event_enable;
+
+
+
+int SMARTTclient_SetUserConfig(char **conf_args);
+void SMARTTclient_GetUserConfig();
+int SMARTTclient_SaveUserConfig();
+void SMARTTclient_RestoreUserConfig();
+int SMARTTclient_SetUserPid(char **conf_args);
+void SMARTTclient_SetConfig(t_event_id, t_event_enable, t_stat_counter,
+ t_sample_count, t_watermark_level );
+
+#endif /*__SMARTT-USERCONFIG_H*/
diff --git a/smartt-client/smartt-utils.c b/smartt-client/smartt-utils.c
new file mode 100644
index 0000000..53914da
--- /dev/null
+++ b/smartt-client/smartt-utils.c
@@ -0,0 +1,58 @@
+
+/*
+ * smartt-util.c
+ *
+ * Utility functions
+*/
+
+#include "smartt-utils.h"
+
+void smartt_util_exec(char *line)
+{
+ char *argv[64]; /* the command line argument */
+ int i = 0;
+
+ if (strcmp(line,"clear")==0)
+ *(argv + i++) = "clear";
+ *(argv + i) = '\0';
+
+ smartt_util_execute(argv);
+}
+
+void smartt_util_parse(char *line, char **argv)
+{
+ while (*line != '\0') { /* if not the end of line ....... */
+ while (*line == ' ' || *line == '\t' || *line == '\n')
+ *line++ = '\0'; /* replace white spaces with 0 */
+ *argv++ = line; /* save the argument position */
+ while (*line != '\0' && *line != ' ' &&
+ *line != '\t' && *line != '\n')
+ line++; /* skip the argument until ... */
+ }
+ *argv = '\0'; /* mark the end of argument list */
+
+}
+
+void smartt_util_execute(char **argv)
+{
+ pid_t pid;
+ int status;
+
+ if ((pid = fork()) < 0) { /* fork a child process */
+ printf("*** ERROR: forking child process failed\n");
+ exit(1);
+ }
+ else if (pid == 0) { /* for the child process: */
+
+ if (execvp(*argv, argv) < 0) { /* execute the command */
+ printf("*** ERROR: exec failed\n");
+ exit(1);
+ }
+ }
+ else { /* for the parent: */
+ while (wait(&status) != pid) /* wait for completion */
+ ;
+ }
+}
+
+
diff --git a/smartt-client/smartt-utils.h b/smartt-client/smartt-utils.h
new file mode 100644
index 0000000..1f96c84
--- /dev/null
+++ b/smartt-client/smartt-utils.h
@@ -0,0 +1,58 @@
+#ifndef __SMARTT_UTILS_H
+#define __SMARTT_UTILS_H
+
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <inttypes.h>
+
+
+typedef signed char t_int8;
+typedef unsigned char t_uint8;
+
+
+#define ADD_SPACES_1 " "
+#define ADD_SPACES_2 " "
+#define ADD_SPACES_3 " "
+#define ADD_SPACES_4 " "
+#define ADD_SPACES_5 " "
+#define ADD_SPACES_6 " "
+#define ADD_SPACES_7 " "
+#define ADD_SPACES_8 " "
+#define ADD_SPACES_9 " "
+
+
+void smartt_util_exec(char *line);
+void smartt_util_parse(char *line, char **argv);
+void smartt_util_execute(char **argv);
+
+#endif /*__SMARTT_UTILS_H*/
diff --git a/smartt-perf/.gitignore b/smartt-perf/.gitignore
new file mode 100644
index 0000000..cb43289
--- /dev/null
+++ b/smartt-perf/.gitignore
@@ -0,0 +1,22 @@
+PERF-BUILD-OPTIONS
+PERF-CFLAGS
+PERF-GUI-VARS
+PERF-VERSION-FILE
+perf
+perf-help
+perf-record
+perf-report
+perf-stat
+perf-top
+perf*.1
+perf*.xml
+perf*.html
+common-cmds.h
+perf.data
+perf.data.old
+perf-archive
+tags
+TAGS
+cscope*
+config.mak
+config.mak.autogen
diff --git a/smartt-perf/CREDITS b/smartt-perf/CREDITS
new file mode 100644
index 0000000..c2ddcb3
--- /dev/null
+++ b/smartt-perf/CREDITS
@@ -0,0 +1,30 @@
+Most of the infrastructure that 'perf' uses here has been reused
+from the Git project, as of version:
+
+ 66996ec: Sync with 1.6.2.4
+
+Here is an (incomplete!) list of main contributors to those files
+in util/* and elsewhere:
+
+ Alex Riesen
+ Christian Couder
+ Dmitry Potapov
+ Jeff King
+ Johannes Schindelin
+ Johannes Sixt
+ Junio C Hamano
+ Linus Torvalds
+ Matthias Kestenholz
+ Michal Ostrowski
+ Miklos Vajna
+ Petr Baudis
+ Pierre Habouzit
+ René Scharfe
+ Samuel Tardieu
+ Shawn O. Pearce
+ Steffen Prohaska
+ Steve Haslam
+
+Thanks guys!
+
+The full history of the files can be found in the upstream Git commits.
diff --git a/smartt-perf/Documentation/Makefile b/smartt-perf/Documentation/Makefile
new file mode 100644
index 0000000..bd498d4
--- /dev/null
+++ b/smartt-perf/Documentation/Makefile
@@ -0,0 +1,302 @@
+MAN1_TXT= \
+ $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
+ $(wildcard perf-*.txt)) \
+ perf.txt
+MAN5_TXT=
+MAN7_TXT=
+
+MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
+MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
+MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
+
+DOC_HTML=$(MAN_HTML)
+
+ARTICLES =
+# with their own formatting rules.
+SP_ARTICLES =
+API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
+SP_ARTICLES += $(API_DOCS)
+SP_ARTICLES += technical/api-index
+
+DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
+
+DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
+DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
+DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
+
+# Make the path relative to DESTDIR, not prefix
+ifndef DESTDIR
+prefix?=$(HOME)
+endif
+bindir?=$(prefix)/bin
+htmldir?=$(prefix)/share/doc/perf-doc
+pdfdir?=$(prefix)/share/doc/perf-doc
+mandir?=$(prefix)/share/man
+man1dir=$(mandir)/man1
+man5dir=$(mandir)/man5
+man7dir=$(mandir)/man7
+
+ASCIIDOC=asciidoc
+ASCIIDOC_EXTRA = --unsafe
+MANPAGE_XSL = manpage-normal.xsl
+XMLTO_EXTRA =
+INSTALL?=install
+RM ?= rm -f
+DOC_REF = origin/man
+HTML_REF = origin/html
+
+infodir?=$(prefix)/share/info
+MAKEINFO=makeinfo
+INSTALL_INFO=install-info
+DOCBOOK2X_TEXI=docbook2x-texi
+DBLATEX=dblatex
+ifndef PERL_PATH
+ PERL_PATH = /usr/bin/perl
+endif
+
+-include ../config.mak.autogen
+-include ../config.mak
+
+#
+# For asciidoc ...
+# -7.1.2, no extra settings are needed.
+# 8.0-, set ASCIIDOC8.
+#
+
+#
+# For docbook-xsl ...
+# -1.68.1, set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)
+# 1.69.0, no extra settings are needed?
+# 1.69.1-1.71.0, set DOCBOOK_SUPPRESS_SP?
+# 1.71.1, no extra settings are needed?
+# 1.72.0, set DOCBOOK_XSL_172.
+# 1.73.0-, set ASCIIDOC_NO_ROFF
+#
+
+#
+# If you had been using DOCBOOK_XSL_172 in an attempt to get rid
+# of 'the ".ft C" problem' in your generated manpages, and you
+# instead ended up with weird characters around callouts, try
+# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).
+#
+
+ifdef ASCIIDOC8
+ASCIIDOC_EXTRA += -a asciidoc7compatible
+endif
+ifdef DOCBOOK_XSL_172
+ASCIIDOC_EXTRA += -a perf-asciidoc-no-roff
+MANPAGE_XSL = manpage-1.72.xsl
+else
+ ifdef ASCIIDOC_NO_ROFF
+ # docbook-xsl after 1.72 needs the regular XSL, but will not
+ # pass-thru raw roff codes from asciidoc.conf, so turn them off.
+ ASCIIDOC_EXTRA += -a perf-asciidoc-no-roff
+ endif
+endif
+ifdef MAN_BOLD_LITERAL
+XMLTO_EXTRA += -m manpage-bold-literal.xsl
+endif
+ifdef DOCBOOK_SUPPRESS_SP
+XMLTO_EXTRA += -m manpage-suppress-sp.xsl
+endif
+
+SHELL_PATH ?= $(SHELL)
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+#
+# Please note that there is a minor bug in asciidoc.
+# The version after 6.0.3 _will_ include the patch found here:
+# http://marc.theaimsgroup.com/?l=perf&m=111558757202243&w=2
+#
+# Until that version is released you may have to apply the patch
+# yourself - yes, all 6 characters of it!
+#
+
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1 =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+ QUIET_ASCIIDOC = @echo ' ' ASCIIDOC $@;
+ QUIET_XMLTO = @echo ' ' XMLTO $@;
+ QUIET_DB2TEXI = @echo ' ' DB2TEXI $@;
+ QUIET_MAKEINFO = @echo ' ' MAKEINFO $@;
+ QUIET_DBLATEX = @echo ' ' DBLATEX $@;
+ QUIET_XSLTPROC = @echo ' ' XSLTPROC $@;
+ QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_STDERR = 2> /dev/null
+ QUIET_SUBDIR0 = +@subdir=
+ QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
+ $(MAKE) $(PRINT_DIR) -C $$subdir
+ export V
+endif
+endif
+
+all: html man
+
+html: $(DOC_HTML)
+
+$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7): asciidoc.conf
+
+man: man1 man5 man7
+man1: $(DOC_MAN1)
+man5: $(DOC_MAN5)
+man7: $(DOC_MAN7)
+
+info: perf.info perfman.info
+
+pdf: user-manual.pdf
+
+install: install-man
+
+install-man: man
+ $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
+# $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir)
+# $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir)
+ $(INSTALL) -m 644 $(DOC_MAN1) $(DESTDIR)$(man1dir)
+# $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
+# $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
+
+install-info: info
+ $(INSTALL) -d -m 755 $(DESTDIR)$(infodir)
+ $(INSTALL) -m 644 perf.info perfman.info $(DESTDIR)$(infodir)
+ if test -r $(DESTDIR)$(infodir)/dir; then \
+ $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perf.info ;\
+ $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perfman.info ;\
+ else \
+ echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
+ fi
+
+install-pdf: pdf
+ $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir)
+ $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
+
+install-html: html
+ '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
+
+../PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
+ $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) PERF-VERSION-FILE
+
+-include ../PERF-VERSION-FILE
+
+#
+# Determine "include::" file references in asciidoc files.
+#
+doc.dep : $(wildcard *.txt) build-docdep.perl
+ $(QUIET_GEN)$(RM) $@+ $@ && \
+ $(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
+ mv $@+ $@
+
+-include doc.dep
+
+cmds_txt = cmds-ancillaryinterrogators.txt \
+ cmds-ancillarymanipulators.txt \
+ cmds-mainporcelain.txt \
+ cmds-plumbinginterrogators.txt \
+ cmds-plumbingmanipulators.txt \
+ cmds-synchingrepositories.txt \
+ cmds-synchelpers.txt \
+ cmds-purehelpers.txt \
+ cmds-foreignscminterface.txt
+
+$(cmds_txt): cmd-list.made
+
+cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
+ $(QUIET_GEN)$(RM) $@ && \
+ $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \
+ date >$@
+
+clean:
+ $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
+ $(RM) *.texi *.texi+ *.texi++ perf.info perfman.info
+ $(RM) howto-index.txt howto/*.html doc.dep
+ $(RM) technical/api-*.html technical/api-index.txt
+ $(RM) $(cmds_txt) *.made
+
+$(MAN_HTML): %.html : %.txt
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
+ $(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \
+ mv $@+ $@
+
+%.1 %.5 %.7 : %.xml
+ $(QUIET_XMLTO)$(RM) $@ && \
+ xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
+
+%.xml : %.txt
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
+ $(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \
+ mv $@+ $@
+
+XSLT = docbook.xsl
+XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
+
+user-manual.html: user-manual.xml
+ $(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
+
+perf.info: user-manual.texi
+ $(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi
+
+user-manual.texi: user-manual.xml
+ $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+ $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
+ $(PERL_PATH) fix-texi.perl <$@++ >$@+ && \
+ rm $@++ && \
+ mv $@+ $@
+
+user-manual.pdf: user-manual.xml
+ $(QUIET_DBLATEX)$(RM) $@+ $@ && \
+ $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \
+ mv $@+ $@
+
+perfman.texi: $(MAN_XML) cat-texi.perl
+ $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+ ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
+ --to-stdout $(xml) &&) true) > $@++ && \
+ $(PERL_PATH) cat-texi.perl $@ <$@++ >$@+ && \
+ rm $@++ && \
+ mv $@+ $@
+
+perfman.info: perfman.texi
+ $(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi
+
+$(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
+ $(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+ $(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ && \
+ mv $@+ $@
+
+howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
+ $(QUIET_GEN)$(RM) $@+ $@ && \
+ '$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
+ mv $@+ $@
+
+$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
+ $(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 $*.txt
+
+WEBDOC_DEST = /pub/software/tools/perf/docs
+
+$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ && \
+ mv $@+ $@
+
+install-webdoc : html
+ '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
+
+quick-install: quick-install-man
+
+quick-install-man:
+ '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
+
+quick-install-html:
+ '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
+
+.PHONY: .FORCE-PERF-VERSION-FILE
diff --git a/smartt-perf/Documentation/asciidoc.conf b/smartt-perf/Documentation/asciidoc.conf
new file mode 100644
index 0000000..356b23a
--- /dev/null
+++ b/smartt-perf/Documentation/asciidoc.conf
@@ -0,0 +1,91 @@
+## linkperf: macro
+#
+# Usage: linkperf:command[manpage-section]
+#
+# Note, {0} is the manpage section, while {target} is the command.
+#
+# Show PERF link as: <command>(<section>); if section is defined, else just show
+# the command.
+
+[macros]
+(?su)[\\]?(?P<name>linkperf):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
+[attributes]
+asterisk=&#42;
+plus=&#43;
+caret=&#94;
+startsb=&#91;
+endsb=&#93;
+tilde=&#126;
+
+ifdef::backend-docbook[]
+[linkperf-inlinemacro]
+{0%{target}}
+{0#<citerefentry>}
+{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
+{0#</citerefentry>}
+endif::backend-docbook[]
+
+ifdef::backend-docbook[]
+ifndef::perf-asciidoc-no-roff[]
+# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
+# v1.72 breaks with this because it replaces dots not in roff requests.
+[listingblock]
+<example><title>{title}</title>
+<literallayout>
+ifdef::doctype-manpage[]
+&#10;.ft C&#10;
+endif::doctype-manpage[]
+|
+ifdef::doctype-manpage[]
+&#10;.ft&#10;
+endif::doctype-manpage[]
+</literallayout>
+{title#}</example>
+endif::perf-asciidoc-no-roff[]
+
+ifdef::perf-asciidoc-no-roff[]
+ifdef::doctype-manpage[]
+# The following two small workarounds insert a simple paragraph after screen
+[listingblock]
+<example><title>{title}</title>
+<literallayout>
+|
+</literallayout><simpara></simpara>
+{title#}</example>
+
+[verseblock]
+<formalpara{id? id="{id}"}><title>{title}</title><para>
+{title%}<literallayout{id? id="{id}"}>
+{title#}<literallayout>
+|
+</literallayout>
+{title#}</para></formalpara>
+{title%}<simpara></simpara>
+endif::doctype-manpage[]
+endif::perf-asciidoc-no-roff[]
+endif::backend-docbook[]
+
+ifdef::doctype-manpage[]
+ifdef::backend-docbook[]
+[header]
+template::[header-declarations]
+<refentry>
+<refmeta>
+<refentrytitle>{mantitle}</refentrytitle>
+<manvolnum>{manvolnum}</manvolnum>
+<refmiscinfo class="source">perf</refmiscinfo>
+<refmiscinfo class="version">{perf_version}</refmiscinfo>
+<refmiscinfo class="manual">perf Manual</refmiscinfo>
+</refmeta>
+<refnamediv>
+ <refname>{manname}</refname>
+ <refpurpose>{manpurpose}</refpurpose>
+</refnamediv>
+endif::backend-docbook[]
+endif::doctype-manpage[]
+
+ifdef::backend-xhtml11[]
+[linkperf-inlinemacro]
+<a href="{target}.html">{target}{0?({0})}</a>
+endif::backend-xhtml11[]
diff --git a/smartt-perf/Documentation/examples.txt b/smartt-perf/Documentation/examples.txt
new file mode 100644
index 0000000..8eb6c48
--- /dev/null
+++ b/smartt-perf/Documentation/examples.txt
@@ -0,0 +1,225 @@
+
+ ------------------------------
+ ****** perf by examples ******
+ ------------------------------
+
+[ From an e-mail by Ingo Molnar, http://lkml.org/lkml/2009/8/4/346 ]
+
+
+First, discovery/enumeration of available counters can be done via
+'perf list':
+
+titan:~> perf list
+ [...]
+ kmem:kmalloc [Tracepoint event]
+ kmem:kmem_cache_alloc [Tracepoint event]
+ kmem:kmalloc_node [Tracepoint event]
+ kmem:kmem_cache_alloc_node [Tracepoint event]
+ kmem:kfree [Tracepoint event]
+ kmem:kmem_cache_free [Tracepoint event]
+ kmem:mm_page_free_direct [Tracepoint event]
+ kmem:mm_pagevec_free [Tracepoint event]
+ kmem:mm_page_alloc [Tracepoint event]
+ kmem:mm_page_alloc_zone_locked [Tracepoint event]
+ kmem:mm_page_pcpu_drain [Tracepoint event]
+ kmem:mm_page_alloc_extfrag [Tracepoint event]
+
+Then any (or all) of the above event sources can be activated and
+measured. For example the page alloc/free properties of a 'hackbench
+run' are:
+
+ titan:~> perf stat -e kmem:mm_page_pcpu_drain -e kmem:mm_page_alloc
+ -e kmem:mm_pagevec_free -e kmem:mm_page_free_direct ./hackbench 10
+ Time: 0.575
+
+ Performance counter stats for './hackbench 10':
+
+ 13857 kmem:mm_page_pcpu_drain
+ 27576 kmem:mm_page_alloc
+ 6025 kmem:mm_pagevec_free
+ 20934 kmem:mm_page_free_direct
+
+ 0.613972165 seconds time elapsed
+
+You can observe the statistical properties as well, by using the
+'repeat the workload N times' feature of perf stat:
+
+ titan:~> perf stat --repeat 5 -e kmem:mm_page_pcpu_drain -e
+ kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+ kmem:mm_page_free_direct ./hackbench 10
+ Time: 0.627
+ Time: 0.644
+ Time: 0.564
+ Time: 0.559
+ Time: 0.626
+
+ Performance counter stats for './hackbench 10' (5 runs):
+
+ 12920 kmem:mm_page_pcpu_drain ( +- 3.359% )
+ 25035 kmem:mm_page_alloc ( +- 3.783% )
+ 6104 kmem:mm_pagevec_free ( +- 0.934% )
+ 18376 kmem:mm_page_free_direct ( +- 4.941% )
+
+ 0.643954516 seconds time elapsed ( +- 2.363% )
+
+Furthermore, these tracepoints can be used to sample the workload as
+well. For example the page allocations done by a 'git gc' can be
+captured the following way:
+
+ titan:~/git> perf record -f -e kmem:mm_page_alloc -c 1 ./git gc
+ Counting objects: 1148, done.
+ Delta compression using up to 2 threads.
+ Compressing objects: 100% (450/450), done.
+ Writing objects: 100% (1148/1148), done.
+ Total 1148 (delta 690), reused 1148 (delta 690)
+ [ perf record: Captured and wrote 0.267 MB perf.data (~11679 samples) ]
+
+To check which functions generated page allocations:
+
+ titan:~/git> perf report
+ # Samples: 10646
+ #
+ # Overhead Command Shared Object
+ # ........ ............... ..........................
+ #
+ 23.57% git-repack /lib64/libc-2.5.so
+ 21.81% git /lib64/libc-2.5.so
+ 14.59% git ./git
+ 11.79% git-repack ./git
+ 7.12% git /lib64/ld-2.5.so
+ 3.16% git-repack /lib64/libpthread-2.5.so
+ 2.09% git-repack /bin/bash
+ 1.97% rm /lib64/libc-2.5.so
+ 1.39% mv /lib64/ld-2.5.so
+ 1.37% mv /lib64/libc-2.5.so
+ 1.12% git-repack /lib64/ld-2.5.so
+ 0.95% rm /lib64/ld-2.5.so
+ 0.90% git-update-serv /lib64/libc-2.5.so
+ 0.73% git-update-serv /lib64/ld-2.5.so
+ 0.68% perf /lib64/libpthread-2.5.so
+ 0.64% git-repack /usr/lib64/libz.so.1.2.3
+
+Or to see it on a more finegrained level:
+
+titan:~/git> perf report --sort comm,dso,symbol
+# Samples: 10646
+#
+# Overhead Command Shared Object Symbol
+# ........ ............... .......................... ......
+#
+ 9.35% git-repack ./git [.] insert_obj_hash
+ 9.12% git ./git [.] insert_obj_hash
+ 7.31% git /lib64/libc-2.5.so [.] memcpy
+ 6.34% git-repack /lib64/libc-2.5.so [.] _int_malloc
+ 6.24% git-repack /lib64/libc-2.5.so [.] memcpy
+ 5.82% git-repack /lib64/libc-2.5.so [.] __GI___fork
+ 5.47% git /lib64/libc-2.5.so [.] _int_malloc
+ 2.99% git /lib64/libc-2.5.so [.] memset
+
+Furthermore, call-graph sampling can be done too, of page
+allocations - to see precisely what kind of page allocations there
+are:
+
+ titan:~/git> perf record -f -g -e kmem:mm_page_alloc -c 1 ./git gc
+ Counting objects: 1148, done.
+ Delta compression using up to 2 threads.
+ Compressing objects: 100% (450/450), done.
+ Writing objects: 100% (1148/1148), done.
+ Total 1148 (delta 690), reused 1148 (delta 690)
+ [ perf record: Captured and wrote 0.963 MB perf.data (~42069 samples) ]
+
+ titan:~/git> perf report -g
+ # Samples: 10686
+ #
+ # Overhead Command Shared Object
+ # ........ ............... ..........................
+ #
+ 23.25% git-repack /lib64/libc-2.5.so
+ |
+ |--50.00%-- _int_free
+ |
+ |--37.50%-- __GI___fork
+ | make_child
+ |
+ |--12.50%-- ptmalloc_unlock_all2
+ | make_child
+ |
+ --6.25%-- __GI_strcpy
+ 21.61% git /lib64/libc-2.5.so
+ |
+ |--30.00%-- __GI_read
+ | |
+ | --83.33%-- git_config_from_file
+ | git_config
+ | |
+ [...]
+
+Or you can observe the whole system's page allocations for 10
+seconds:
+
+titan:~/git> perf stat -a -e kmem:mm_page_pcpu_drain -e
+kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+kmem:mm_page_free_direct sleep 10
+
+ Performance counter stats for 'sleep 10':
+
+ 171585 kmem:mm_page_pcpu_drain
+ 322114 kmem:mm_page_alloc
+ 73623 kmem:mm_pagevec_free
+ 254115 kmem:mm_page_free_direct
+
+ 10.000591410 seconds time elapsed
+
+Or observe how fluctuating the page allocations are, via statistical
+analysis done over ten 1-second intervals:
+
+ titan:~/git> perf stat --repeat 10 -a -e kmem:mm_page_pcpu_drain -e
+ kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+ kmem:mm_page_free_direct sleep 1
+
+ Performance counter stats for 'sleep 1' (10 runs):
+
+ 17254 kmem:mm_page_pcpu_drain ( +- 3.709% )
+ 34394 kmem:mm_page_alloc ( +- 4.617% )
+ 7509 kmem:mm_pagevec_free ( +- 4.820% )
+ 25653 kmem:mm_page_free_direct ( +- 3.672% )
+
+ 1.058135029 seconds time elapsed ( +- 3.089% )
+
+Or you can annotate the recorded 'git gc' run on a per symbol basis
+and check which instructions/source-code generated page allocations:
+
+ titan:~/git> perf annotate __GI___fork
+ ------------------------------------------------
+ Percent | Source code & Disassembly of libc-2.5.so
+ ------------------------------------------------
+ :
+ :
+ : Disassembly of section .plt:
+ : Disassembly of section .text:
+ :
+ : 00000031a2e95560 <__fork>:
+ [...]
+ 0.00 : 31a2e95602: b8 38 00 00 00 mov $0x38,%eax
+ 0.00 : 31a2e95607: 0f 05 syscall
+ 83.42 : 31a2e95609: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax
+ 0.00 : 31a2e9560f: 0f 87 4d 01 00 00 ja 31a2e95762 <__fork+0x202>
+ 0.00 : 31a2e95615: 85 c0 test %eax,%eax
+
+( this shows that 83.42% of __GI___fork's page allocations come from
+ the 0x38 system call it performs. )
+
+etc. etc. - a lot more is possible. I could list a dozen of
+other different usecases straight away - neither of which is
+possible via /proc/vmstat.
+
+/proc/vmstat is not in the same league really, in terms of
+expressive power of system analysis and performance
+analysis.
+
+All that the above results needed were those new tracepoints
+in include/tracing/events/kmem.h.
+
+ Ingo
+
+
diff --git a/smartt-perf/Documentation/manpage-1.72.xsl b/smartt-perf/Documentation/manpage-1.72.xsl
new file mode 100644
index 0000000..b4d315c
--- /dev/null
+++ b/smartt-perf/Documentation/manpage-1.72.xsl
@@ -0,0 +1,14 @@
+<!-- manpage-1.72.xsl:
+ special settings for manpages rendered from asciidoc+docbook
+ handles peculiarities in docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<xsl:import href="manpage-base.xsl"/>
+
+<!-- these are the special values for the roff control characters
+ needed for docbook-xsl 1.72.0 -->
+<xsl:param name="git.docbook.backslash">&#x2593;</xsl:param>
+<xsl:param name="git.docbook.dot" >&#x2302;</xsl:param>
+
+</xsl:stylesheet>
diff --git a/smartt-perf/Documentation/manpage-base.xsl b/smartt-perf/Documentation/manpage-base.xsl
new file mode 100644
index 0000000..a264fa6
--- /dev/null
+++ b/smartt-perf/Documentation/manpage-base.xsl
@@ -0,0 +1,35 @@
+<!-- manpage-base.xsl:
+ special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<!-- these params silence some output from xmlto -->
+<xsl:param name="man.output.quietly" select="1"/>
+<xsl:param name="refentry.meta.get.quietly" select="1"/>
+
+<!-- convert asciidoc callouts to man page format;
+ git.docbook.backslash and git.docbook.dot params
+ must be supplied by another XSL file or other means -->
+<xsl:template match="co">
+ <xsl:value-of select="concat(
+ $git.docbook.backslash,'fB(',
+ substring-after(@id,'-'),')',
+ $git.docbook.backslash,'fR')"/>
+</xsl:template>
+<xsl:template match="calloutlist">
+ <xsl:value-of select="$git.docbook.dot"/>
+ <xsl:text>sp&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;</xsl:text>
+</xsl:template>
+<xsl:template match="callout">
+ <xsl:value-of select="concat(
+ $git.docbook.backslash,'fB',
+ substring-after(@arearefs,'-'),
+ '. ',$git.docbook.backslash,'fR')"/>
+ <xsl:apply-templates/>
+ <xsl:value-of select="$git.docbook.dot"/>
+ <xsl:text>br&#10;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/smartt-perf/Documentation/manpage-bold-literal.xsl b/smartt-perf/Documentation/manpage-bold-literal.xsl
new file mode 100644
index 0000000..608eb5d
--- /dev/null
+++ b/smartt-perf/Documentation/manpage-bold-literal.xsl
@@ -0,0 +1,17 @@
+<!-- manpage-bold-literal.xsl:
+ special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<!-- render literal text as bold (instead of plain or monospace);
+ this makes literal text easier to distinguish in manpages
+ viewed on a tty -->
+<xsl:template match="literal">
+ <xsl:value-of select="$git.docbook.backslash"/>
+ <xsl:text>fB</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:value-of select="$git.docbook.backslash"/>
+ <xsl:text>fR</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/smartt-perf/Documentation/manpage-normal.xsl b/smartt-perf/Documentation/manpage-normal.xsl
new file mode 100644
index 0000000..a48f5b1
--- /dev/null
+++ b/smartt-perf/Documentation/manpage-normal.xsl
@@ -0,0 +1,13 @@
+<!-- manpage-normal.xsl:
+ special settings for manpages rendered from asciidoc+docbook
+ handles anything we want to keep away from docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<xsl:import href="manpage-base.xsl"/>
+
+<!-- these are the normal values for the roff control characters -->
+<xsl:param name="git.docbook.backslash">\</xsl:param>
+<xsl:param name="git.docbook.dot" >.</xsl:param>
+
+</xsl:stylesheet>
diff --git a/smartt-perf/Documentation/manpage-suppress-sp.xsl b/smartt-perf/Documentation/manpage-suppress-sp.xsl
new file mode 100644
index 0000000..a63c763
--- /dev/null
+++ b/smartt-perf/Documentation/manpage-suppress-sp.xsl
@@ -0,0 +1,21 @@
+<!-- manpage-suppress-sp.xsl:
+ special settings for manpages rendered from asciidoc+docbook
+ handles erroneous, inline .sp in manpage output of some
+ versions of docbook-xsl -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<!-- attempt to work around spurious .sp at the tail of the line
+ that some versions of docbook stylesheets seem to add -->
+<xsl:template match="simpara">
+ <xsl:variable name="content">
+ <xsl:apply-templates/>
+ </xsl:variable>
+ <xsl:value-of select="normalize-space($content)"/>
+ <xsl:if test="not(ancestor::authorblurb) and
+ not(ancestor::personblurb)">
+ <xsl:text>&#10;&#10;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/smartt-perf/Documentation/perf-annotate.txt b/smartt-perf/Documentation/perf-annotate.txt
new file mode 100644
index 0000000..6f5a498
--- /dev/null
+++ b/smartt-perf/Documentation/perf-annotate.txt
@@ -0,0 +1,71 @@
+perf-annotate(1)
+================
+
+NAME
+----
+perf-annotate - Read perf.data (created by perf record) and display annotated code
+
+SYNOPSIS
+--------
+[verse]
+'perf annotate' [-i <file> | --input=file] [symbol_name]
+
+DESCRIPTION
+-----------
+This command reads the input file and displays an annotated version of the
+code. If the object file has debug symbols then the source code will be
+displayed alongside assembly code.
+
+If there is no debug info in the object, then annotated assembly is displayed.
+
+OPTIONS
+-------
+-i::
+--input=::
+ Input file name. (default: perf.data)
+
+-d::
+--dsos=<dso[,dso...]>::
+ Only consider symbols in these dsos.
+-s::
+--symbol=<symbol>::
+ Symbol to annotate.
+
+-f::
+--force::
+ Don't complain, do it.
+
+-v::
+--verbose::
+ Be more verbose. (Show symbol address, etc)
+
+-D::
+--dump-raw-trace::
+ Dump raw trace in ASCII.
+
+-k::
+--vmlinux=<file>::
+ vmlinux pathname.
+
+-m::
+--modules::
+ Load module symbols. WARNING: use only with -k and LIVE kernel.
+
+-l::
+--print-line::
+ Print matching source lines (may be slow).
+
+-P::
+--full-paths::
+ Don't shorten the displayed pathnames.
+
+--stdio:: Use the stdio interface.
+
+--tui:: Use the TUI interface Use of --tui requires a tty, if one is not
+ present, as when piping to other commands, the stdio interface is
+ used. This interfaces starts by centering on the line with more
+ samples, TAB/UNTAB cycles through the lines with more samples.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-report[1]
diff --git a/smartt-perf/Documentation/perf-archive.txt b/smartt-perf/Documentation/perf-archive.txt
new file mode 100644
index 0000000..fae174d
--- /dev/null
+++ b/smartt-perf/Documentation/perf-archive.txt
@@ -0,0 +1,22 @@
+perf-archive(1)
+===============
+
+NAME
+----
+perf-archive - Create archive with object files with build-ids found in perf.data file
+
+SYNOPSIS
+--------
+[verse]
+'perf archive' [file]
+
+DESCRIPTION
+-----------
+This command runs runs perf-buildid-list --with-hits, and collects the files
+with the buildids found so that analisys of perf.data contents can be possible
+on another machine.
+
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-buildid-list[1], linkperf:perf-report[1]
diff --git a/smartt-perf/Documentation/perf-bench.txt b/smartt-perf/Documentation/perf-bench.txt
new file mode 100644
index 0000000..a3dbadb
--- /dev/null
+++ b/smartt-perf/Documentation/perf-bench.txt
@@ -0,0 +1,120 @@
+perf-bench(1)
+=============
+
+NAME
+----
+perf-bench - General framework for benchmark suites
+
+SYNOPSIS
+--------
+[verse]
+'perf bench' [<common options>] <subsystem> <suite> [<options>]
+
+DESCRIPTION
+-----------
+This 'perf bench' command is general framework for benchmark suites.
+
+COMMON OPTIONS
+--------------
+-f::
+--format=::
+Specify format style.
+Current available format styles are:
+
+'default'::
+Default style. This is mainly for human reading.
+---------------------
+% perf bench sched pipe # with no style specified
+(executing 1000000 pipe operations between two tasks)
+ Total time:5.855 sec
+ 5.855061 usecs/op
+ 170792 ops/sec
+---------------------
+
+'simple'::
+This simple style is friendly for automated
+processing by scripts.
+---------------------
+% perf bench --format=simple sched pipe # specified simple
+5.988
+---------------------
+
+SUBSYSTEM
+---------
+
+'sched'::
+ Scheduler and IPC mechanisms.
+
+SUITES FOR 'sched'
+~~~~~~~~~~~~~~~~~~
+*messaging*::
+Suite for evaluating performance of scheduler and IPC mechanisms.
+Based on hackbench by Rusty Russell.
+
+Options of *pipe*
+^^^^^^^^^^^^^^^^^
+-p::
+--pipe::
+Use pipe() instead of socketpair()
+
+-t::
+--thread::
+Be multi thread instead of multi process
+
+-g::
+--group=::
+Specify number of groups
+
+-l::
+--loop=::
+Specify number of loops
+
+Example of *messaging*
+^^^^^^^^^^^^^^^^^^^^^^
+
+---------------------
+% perf bench sched messaging # run with default
+options (20 sender and receiver processes per group)
+(10 groups == 400 processes run)
+
+ Total time:0.308 sec
+
+% perf bench sched messaging -t -g 20 # be multi-thread, with 20 groups
+(20 sender and receiver threads per group)
+(20 groups == 800 threads run)
+
+ Total time:0.582 sec
+---------------------
+
+*pipe*::
+Suite for pipe() system call.
+Based on pipe-test-1m.c by Ingo Molnar.
+
+Options of *pipe*
+^^^^^^^^^^^^^^^^^
+-l::
+--loop=::
+Specify number of loops.
+
+Example of *pipe*
+^^^^^^^^^^^^^^^^^
+
+---------------------
+% perf bench sched pipe
+(executing 1000000 pipe operations between two tasks)
+
+ Total time:8.091 sec
+ 8.091833 usecs/op
+ 123581 ops/sec
+
+% perf bench sched pipe -l 1000 # loop 1000
+(executing 1000 pipe operations between two tasks)
+
+ Total time:0.016 sec
+ 16.948000 usecs/op
+ 59004 ops/sec
+---------------------
+
+SEE ALSO
+--------
+linkperf:perf[1]
diff --git a/smartt-perf/Documentation/perf-buildid-cache.txt b/smartt-perf/Documentation/perf-buildid-cache.txt
new file mode 100644
index 0000000..c105770
--- /dev/null
+++ b/smartt-perf/Documentation/perf-buildid-cache.txt
@@ -0,0 +1,33 @@
+perf-buildid-cache(1)
+=====================
+
+NAME
+----
+perf-buildid-cache - Manage build-id cache.
+
+SYNOPSIS
+--------
+[verse]
+'perf buildid-cache <options>'
+
+DESCRIPTION
+-----------
+This command manages the build-id cache. It can add and remove files to/from
+the cache. In the future it should as well purge older entries, set upper
+limits for the space used by the cache, etc.
+
+OPTIONS
+-------
+-a::
+--add=::
+ Add specified file to the cache.
+-r::
+--remove=::
+ Remove specified file from the cache.
+-v::
+--verbose::
+ Be more verbose.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
diff --git a/smartt-perf/Documentation/perf-buildid-list.txt b/smartt-perf/Documentation/perf-buildid-list.txt
new file mode 100644
index 0000000..5eaac6f
--- /dev/null
+++ b/smartt-perf/Documentation/perf-buildid-list.txt
@@ -0,0 +1,37 @@
+perf-buildid-list(1)
+====================
+
+NAME
+----
+perf-buildid-list - List the buildids in a perf.data file
+
+SYNOPSIS
+--------
+[verse]
+'perf buildid-list <options>'
+
+DESCRIPTION
+-----------
+This command displays the buildids found in a perf.data file, so that other
+tools can be used to fetch packages with matching symbol tables for use by
+perf report.
+
+OPTIONS
+-------
+-H::
+--with-hits::
+ Show only DSOs with hits.
+-i::
+--input=::
+ Input file name. (default: perf.data)
+-f::
+--force::
+ Don't do ownership validation.
+-v::
+--verbose::
+ Be more verbose.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-top[1],
+linkperf:perf-report[1]
diff --git a/smartt-perf/Documentation/perf-diff.txt b/smartt-perf/Documentation/perf-diff.txt
new file mode 100644
index 0000000..74d7481
--- /dev/null
+++ b/smartt-perf/Documentation/perf-diff.txt
@@ -0,0 +1,74 @@
+perf-diff(1)
+============
+
+NAME
+----
+perf-diff - Read two perf.data files and display the differential profile
+
+SYNOPSIS
+--------
+[verse]
+'perf diff' [oldfile] [newfile]
+
+DESCRIPTION
+-----------
+This command displays the performance difference amongst two perf.data files
+captured via perf record.
+
+If no parameters are passed it will assume perf.data.old and perf.data.
+
+OPTIONS
+-------
+-M::
+--displacement::
+ Show position displacement relative to baseline.
+
+-D::
+--dump-raw-trace::
+ Dump raw trace in ASCII.
+
+-m::
+--modules::
+ Load module symbols. WARNING: use only with -k and LIVE kernel
+
+-d::
+--dsos=::
+ Only consider symbols in these dsos. CSV that understands
+ file://filename entries.
+
+-C::
+--comms=::
+ Only consider symbols in these comms. CSV that understands
+ file://filename entries.
+
+-S::
+--symbols=::
+ Only consider these symbols. CSV that understands
+ file://filename entries.
+
+-s::
+--sort=::
+ Sort by key(s): pid, comm, dso, symbol.
+
+-t::
+--field-separator=::
+
+ Use a special separator character and don't pad with spaces, replacing
+ all occurrences of this separator in symbol names (and other output)
+ with a '.' character, that thus it's the only non valid separator.
+
+-v::
+--verbose::
+ Be verbose, for instance, show the raw counts in addition to the
+ diff.
+
+-f::
+--force::
+ Don't complain, do it.
+
+--symfs=<directory>::
+ Look for files with symbols relative to this directory.
+
+SEE ALSO
+--------
+linkperf:perf-record[1]
diff --git a/smartt-perf/Documentation/perf-help.txt b/smartt-perf/Documentation/perf-help.txt
new file mode 100644
index 0000000..5143918
--- /dev/null
+++ b/smartt-perf/Documentation/perf-help.txt
@@ -0,0 +1,38 @@
+perf-help(1)
+============
+
+NAME
+----
+perf-help - display help information about perf
+
+SYNOPSIS
+--------
+'perf help' [-a|--all] [COMMAND]
+
+DESCRIPTION
+-----------
+
+With no options and no COMMAND given, the synopsis of the 'perf'
+command and a list of the most commonly used perf commands are printed
+on the standard output.
+
+If the option '--all' or '-a' is given, then all available commands are
+printed on the standard output.
+
+If a perf command is named, a manual page for that command is brought
+up. The 'man' program is used by default for this purpose, but this
+can be overridden by other options or configuration variables.
+
+Note that `perf --help ...` is identical to `perf help ...` because the
+former is internally converted into the latter.
+
+OPTIONS
+-------
+-a::
+--all::
+ Prints all the available commands on the standard output. This
+ option supersedes any other option.
+
+PERF
+----
+Part of the linkperf:perf[1] suite
diff --git a/smartt-perf/Documentation/perf-inject.txt b/smartt-perf/Documentation/perf-inject.txt
new file mode 100644
index 0000000..025630d
--- /dev/null
+++ b/smartt-perf/Documentation/perf-inject.txt
@@ -0,0 +1,35 @@
+perf-inject(1)
+==============
+
+NAME
+----
+perf-inject - Filter to augment the events stream with additional information
+
+SYNOPSIS
+--------
+[verse]
+'perf inject <options>'
+
+DESCRIPTION
+-----------
+perf-inject reads a perf-record event stream and repipes it to stdout. At any
+point the processing code can inject other events into the event stream - in
+this case build-ids (-b option) are read and injected as needed into the event
+stream.
+
+Build-ids are just the first user of perf-inject - potentially anything that
+needs userspace processing to augment the events stream with additional
+information could make use of this facility.
+
+OPTIONS
+-------
+-b::
+--build-ids=::
+ Inject build-ids into the output stream
+-v::
+--verbose::
+ Be more verbose.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]
diff --git a/smartt-perf/Documentation/perf-kmem.txt b/smartt-perf/Documentation/perf-kmem.txt
new file mode 100644
index 0000000..a52fcde
--- /dev/null
+++ b/smartt-perf/Documentation/perf-kmem.txt
@@ -0,0 +1,47 @@
+perf-kmem(1)
+============
+
+NAME
+----
+perf-kmem - Tool to trace/measure kernel memory(slab) properties
+
+SYNOPSIS
+--------
+[verse]
+'perf kmem' {record|stat} [<options>]
+
+DESCRIPTION
+-----------
+There are two variants of perf kmem:
+
+ 'perf kmem record <command>' to record the kmem events
+ of an arbitrary workload.
+
+ 'perf kmem stat' to report kernel memory statistics.
+
+OPTIONS
+-------
+-i <file>::
+--input=<file>::
+ Select the input file (default: perf.data)
+
+--caller::
+ Show per-callsite statistics
+
+--alloc::
+ Show per-allocation statistics
+
+-s <key[,key2...]>::
+--sort=<key[,key2...]>::
+ Sort the output (default: frag,hit,bytes)
+
+-l <num>::
+--line=<num>::
+ Print n lines only
+
+--raw-ip::
+ Print raw ip instead of symbol
+
+SEE ALSO
+--------
+linkperf:perf-record[1]
diff --git a/smartt-perf/Documentation/perf-kvm.txt b/smartt-perf/Documentation/perf-kvm.txt
new file mode 100644
index 0000000..dd84cb2
--- /dev/null
+++ b/smartt-perf/Documentation/perf-kvm.txt
@@ -0,0 +1,74 @@
+perf-kvm(1)
+===========
+
+NAME
+----
+perf-kvm - Tool to trace/measure kvm guest os
+
+SYNOPSIS
+--------
+[verse]
+'perf kvm' [--host] [--guest] [--guestmount=<path>
+ [--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
+ {top|record|report|diff|buildid-list}
+'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
+ | --guestvmlinux=<path>] {top|record|report|diff|buildid-list}
+
+DESCRIPTION
+-----------
+There are a couple of variants of perf kvm:
+
+ 'perf kvm [options] top <command>' to generates and displays
+ a performance counter profile of guest os in realtime
+ of an arbitrary workload.
+
+ 'perf kvm record <command>' to record the performance counter profile
+ of an arbitrary workload and save it into a perf data file. If both
+ --host and --guest are input, the perf data file name is perf.data.kvm.
+ If there is no --host but --guest, the file name is perf.data.guest.
+ If there is no --guest but --host, the file name is perf.data.host.
+
+ 'perf kvm report' to display the performance counter profile information
+ recorded via perf kvm record.
+
+ 'perf kvm diff' to displays the performance difference amongst two perf.data
+ files captured via perf record.
+
+ 'perf kvm buildid-list' to display the buildids found in a perf data file,
+ so that other tools can be used to fetch packages with matching symbol tables
+ for use by perf report.
+
+OPTIONS
+-------
+-i::
+--input=::
+ Input file name.
+-o::
+--output::
+ Output file name.
+--host=::
+ Collect host side performance profile.
+--guest=::
+ Collect guest side performance profile.
+--guestmount=<path>::
+ Guest os root file system mount directory. Users mounts guest os
+ root directories under <path> by a specific filesystem access method,
+ typically, sshfs. For example, start 2 guest os. The one's pid is 8888
+ and the other's is 9999.
+ #mkdir ~/guestmount; cd ~/guestmount
+ #sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/
+ #sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/
+ #perf kvm --host --guest --guestmount=~/guestmount top
+--guestkallsyms=<path>::
+ Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest
+ kernel symbols. Users copy it out from guest os.
+--guestmodules=<path>::
+ Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest
+ kernel module information. Users copy it out from guest os.
+--guestvmlinux=<path>::
+ Guest os kernel vmlinux.
+
+SEE ALSO
+--------
+linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1],
+linkperf:perf-diff[1], linkperf:perf-buildid-list[1]
diff --git a/smartt-perf/Documentation/perf-list.txt b/smartt-perf/Documentation/perf-list.txt
new file mode 100644
index 0000000..399751b
--- /dev/null
+++ b/smartt-perf/Documentation/perf-list.txt
@@ -0,0 +1,73 @@
+perf-list(1)
+============
+
+NAME
+----
+perf-list - List all symbolic event types
+
+SYNOPSIS
+--------
+[verse]
+'perf list'
+
+DESCRIPTION
+-----------
+This command displays the symbolic event types which can be selected in the
+various perf commands with the -e option.
+
+EVENT MODIFIERS
+---------------
+
+Events can optionally have a modifer by appending a colon and one or
+more modifiers. Modifiers allow the user to restrict when events are
+counted with 'u' for user-space, 'k' for kernel, 'h' for hypervisor.
+
+The 'p' modifier can be used for specifying how precise the instruction
+address should be. The 'p' modifier is currently only implemented for
+Intel PEBS and can be specified multiple times:
+ 0 - SAMPLE_IP can have arbitrary skid
+ 1 - SAMPLE_IP must have constant skid
+ 2 - SAMPLE_IP requested to have 0 skid
+ 3 - SAMPLE_IP must have 0 skid
+
+The PEBS implementation now supports up to 2.
+
+RAW HARDWARE EVENT DESCRIPTOR
+-----------------------------
+Even when an event is not available in a symbolic form within perf right now,
+it can be encoded in a per processor specific way.
+
+For instance For x86 CPUs NNN represents the raw register encoding with the
+layout of IA32_PERFEVTSELx MSRs (see [Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide] Figure 30-1 Layout
+of IA32_PERFEVTSELx MSRs) or AMD's PerfEvtSeln (see [AMD64 Architecture Programmer’s Manual Volume 2: System Programming], Page 344,
+Figure 13-7 Performance Event-Select Register (PerfEvtSeln)).
+
+Example:
+
+If the Intel docs for a QM720 Core i7 describe an event as:
+
+ Event Umask Event Mask
+ Num. Value Mnemonic Description Comment
+
+ A8H 01H LSD.UOPS Counts the number of micro-ops Use cmask=1 and
+ delivered by loop stream detector invert to count
+ cycles
+
+raw encoding of 0x1A8 can be used:
+
+ perf stat -e r1a8 -a sleep 1
+ perf record -e r1a8 ...
+
+You should refer to the processor specific documentation for getting these
+details. Some of them are referenced in the SEE ALSO section below.
+
+OPTIONS
+-------
+None
+
+SEE ALSO
+--------
+linkperf:perf-stat[1], linkperf:perf-top[1],
+linkperf:perf-record[1],
+http://www.intel.com/Assets/PDF/manual/253669.pdf[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide],
+http://support.amd.com/us/Processor_TechDocs/24593.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming]
diff --git a/smartt-perf/Documentation/perf-lock.txt b/smartt-perf/Documentation/perf-lock.txt
new file mode 100644
index 0000000..921de25
--- /dev/null
+++ b/smartt-perf/Documentation/perf-lock.txt
@@ -0,0 +1,44 @@
+perf-lock(1)
+============
+
+NAME
+----
+perf-lock - Analyze lock events
+
+SYNOPSIS
+--------
+[verse]
+'perf lock' {record|report|trace}
+
+DESCRIPTION
+-----------
+You can analyze various lock behaviours
+and statistics with this 'perf lock' command.
+
+ 'perf lock record <command>' records lock events
+ between start and end <command>. And this command
+ produces the file "perf.data" which contains tracing
+ results of lock events.
+
+ 'perf lock trace' shows raw lock events.
+
+ 'perf lock report' reports statistical data.
+
+OPTIONS
+-------
+
+-i::
+--input=<file>::
+ Input file name.
+
+-v::
+--verbose::
+ Be more verbose (show symbol address, etc).
+
+-D::
+--dump-raw-trace::
+ Dump raw trace in ASCII.
+
+SEE ALSO
+--------
+linkperf:perf[1]
diff --git a/smartt-perf/Documentation/perf-probe.txt b/smartt-perf/Documentation/perf-probe.txt
new file mode 100644
index 0000000..86b797a
--- /dev/null
+++ b/smartt-perf/Documentation/perf-probe.txt
@@ -0,0 +1,166 @@
+perf-probe(1)
+=============
+
+NAME
+----
+perf-probe - Define new dynamic tracepoints
+
+SYNOPSIS
+--------
+[verse]
+'perf probe' [options] --add='PROBE' [...]
+or
+'perf probe' [options] PROBE
+or
+'perf probe' [options] --del='[GROUP:]EVENT' [...]
+or
+'perf probe' --list
+or
+'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
+or
+'perf probe' [options] --vars='PROBEPOINT'
+
+DESCRIPTION
+-----------
+This command defines dynamic tracepoint events, by symbol and registers
+without debuginfo, or by C expressions (C line numbers, C function names,
+and C local variables) with debuginfo.
+
+
+OPTIONS
+-------
+-k::
+--vmlinux=PATH::
+ Specify vmlinux path which has debuginfo (Dwarf binary).
+
+-m::
+--module=MODNAME::
+ Specify module name in which perf-probe searches probe points
+ or lines.
+
+-s::
+--source=PATH::
+ Specify path to kernel source.
+
+-v::
+--verbose::
+ Be more verbose (show parsed arguments, etc).
+
+-a::
+--add=::
+ Define a probe event (see PROBE SYNTAX for detail).
+
+-d::
+--del=::
+ Delete probe events. This accepts glob wildcards('*', '?') and character
+ classes(e.g. [a-z], [!A-Z]).
+
+-l::
+--list::
+ List up current probe events.
+
+-L::
+--line=::
+ Show source code lines which can be probed. This needs an argument
+ which specifies a range of the source code. (see LINE SYNTAX for detail)
+
+-V::
+--vars=::
+ Show available local variables at given probe point. The argument
+ syntax is same as PROBE SYNTAX, but NO ARGs.
+
+--externs::
+ (Only for --vars) Show external defined variables in addition to local
+ variables.
+
+-f::
+--force::
+ Forcibly add events with existing name.
+
+-n::
+--dry-run::
+ Dry run. With this option, --add and --del doesn't execute actual
+ adding and removal operations.
+
+--max-probes::
+ Set the maximum number of probe points for an event. Default is 128.
+
+PROBE SYNTAX
+------------
+Probe points are defined by following syntax.
+
+ 1) Define event based on function name
+ [EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
+
+ 2) Define event based on source file with line number
+ [EVENT=]SRC:ALN [ARG ...]
+
+ 3) Define event based on source file with lazy pattern
+ [EVENT=]SRC;PTN [ARG ...]
+
+
+'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
+'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
+It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
+'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
+
+PROBE ARGUMENT
+--------------
+Each probe argument follows below syntax.
+
+ [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
+
+'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
+'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
+
+LINE SYNTAX
+-----------
+Line range is described by following syntax.
+
+ "FUNC[:RLN[+NUM|-RLN2]]|SRC[:ALN[+NUM|-ALN2]]"
+
+FUNC specifies the function name of showing lines. 'RLN' is the start line
+number from function entry line, and 'RLN2' is the end line number. As same as
+probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
+and 'ALN2' is end line number in the file. It is also possible to specify how
+many lines to show by using 'NUM'.
+So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
+
+LAZY MATCHING
+-------------
+ The lazy line matching is similar to glob matching but ignoring spaces in both of pattern and target. So this accepts wildcards('*', '?') and character classes(e.g. [a-z], [!A-Z]).
+
+e.g.
+ 'a=*' can matches 'a=b', 'a = b', 'a == b' and so on.
+
+This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.)
+
+
+EXAMPLES
+--------
+Display which lines in schedule() can be probed:
+
+ ./perf probe --line schedule
+
+Add a probe on schedule() function 12th line with recording cpu local variable:
+
+ ./perf probe schedule:12 cpu
+ or
+ ./perf probe --add='schedule:12 cpu'
+
+ this will add one or more probes which has the name start with "schedule".
+
+ Add probes on lines in schedule() function which calls update_rq_clock().
+
+ ./perf probe 'schedule;update_rq_clock*'
+ or
+ ./perf probe --add='schedule;update_rq_clock*'
+
+Delete all probes on schedule().
+
+ ./perf probe --del='schedule*'
+
+
+SEE ALSO
+--------
+linkperf:perf-trace[1], linkperf:perf-record[1]
diff --git a/smartt-perf/Documentation/perf-record.txt b/smartt-perf/Documentation/perf-record.txt
new file mode 100644
index 0000000..e032716
--- /dev/null
+++ b/smartt-perf/Documentation/perf-record.txt
@@ -0,0 +1,142 @@
+perf-record(1)
+==============
+
+NAME
+----
+perf-record - Run a command and record its profile into perf.data
+
+SYNOPSIS
+--------
+[verse]
+'perf record' [-e <EVENT> | --event=EVENT] [-l] [-a] <command>
+'perf record' [-e <EVENT> | --event=EVENT] [-l] [-a] -- <command> [<options>]
+
+DESCRIPTION
+-----------
+This command runs a command and gathers a performance counter profile
+from it, into perf.data - without displaying anything.
+
+This file can then be inspected later on, using 'perf report'.
+
+
+OPTIONS
+-------
+<command>...::
+ Any command you can specify in a shell.
+
+-e::
+--event=::
+ Select the PMU event. Selection can be:
+
+ - a symbolic event name (use 'perf list' to list all events)
+
+ - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a
+ hexadecimal event descriptor.
+
+ - a hardware breakpoint event in the form of '\mem:addr[:access]'
+ where addr is the address in memory you want to break in.
+ Access is the memory access type (read, write, execute) it can
+ be passed as follows: '\mem:addr[:[r][w][x]]'.
+ If you want to profile read-write accesses in 0x1000, just set
+ 'mem:0x1000:rw'.
+
+--filter=<filter>::
+ Event filter.
+
+-a::
+--all-cpus::
+ System-wide collection from all CPUs.
+
+-l::
+ Scale counter values.
+
+-p::
+--pid=::
+ Record events on existing process ID.
+
+-t::
+--tid=::
+ Record events on existing thread ID.
+
+-r::
+--realtime=::
+ Collect data with this RT SCHED_FIFO priority.
+-D::
+--no-delay::
+ Collect data without buffering.
+-A::
+--append::
+ Append to the output file to do incremental profiling.
+
+-f::
+--force::
+ Overwrite existing data file. (deprecated)
+
+-c::
+--count=::
+ Event period to sample.
+
+-o::
+--output=::
+ Output file name.
+
+-i::
+--no-inherit::
+ Child tasks do not inherit counters.
+-F::
+--freq=::
+ Profile at this frequency.
+
+-m::
+--mmap-pages=::
+ Number of mmap data pages.
+
+-g::
+--call-graph::
+ Do call-graph (stack chain/backtrace) recording.
+
+-q::
+--quiet::
+ Don't print any message, useful for scripting.
+
+-v::
+--verbose::
+ Be more verbose (show counter open errors, etc).
+
+-s::
+--stat::
+ Per thread counts.
+
+-d::
+--data::
+ Sample addresses.
+
+-T::
+--timestamp::
+ Sample timestamps. Use it with 'perf report -D' to see the timestamps,
+ for instance.
+
+-n::
+--no-samples::
+ Don't sample.
+
+-R::
+--raw-samples::
+Collect raw sample records from all opened counters (default for tracepoint counters).
+
+-C::
+--cpu::
+Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a
+comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
+In per-thread mode with inheritance mode on (default), samples are captured only when
+the thread executes on the designated CPUs. Default is to monitor all CPUs.
+
+-N::
+--no-buildid-cache::
+Do not update the builid cache. This saves some overhead in situations
+where the information in the perf.data file (which includes buildids)
+is sufficient.
+
+SEE ALSO
+--------
+linkperf:perf-stat[1], linkperf:perf-list[1]
diff --git a/smartt-perf/Documentation/perf-report.txt b/smartt-perf/Documentation/perf-report.txt
new file mode 100644
index 0000000..8ba03d6
--- /dev/null
+++ b/smartt-perf/Documentation/perf-report.txt
@@ -0,0 +1,124 @@
+perf-report(1)
+==============
+
+NAME
+----
+perf-report - Read perf.data (created by perf record) and display the profile
+
+SYNOPSIS
+--------
+[verse]
+'perf report' [-i <file> | --input=file]
+
+DESCRIPTION
+-----------
+This command displays the performance counter profile information recorded
+via perf record.
+
+OPTIONS
+-------
+-i::
+--input=::
+ Input file name. (default: perf.data)
+
+-v::
+--verbose::
+ Be more verbose. (show symbol address, etc)
+
+-d::
+--dsos=::
+ Only consider symbols in these dsos. CSV that understands
+ file://filename entries.
+-n::
+--show-nr-samples::
+ Show the number of samples for each symbol
+
+--showcpuutilization::
+ Show sample percentage for different cpu modes.
+
+-T::
+--threads::
+ Show per-thread event counters
+-C::
+--comms=::
+ Only consider symbols in these comms. CSV that understands
+ file://filename entries.
+-S::
+--symbols=::
+ Only consider these symbols. CSV that understands
+ file://filename entries.
+
+-U::
+--hide-unresolved::
+ Only display entries resolved to a symbol.
+
+-s::
+--sort=::
+ Sort by key(s): pid, comm, dso, symbol, parent.
+
+-p::
+--parent=<regex>::
+ regex filter to identify parent, see: '--sort parent'
+
+-x::
+--exclude-other::
+ Only display entries with parent-match.
+
+-w::
+--column-widths=<width[,width...]>::
+ Force each column width to the provided list, for large terminal
+ readability.
+
+-t::
+--field-separator=::
+
+ Use a special separator character and don't pad with spaces, replacing
+ all occurrences of this separator in symbol names (and other output)
+ with a '.' character, that thus it's the only non valid separator.
+
+-D::
+--dump-raw-trace::
+ Dump raw trace in ASCII.
+
+-g [type,min]::
+--call-graph::
+ Display call chains using type and min percent threshold.
+ type can be either:
+ - flat: single column, linear exposure of call chains.
+ - graph: use a graph tree, displaying absolute overhead rates.
+ - fractal: like graph, but displays relative rates. Each branch of
+ the tree is considered as a new profiled object. +
+ Default: fractal,0.5.
+
+--pretty=<key>::
+ Pretty printing style. key: normal, raw
+
+--stdio:: Use the stdio interface.
+
+--tui:: Use the TUI interface, that is integrated with annotate and allows
+ zooming into DSOs or threads, among other features. Use of --tui
+ requires a tty, if one is not present, as when piping to other
+ commands, the stdio interface is used.
+
+-k::
+--vmlinux=<file>::
+ vmlinux pathname
+
+--kallsyms=<file>::
+ kallsyms pathname
+
+-m::
+--modules::
+ Load module symbols. WARNING: This should only be used with -k and
+ a LIVE kernel.
+
+-f::
+--force::
+ Don't complain, do it.
+
+--symfs=<directory>::
+ Look for files with symbols relative to this directory.
+
+SEE ALSO
+--------
+linkperf:perf-stat[1]
diff --git a/smartt-perf/Documentation/perf-sched.txt b/smartt-perf/Documentation/perf-sched.txt
new file mode 100644
index 0000000..46822d5
--- /dev/null
+++ b/smartt-perf/Documentation/perf-sched.txt
@@ -0,0 +1,55 @@
+perf-sched(1)
+==============
+
+NAME
+----
+perf-sched - Tool to trace/measure scheduler properties (latencies)
+
+SYNOPSIS
+--------
+[verse]
+'perf sched' {record|latency|map|replay|trace}
+
+DESCRIPTION
+-----------
+There are five variants of perf sched:
+
+ 'perf sched record <command>' to record the scheduling events
+ of an arbitrary workload.
+
+ 'perf sched latency' to report the per task scheduling latencies
+ and other scheduling properties of the workload.
+
+ 'perf sched trace' to see a detailed trace of the workload that
+ was recorded.
+
+ 'perf sched replay' to simulate the workload that was recorded
+ via perf sched record. (this is done by starting up mockup threads
+ that mimic the workload based on the events in the trace. These
+ threads can then replay the timings (CPU runtime and sleep patterns)
+ of the workload as it occurred when it was recorded - and can repeat
+ it a number of times, measuring its performance.)
+
+ 'perf sched map' to print a textual context-switching outline of
+ workload captured via perf sched record. Columns stand for
+ individual CPUs, and the two-letter shortcuts stand for tasks that
+ are running on a CPU. A '*' denotes the CPU that had the event, and
+ a dot signals an idle CPU.
+
+OPTIONS
+-------
+-i::
+--input=<file>::
+ Input file name. (default: perf.data)
+
+-v::
+--verbose::
+ Be more verbose. (show symbol address, etc)
+
+-D::
+--dump-raw-trace=::
+ Display verbose dump of the sched data.
+
+SEE ALSO
+--------
+linkperf:perf-record[1]
diff --git a/smartt-perf/Documentation/perf-script-perl.txt b/smartt-perf/Documentation/perf-script-perl.txt
new file mode 100644
index 0000000..5bb41e5
--- /dev/null
+++ b/smartt-perf/Documentation/perf-script-perl.txt
@@ -0,0 +1,217 @@
+perf-script-perl(1)
+==================
+
+NAME
+----
+perf-script-perl - Process trace data with a Perl script
+
+SYNOPSIS
+--------
+[verse]
+'perf script' [-s [Perl]:script[.pl] ]
+
+DESCRIPTION
+-----------
+
+This perf script option is used to process perf script data using perf's
+built-in Perl interpreter. It reads and processes the input file and
+displays the results of the trace analysis implemented in the given
+Perl script, if any.
+
+STARTER SCRIPTS
+---------------
+
+You can avoid reading the rest of this document by running 'perf script
+-g perl' in the same directory as an existing perf.data trace file.
+That will generate a starter script containing a handler for each of
+the event types in the trace file; it simply prints every available
+field for each event in the trace file.
+
+You can also look at the existing scripts in
+~/libexec/perf-core/scripts/perl for typical examples showing how to
+do basic things like aggregate event data, print results, etc. Also,
+the check-perf-script.pl script, while not interesting for its results,
+attempts to exercise all of the main scripting features.
+
+EVENT HANDLERS
+--------------
+
+When perf script is invoked using a trace script, a user-defined
+'handler function' is called for each event in the trace. If there's
+no handler function defined for a given event type, the event is
+ignored (or passed to a 'trace_handled' function, see below) and the
+next event is processed.
+
+Most of the event's field values are passed as arguments to the
+handler function; some of the less common ones aren't - those are
+available as calls back into the perf executable (see below).
+
+As an example, the following perf record command can be used to record
+all sched_wakeup events in the system:
+
+ # perf record -a -e sched:sched_wakeup
+
+Traces meant to be processed using a script should be recorded with
+the above option: -a to enable system-wide collection.
+
+The format file for the sched_wakep event defines the following fields
+(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
+
+----
+ format:
+ field:unsigned short common_type;
+ field:unsigned char common_flags;
+ field:unsigned char common_preempt_count;
+ field:int common_pid;
+ field:int common_lock_depth;
+
+ field:char comm[TASK_COMM_LEN];
+ field:pid_t pid;
+ field:int prio;
+ field:int success;
+ field:int target_cpu;
+----
+
+The handler function for this event would be defined as:
+
+----
+sub sched::sched_wakeup
+{
+ my ($event_name, $context, $common_cpu, $common_secs,
+ $common_nsecs, $common_pid, $common_comm,
+ $comm, $pid, $prio, $success, $target_cpu) = @_;
+}
+----
+
+The handler function takes the form subsystem::event_name.
+
+The $common_* arguments in the handler's argument list are the set of
+arguments passed to all event handlers; some of the fields correspond
+to the common_* fields in the format file, but some are synthesized,
+and some of the common_* fields aren't common enough to to be passed
+to every event as arguments but are available as library functions.
+
+Here's a brief description of each of the invariant event args:
+
+ $event_name the name of the event as text
+ $context an opaque 'cookie' used in calls back into perf
+ $common_cpu the cpu the event occurred on
+ $common_secs the secs portion of the event timestamp
+ $common_nsecs the nsecs portion of the event timestamp
+ $common_pid the pid of the current task
+ $common_comm the name of the current process
+
+All of the remaining fields in the event's format file have
+counterparts as handler function arguments of the same name, as can be
+seen in the example above.
+
+The above provides the basics needed to directly access every field of
+every event in a trace, which covers 90% of what you need to know to
+write a useful trace script. The sections below cover the rest.
+
+SCRIPT LAYOUT
+-------------
+
+Every perf script Perl script should start by setting up a Perl module
+search path and 'use'ing a few support modules (see module
+descriptions below):
+
+----
+ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/perf-script-Util/lib";
+ use lib "./perf-script-Util/lib";
+ use Perf::Trace::Core;
+ use Perf::Trace::Context;
+ use Perf::Trace::Util;
+----
+
+The rest of the script can contain handler functions and support
+functions in any order.
+
+Aside from the event handler functions discussed above, every script
+can implement a set of optional functions:
+
+*trace_begin*, if defined, is called before any event is processed and
+gives scripts a chance to do setup tasks:
+
+----
+ sub trace_begin
+ {
+ }
+----
+
+*trace_end*, if defined, is called after all events have been
+ processed and gives scripts a chance to do end-of-script tasks, such
+ as display results:
+
+----
+sub trace_end
+{
+}
+----
+
+*trace_unhandled*, if defined, is called after for any event that
+ doesn't have a handler explicitly defined for it. The standard set
+ of common arguments are passed into it:
+
+----
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs,
+ $common_nsecs, $common_pid, $common_comm) = @_;
+}
+----
+
+The remaining sections provide descriptions of each of the available
+built-in perf script Perl modules and their associated functions.
+
+AVAILABLE MODULES AND FUNCTIONS
+-------------------------------
+
+The following sections describe the functions and variables available
+via the various Perf::Trace::* Perl modules. To use the functions and
+variables from the given module, add the corresponding 'use
+Perf::Trace::XXX' line to your perf script script.
+
+Perf::Trace::Core Module
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+These functions provide some essential functions to user scripts.
+
+The *flag_str* and *symbol_str* functions provide human-readable
+strings for flag and symbolic fields. These correspond to the strings
+and values parsed from the 'print fmt' fields of the event format
+files:
+
+ flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name
+ symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name
+
+Perf::Trace::Context Module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some of the 'common' fields in the event format file aren't all that
+common, but need to be made accessible to user scripts nonetheless.
+
+Perf::Trace::Context defines a set of functions that can be used to
+access this data in the context of the current event. Each of these
+functions expects a $context variable, which is the same as the
+$context variable passed into every event handler as the second
+argument.
+
+ common_pc($context) - returns common_preempt count for the current event
+ common_flags($context) - returns common_flags for the current event
+ common_lock_depth($context) - returns common_lock_depth for the current event
+
+Perf::Trace::Util Module
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Various utility functions for use with perf script:
+
+ nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair
+ nsecs_secs($nsecs) - returns whole secs portion given nsecs
+ nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs
+ nsecs_str($nsecs) - returns printable string in the form secs.nsecs
+ avg($total, $n) - returns average given a sum and a total number of values
+
+SEE ALSO
+--------
+linkperf:perf-script[1]
diff --git a/smartt-perf/Documentation/perf-script-python.txt b/smartt-perf/Documentation/perf-script-python.txt
new file mode 100644
index 0000000..36b3827
--- /dev/null
+++ b/smartt-perf/Documentation/perf-script-python.txt
@@ -0,0 +1,623 @@
+perf-script-python(1)
+====================
+
+NAME
+----
+perf-script-python - Process trace data with a Python script
+
+SYNOPSIS
+--------
+[verse]
+'perf script' [-s [Python]:script[.py] ]
+
+DESCRIPTION
+-----------
+
+This perf script option is used to process perf script data using perf's
+built-in Python interpreter. It reads and processes the input file and
+displays the results of the trace analysis implemented in the given
+Python script, if any.
+
+A QUICK EXAMPLE
+---------------
+
+This section shows the process, start to finish, of creating a working
+Python script that aggregates and extracts useful information from a
+raw perf script stream. You can avoid reading the rest of this
+document if an example is enough for you; the rest of the document
+provides more details on each step and lists the library functions
+available to script writers.
+
+This example actually details the steps that were used to create the
+'syscall-counts' script you see when you list the available perf script
+scripts via 'perf script -l'. As such, this script also shows how to
+integrate your script into the list of general-purpose 'perf script'
+scripts listed by that command.
+
+The syscall-counts script is a simple script, but demonstrates all the
+basic ideas necessary to create a useful script. Here's an example
+of its output (syscall names are not yet supported, they will appear
+as numbers):
+
+----
+syscall events:
+
+event count
+---------------------------------------- -----------
+sys_write 455067
+sys_getdents 4072
+sys_close 3037
+sys_swapoff 1769
+sys_read 923
+sys_sched_setparam 826
+sys_open 331
+sys_newfstat 326
+sys_mmap 217
+sys_munmap 216
+sys_futex 141
+sys_select 102
+sys_poll 84
+sys_setitimer 12
+sys_writev 8
+15 8
+sys_lseek 7
+sys_rt_sigprocmask 6
+sys_wait4 3
+sys_ioctl 3
+sys_set_robust_list 1
+sys_exit 1
+56 1
+sys_access 1
+----
+
+Basically our task is to keep a per-syscall tally that gets updated
+every time a system call occurs in the system. Our script will do
+that, but first we need to record the data that will be processed by
+that script. Theoretically, there are a couple of ways we could do
+that:
+
+- we could enable every event under the tracing/events/syscalls
+ directory, but this is over 600 syscalls, well beyond the number
+ allowable by perf. These individual syscall events will however be
+ useful if we want to later use the guidance we get from the
+ general-purpose scripts to drill down and get more detail about
+ individual syscalls of interest.
+
+- we can enable the sys_enter and/or sys_exit syscalls found under
+ tracing/events/raw_syscalls. These are called for all syscalls; the
+ 'id' field can be used to distinguish between individual syscall
+ numbers.
+
+For this script, we only need to know that a syscall was entered; we
+don't care how it exited, so we'll use 'perf record' to record only
+the sys_enter events:
+
+----
+# perf record -a -e raw_syscalls:sys_enter
+
+^C[ perf record: Woken up 1 times to write data ]
+[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ]
+----
+
+The options basically say to collect data for every syscall event
+system-wide and multiplex the per-cpu output into a single stream.
+That single stream will be recorded in a file in the current directory
+called perf.data.
+
+Once we have a perf.data file containing our data, we can use the -g
+'perf script' option to generate a Python script that will contain a
+callback handler for each event type found in the perf.data trace
+stream (for more details, see the STARTER SCRIPTS section).
+
+----
+# perf script -g python
+generated Python script: perf-script.py
+
+The output file created also in the current directory is named
+perf-script.py. Here's the file in its entirety:
+
+# perf script event handlers, generated by perf script -g python
+# Licensed under the terms of the GNU GPL License version 2
+
+# The common_* event handler fields are the most useful fields common to
+# all events. They don't necessarily correspond to the 'common_*' fields
+# in the format files. Those fields not available as handler params can
+# be retrieved using Python functions of the form common_*(context).
+# See the perf-script-python Documentation for the list of available functions.
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/perf-script-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+
+def trace_begin():
+ print "in trace_begin"
+
+def trace_end():
+ print "in trace_end"
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+ print_header(event_name, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+
+ print "id=%d, args=%s\n" % \
+ (id, args),
+
+def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm):
+ print_header(event_name, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+
+def print_header(event_name, cpu, secs, nsecs, pid, comm):
+ print "%-20s %5u %05u.%09u %8u %-20s " % \
+ (event_name, cpu, secs, nsecs, pid, comm),
+----
+
+At the top is a comment block followed by some import statements and a
+path append which every perf script script should include.
+
+Following that are a couple generated functions, trace_begin() and
+trace_end(), which are called at the beginning and the end of the
+script respectively (for more details, see the SCRIPT_LAYOUT section
+below).
+
+Following those are the 'event handler' functions generated one for
+every event in the 'perf record' output. The handler functions take
+the form subsystem__event_name, and contain named parameters, one for
+each field in the event; in this case, there's only one event,
+raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for
+more info on event handlers).
+
+The final couple of functions are, like the begin and end functions,
+generated for every script. The first, trace_unhandled(), is called
+every time the script finds an event in the perf.data file that
+doesn't correspond to any event handler in the script. This could
+mean either that the record step recorded event types that it wasn't
+really interested in, or the script was run against a trace file that
+doesn't correspond to the script.
+
+The script generated by -g option simply prints a line for each
+event found in the trace stream i.e. it basically just dumps the event
+and its parameter values to stdout. The print_header() function is
+simply a utility function used for that purpose. Let's rename the
+script and run it to see the default output:
+
+----
+# mv perf-script.py syscall-counts.py
+# perf script -s syscall-counts.py
+
+raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args=
+raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args=
+raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args=
+raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args=
+raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args=
+raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args=
+raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args=
+raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args=
+.
+.
+.
+----
+
+Of course, for this script, we're not interested in printing every
+trace event, but rather aggregating it in a useful way. So we'll get
+rid of everything to do with printing as well as the trace_begin() and
+trace_unhandled() functions, which we won't be using. That leaves us
+with this minimalistic skeleton:
+
+----
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/perf-script-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+
+def trace_end():
+ print "in trace_end"
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+----
+
+In trace_end(), we'll simply print the results, but first we need to
+generate some results to print. To do that we need to have our
+sys_enter() handler do the necessary tallying until all events have
+been counted. A hash table indexed by syscall id is a good way to
+store that information; every time the sys_enter() handler is called,
+we simply increment a count associated with that hash entry indexed by
+that syscall id:
+
+----
+ syscalls = autodict()
+
+ try:
+ syscalls[id] += 1
+ except TypeError:
+ syscalls[id] = 1
+----
+
+The syscalls 'autodict' object is a special kind of Python dictionary
+(implemented in Core.py) that implements Perl's 'autovivifying' hashes
+in Python i.e. with autovivifying hashes, you can assign nested hash
+values without having to go to the trouble of creating intermediate
+levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create
+the intermediate hash levels and finally assign the value 1 to the
+hash entry for 'id' (because the value being assigned isn't a hash
+object itself, the initial value is assigned in the TypeError
+exception. Well, there may be a better way to do this in Python but
+that's what works for now).
+
+Putting that code into the raw_syscalls__sys_enter() handler, we
+effectively end up with a single-level dictionary keyed on syscall id
+and having the counts we've tallied as values.
+
+The print_syscall_totals() function iterates over the entries in the
+dictionary and displays a line for each entry containing the syscall
+name (the dictonary keys contain the syscall ids, which are passed to
+the Util function syscall_name(), which translates the raw syscall
+numbers to the corresponding syscall name strings). The output is
+displayed after all the events in the trace have been processed, by
+calling the print_syscall_totals() function from the trace_end()
+handler called at the end of script processing.
+
+The final script producing the output shown above is shown in its
+entirety below (syscall_name() helper is not yet available, you can
+only deal with id's for now):
+
+----
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/perf-script-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import *
+
+syscalls = autodict()
+
+def trace_end():
+ print_syscall_totals()
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+ try:
+ syscalls[id] += 1
+ except TypeError:
+ syscalls[id] = 1
+
+def print_syscall_totals():
+ if for_comm is not None:
+ print "\nsyscall events for %s:\n\n" % (for_comm),
+ else:
+ print "\nsyscall events:\n\n",
+
+ print "%-40s %10s\n" % ("event", "count"),
+ print "%-40s %10s\n" % ("----------------------------------------", \
+ "-----------"),
+
+ for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
+ reverse = True):
+ print "%-40s %10d\n" % (syscall_name(id), val),
+----
+
+The script can be run just as before:
+
+ # perf script -s syscall-counts.py
+
+So those are the essential steps in writing and running a script. The
+process can be generalized to any tracepoint or set of tracepoints
+you're interested in - basically find the tracepoint(s) you're
+interested in by looking at the list of available events shown by
+'perf list' and/or look in /sys/kernel/debug/tracing events for
+detailed event and field info, record the corresponding trace data
+using 'perf record', passing it the list of interesting events,
+generate a skeleton script using 'perf script -g python' and modify the
+code to aggregate and display it for your particular needs.
+
+After you've done that you may end up with a general-purpose script
+that you want to keep around and have available for future use. By
+writing a couple of very simple shell scripts and putting them in the
+right place, you can have your script listed alongside the other
+scripts listed by the 'perf script -l' command e.g.:
+
+----
+root@tropicana:~# perf script -l
+List of available trace scripts:
+ workqueue-stats workqueue stats (ins/exe/create/destroy)
+ wakeup-latency system-wide min/max/avg wakeup latency
+ rw-by-file <comm> r/w activity for a program, by file
+ rw-by-pid system-wide r/w activity
+----
+
+A nice side effect of doing this is that you also then capture the
+probably lengthy 'perf record' command needed to record the events for
+the script.
+
+To have the script appear as a 'built-in' script, you write two simple
+scripts, one for recording and one for 'reporting'.
+
+The 'record' script is a shell script with the same base name as your
+script, but with -record appended. The shell script should be put
+into the perf/scripts/python/bin directory in the kernel source tree.
+In that script, you write the 'perf record' command-line needed for
+your script:
+
+----
+# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record
+
+#!/bin/bash
+perf record -a -e raw_syscalls:sys_enter
+----
+
+The 'report' script is also a shell script with the same base name as
+your script, but with -report appended. It should also be located in
+the perf/scripts/python/bin directory. In that script, you write the
+'perf script -s' command-line needed for running your script:
+
+----
+# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report
+
+#!/bin/bash
+# description: system-wide syscall counts
+perf script -s ~/libexec/perf-core/scripts/python/syscall-counts.py
+----
+
+Note that the location of the Python script given in the shell script
+is in the libexec/perf-core/scripts/python directory - this is where
+the script will be copied by 'make install' when you install perf.
+For the installation to install your script there, your script needs
+to be located in the perf/scripts/python directory in the kernel
+source tree:
+
+----
+# ls -al kernel-source/tools/perf/scripts/python
+
+root@tropicana:/home/trz/src/tip# ls -al tools/perf/scripts/python
+total 32
+drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 .
+drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 ..
+drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin
+-rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py
+drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 perf-script-Util
+-rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py
+----
+
+Once you've done that (don't forget to do a new 'make install',
+otherwise your script won't show up at run-time), 'perf script -l'
+should show a new entry for your script:
+
+----
+root@tropicana:~# perf script -l
+List of available trace scripts:
+ workqueue-stats workqueue stats (ins/exe/create/destroy)
+ wakeup-latency system-wide min/max/avg wakeup latency
+ rw-by-file <comm> r/w activity for a program, by file
+ rw-by-pid system-wide r/w activity
+ syscall-counts system-wide syscall counts
+----
+
+You can now perform the record step via 'perf script record':
+
+ # perf script record syscall-counts
+
+and display the output using 'perf script report':
+
+ # perf script report syscall-counts
+
+STARTER SCRIPTS
+---------------
+
+You can quickly get started writing a script for a particular set of
+trace data by generating a skeleton script using 'perf script -g
+python' in the same directory as an existing perf.data trace file.
+That will generate a starter script containing a handler for each of
+the event types in the trace file; it simply prints every available
+field for each event in the trace file.
+
+You can also look at the existing scripts in
+~/libexec/perf-core/scripts/python for typical examples showing how to
+do basic things like aggregate event data, print results, etc. Also,
+the check-perf-script.py script, while not interesting for its results,
+attempts to exercise all of the main scripting features.
+
+EVENT HANDLERS
+--------------
+
+When perf script is invoked using a trace script, a user-defined
+'handler function' is called for each event in the trace. If there's
+no handler function defined for a given event type, the event is
+ignored (or passed to a 'trace_handled' function, see below) and the
+next event is processed.
+
+Most of the event's field values are passed as arguments to the
+handler function; some of the less common ones aren't - those are
+available as calls back into the perf executable (see below).
+
+As an example, the following perf record command can be used to record
+all sched_wakeup events in the system:
+
+ # perf record -a -e sched:sched_wakeup
+
+Traces meant to be processed using a script should be recorded with
+the above option: -a to enable system-wide collection.
+
+The format file for the sched_wakep event defines the following fields
+(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
+
+----
+ format:
+ field:unsigned short common_type;
+ field:unsigned char common_flags;
+ field:unsigned char common_preempt_count;
+ field:int common_pid;
+ field:int common_lock_depth;
+
+ field:char comm[TASK_COMM_LEN];
+ field:pid_t pid;
+ field:int prio;
+ field:int success;
+ field:int target_cpu;
+----
+
+The handler function for this event would be defined as:
+
+----
+def sched__sched_wakeup(event_name, context, common_cpu, common_secs,
+ common_nsecs, common_pid, common_comm,
+ comm, pid, prio, success, target_cpu):
+ pass
+----
+
+The handler function takes the form subsystem__event_name.
+
+The common_* arguments in the handler's argument list are the set of
+arguments passed to all event handlers; some of the fields correspond
+to the common_* fields in the format file, but some are synthesized,
+and some of the common_* fields aren't common enough to to be passed
+to every event as arguments but are available as library functions.
+
+Here's a brief description of each of the invariant event args:
+
+ event_name the name of the event as text
+ context an opaque 'cookie' used in calls back into perf
+ common_cpu the cpu the event occurred on
+ common_secs the secs portion of the event timestamp
+ common_nsecs the nsecs portion of the event timestamp
+ common_pid the pid of the current task
+ common_comm the name of the current process
+
+All of the remaining fields in the event's format file have
+counterparts as handler function arguments of the same name, as can be
+seen in the example above.
+
+The above provides the basics needed to directly access every field of
+every event in a trace, which covers 90% of what you need to know to
+write a useful trace script. The sections below cover the rest.
+
+SCRIPT LAYOUT
+-------------
+
+Every perf script Python script should start by setting up a Python
+module search path and 'import'ing a few support modules (see module
+descriptions below):
+
+----
+ import os
+ import sys
+
+ sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/perf-script-Util/lib/Perf/Trace')
+
+ from perf_trace_context import *
+ from Core import *
+----
+
+The rest of the script can contain handler functions and support
+functions in any order.
+
+Aside from the event handler functions discussed above, every script
+can implement a set of optional functions:
+
+*trace_begin*, if defined, is called before any event is processed and
+gives scripts a chance to do setup tasks:
+
+----
+def trace_begin:
+ pass
+----
+
+*trace_end*, if defined, is called after all events have been
+ processed and gives scripts a chance to do end-of-script tasks, such
+ as display results:
+
+----
+def trace_end:
+ pass
+----
+
+*trace_unhandled*, if defined, is called after for any event that
+ doesn't have a handler explicitly defined for it. The standard set
+ of common arguments are passed into it:
+
+----
+def trace_unhandled(event_name, context, common_cpu, common_secs,
+ common_nsecs, common_pid, common_comm):
+ pass
+----
+
+The remaining sections provide descriptions of each of the available
+built-in perf script Python modules and their associated functions.
+
+AVAILABLE MODULES AND FUNCTIONS
+-------------------------------
+
+The following sections describe the functions and variables available
+via the various perf script Python modules. To use the functions and
+variables from the given module, add the corresponding 'from XXXX
+import' line to your perf script script.
+
+Core.py Module
+~~~~~~~~~~~~~~
+
+These functions provide some essential functions to user scripts.
+
+The *flag_str* and *symbol_str* functions provide human-readable
+strings for flag and symbolic fields. These correspond to the strings
+and values parsed from the 'print fmt' fields of the event format
+files:
+
+ flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name
+ symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name
+
+The *autodict* function returns a special kind of Python
+dictionary that implements Perl's 'autovivifying' hashes in Python
+i.e. with autovivifying hashes, you can assign nested hash values
+without having to go to the trouble of creating intermediate levels if
+they don't exist.
+
+ autodict() - returns an autovivifying dictionary instance
+
+
+perf_trace_context Module
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some of the 'common' fields in the event format file aren't all that
+common, but need to be made accessible to user scripts nonetheless.
+
+perf_trace_context defines a set of functions that can be used to
+access this data in the context of the current event. Each of these
+functions expects a context variable, which is the same as the
+context variable passed into every event handler as the second
+argument.
+
+ common_pc(context) - returns common_preempt count for the current event
+ common_flags(context) - returns common_flags for the current event
+ common_lock_depth(context) - returns common_lock_depth for the current event
+
+Util.py Module
+~~~~~~~~~~~~~~
+
+Various utility functions for use with perf script:
+
+ nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair
+ nsecs_secs(nsecs) - returns whole secs portion given nsecs
+ nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs
+ nsecs_str(nsecs) - returns printable string in the form secs.nsecs
+ avg(total, n) - returns average given a sum and a total number of values
+
+SEE ALSO
+--------
+linkperf:perf-script[1]
diff --git a/smartt-perf/Documentation/perf-script.txt b/smartt-perf/Documentation/perf-script.txt
new file mode 100644
index 0000000..29ad942
--- /dev/null
+++ b/smartt-perf/Documentation/perf-script.txt
@@ -0,0 +1,118 @@
+perf-script(1)
+=============
+
+NAME
+----
+perf-script - Read perf.data (created by perf record) and display trace output
+
+SYNOPSIS
+--------
+[verse]
+'perf script' [<options>]
+'perf script' [<options>] record <script> [<record-options>] <command>
+'perf script' [<options>] report <script> [script-args]
+'perf script' [<options>] <script> <required-script-args> [<record-options>] <command>
+'perf script' [<options>] <top-script> [script-args]
+
+DESCRIPTION
+-----------
+This command reads the input file and displays the trace recorded.
+
+There are several variants of perf script:
+
+ 'perf script' to see a detailed trace of the workload that was
+ recorded.
+
+ You can also run a set of pre-canned scripts that aggregate and
+ summarize the raw trace data in various ways (the list of scripts is
+ available via 'perf script -l'). The following variants allow you to
+ record and run those scripts:
+
+ 'perf script record <script> <command>' to record the events required
+ for 'perf script report'. <script> is the name displayed in the
+ output of 'perf script --list' i.e. the actual script name minus any
+ language extension. If <command> is not specified, the events are
+ recorded using the -a (system-wide) 'perf record' option.
+
+ 'perf script report <script> [args]' to run and display the results
+ of <script>. <script> is the name displayed in the output of 'perf
+ trace --list' i.e. the actual script name minus any language
+ extension. The perf.data output from a previous run of 'perf script
+ record <script>' is used and should be present for this command to
+ succeed. [args] refers to the (mainly optional) args expected by
+ the script.
+
+ 'perf script <script> <required-script-args> <command>' to both
+ record the events required for <script> and to run the <script>
+ using 'live-mode' i.e. without writing anything to disk. <script>
+ is the name displayed in the output of 'perf script --list' i.e. the
+ actual script name minus any language extension. If <command> is
+ not specified, the events are recorded using the -a (system-wide)
+ 'perf record' option. If <script> has any required args, they
+ should be specified before <command>. This mode doesn't allow for
+ optional script args to be specified; if optional script args are
+ desired, they can be specified using separate 'perf script record'
+ and 'perf script report' commands, with the stdout of the record step
+ piped to the stdin of the report script, using the '-o -' and '-i -'
+ options of the corresponding commands.
+
+ 'perf script <top-script>' to both record the events required for
+ <top-script> and to run the <top-script> using 'live-mode'
+ i.e. without writing anything to disk. <top-script> is the name
+ displayed in the output of 'perf script --list' i.e. the actual
+ script name minus any language extension; a <top-script> is defined
+ as any script name ending with the string 'top'.
+
+ [<record-options>] can be passed to the record steps of 'perf script
+ record' and 'live-mode' variants; this isn't possible however for
+ <top-script> 'live-mode' or 'perf script report' variants.
+
+ See the 'SEE ALSO' section for links to language-specific
+ information on how to write and run your own trace scripts.
+
+OPTIONS
+-------
+<command>...::
+ Any command you can specify in a shell.
+
+-D::
+--dump-raw-script=::
+ Display verbose dump of the trace data.
+
+-L::
+--Latency=::
+ Show latency attributes (irqs/preemption disabled, etc).
+
+-l::
+--list=::
+ Display a list of available trace scripts.
+
+-s ['lang']::
+--script=::
+ Process trace data with the given script ([lang]:script[.ext]).
+ If the string 'lang' is specified in place of a script name, a
+ list of supported languages will be displayed instead.
+
+-g::
+--gen-script=::
+ Generate perf-script.[ext] starter script for given language,
+ using current perf.data.
+
+-a::
+ Force system-wide collection. Scripts run without a <command>
+ normally use -a by default, while scripts run with a <command>
+ normally don't - this option allows the latter to be run in
+ system-wide mode.
+
+-i::
+--input=::
+ Input file name.
+
+-d::
+--debug-mode::
+ Do various checks like samples ordering and lost events.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-script-perl[1],
+linkperf:perf-script-python[1]
diff --git a/smartt-perf/Documentation/perf-stat.txt b/smartt-perf/Documentation/perf-stat.txt
new file mode 100644
index 0000000..b6da7af
--- /dev/null
+++ b/smartt-perf/Documentation/perf-stat.txt
@@ -0,0 +1,106 @@
+perf-stat(1)
+============
+
+NAME
+----
+perf-stat - Run a command and gather performance counter statistics
+
+SYNOPSIS
+--------
+[verse]
+'perf stat' [-e <EVENT> | --event=EVENT] [-a] <command>
+'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>]
+
+DESCRIPTION
+-----------
+This command runs a command and gathers performance counter statistics
+from it.
+
+
+OPTIONS
+-------
+<command>...::
+ Any command you can specify in a shell.
+
+
+-e::
+--event=::
+ Select the PMU event. Selection can be a symbolic event name
+ (use 'perf list' to list all events) or a raw PMU
+ event (eventsel+umask) in the form of rNNN where NNN is a
+ hexadecimal event descriptor.
+
+-i::
+--no-inherit::
+ child tasks do not inherit counters
+-p::
+--pid=<pid>::
+ stat events on existing process id
+
+-t::
+--tid=<tid>::
+ stat events on existing thread id
+
+
+-a::
+--all-cpus::
+ system-wide collection from all CPUs
+
+-c::
+--scale::
+ scale/normalize counter values
+
+-r::
+--repeat=<n>::
+ repeat command and print average + stddev (max: 100)
+
+-B::
+--big-num::
+ print large numbers with thousands' separators according to locale
+
+-C::
+--cpu=::
+Count only on the list of CPUs provided. Multiple CPUs can be provided as a
+comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
+In per-thread mode, this option is ignored. The -a option is still necessary
+to activate system-wide monitoring. Default is to count on all CPUs.
+
+-A::
+--no-aggr::
+Do not aggregate counts across all monitored CPUs in system-wide mode (-a).
+This option is only valid in system-wide mode.
+
+-n::
+--null::
+ null run - don't start any counters
+
+-v::
+--verbose::
+ be more verbose (show counter open errors, etc)
+
+-x SEP::
+--field-separator SEP::
+print counts using a CSV-style output to make it easy to import directly into
+spreadsheets. Columns are separated by the string specified in SEP.
+
+EXAMPLES
+--------
+
+$ perf stat -- make -j
+
+ Performance counter stats for 'make -j':
+
+ 8117.370256 task clock ticks # 11.281 CPU utilization factor
+ 678 context switches # 0.000 M/sec
+ 133 CPU migrations # 0.000 M/sec
+ 235724 pagefaults # 0.029 M/sec
+ 24821162526 CPU cycles # 3057.784 M/sec
+ 18687303457 instructions # 2302.138 M/sec
+ 172158895 cache references # 21.209 M/sec
+ 27075259 cache misses # 3.335 M/sec
+
+ Wall-clock time elapsed: 719.554352 msecs
+
+SEE ALSO
+--------
+linkperf:perf-top[1], linkperf:perf-list[1]
diff --git a/smartt-perf/Documentation/perf-test.txt b/smartt-perf/Documentation/perf-test.txt
new file mode 100644
index 0000000..2c3b462
--- /dev/null
+++ b/smartt-perf/Documentation/perf-test.txt
@@ -0,0 +1,22 @@
+perf-test(1)
+============
+
+NAME
+----
+perf-test - Runs sanity tests.
+
+SYNOPSIS
+--------
+[verse]
+'perf test <options>'
+
+DESCRIPTION
+-----------
+This command does assorted sanity tests, initially through linked routines but
+also will look for a directory with more tests in the form of scripts.
+
+OPTIONS
+-------
+-v::
+--verbose::
+ Be more verbose.
diff --git a/smartt-perf/Documentation/perf-timechart.txt b/smartt-perf/Documentation/perf-timechart.txt
new file mode 100644
index 0000000..d7b79e2
--- /dev/null
+++ b/smartt-perf/Documentation/perf-timechart.txt
@@ -0,0 +1,46 @@
+perf-timechart(1)
+=================
+
+NAME
+----
+perf-timechart - Tool to visualize total system behavior during a workload
+
+SYNOPSIS
+--------
+[verse]
+'perf timechart' {record}
+
+DESCRIPTION
+-----------
+There are two variants of perf timechart:
+
+ 'perf timechart record <command>' to record the system level events
+ of an arbitrary workload.
+
+ 'perf timechart' to turn a trace into a Scalable Vector Graphics file,
+ that can be viewed with popular SVG viewers such as 'Inkscape'.
+
+OPTIONS
+-------
+-o::
+--output=::
+ Select the output file (default: output.svg)
+-i::
+--input=::
+ Select the input file (default: perf.data)
+-w::
+--width=::
+ Select the width of the SVG file (default: 1000)
+-P::
+--power-only::
+ Only output the CPU power section of the diagram
+-p::
+--process::
+ Select the processes to display, by name or PID
+
+--symfs=<directory>::
+ Look for files with symbols relative to this directory.
+
+SEE ALSO
+--------
+linkperf:perf-record[1]
diff --git a/smartt-perf/Documentation/perf-top.txt b/smartt-perf/Documentation/perf-top.txt
new file mode 100644
index 0000000..f6eb1cd
--- /dev/null
+++ b/smartt-perf/Documentation/perf-top.txt
@@ -0,0 +1,147 @@
+perf-top(1)
+===========
+
+NAME
+----
+perf-top - System profiling tool.
+
+SYNOPSIS
+--------
+[verse]
+'perf top' [-e <EVENT> | --event=EVENT] [<options>]
+
+DESCRIPTION
+-----------
+This command generates and displays a performance counter profile in real time.
+
+
+OPTIONS
+-------
+-a::
+--all-cpus::
+ System-wide collection. (default)
+
+-c <count>::
+--count=<count>::
+ Event period to sample.
+
+-C <cpu-list>::
+--cpu=<cpu>::
+Monitor only on the list of CPUs provided. Multiple CPUs can be provided as a
+comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
+Default is to monitor all CPUS.
+
+-d <seconds>::
+--delay=<seconds>::
+ Number of seconds to delay between refreshes.
+
+-e <event>::
+--event=<event>::
+ Select the PMU event. Selection can be a symbolic event name
+ (use 'perf list' to list all events) or a raw PMU
+ event (eventsel+umask) in the form of rNNN where NNN is a
+ hexadecimal event descriptor.
+
+-E <entries>::
+--entries=<entries>::
+ Display this many functions.
+
+-f <count>::
+--count-filter=<count>::
+ Only display functions with more events than this.
+
+-g::
+--group::
+ Put the counters into a counter group.
+
+-F <freq>::
+--freq=<freq>::
+ Profile at this frequency.
+
+-i::
+--inherit::
+ Child tasks inherit counters, only makes sens with -p option.
+
+-k <path>::
+--vmlinux=<path>::
+ Path to vmlinux. Required for annotation functionality.
+
+-m <pages>::
+--mmap-pages=<pages>::
+ Number of mmapped data pages.
+
+-p <pid>::
+--pid=<pid>::
+ Profile events on existing Process ID.
+
+-t <tid>::
+--tid=<tid>::
+ Profile events on existing thread ID.
+
+-r <priority>::
+--realtime=<priority>::
+ Collect data with this RT SCHED_FIFO priority.
+
+-s <symbol>::
+--sym-annotate=<symbol>::
+ Annotate this symbol.
+
+-K::
+--hide_kernel_symbols::
+ Hide kernel symbols.
+
+-U::
+--hide_user_symbols::
+ Hide user symbols.
+
+-D::
+--dump-symtab::
+ Dump the symbol table used for profiling.
+
+-v::
+--verbose::
+ Be more verbose (show counter open errors, etc).
+
+-z::
+--zero::
+ Zero history across display updates.
+
+INTERACTIVE PROMPTING KEYS
+--------------------------
+
+[d]::
+ Display refresh delay.
+
+[e]::
+ Number of entries to display.
+
+[E]::
+ Event to display when multiple counters are active.
+
+[f]::
+ Profile display filter (>= hit count).
+
+[F]::
+ Annotation display filter (>= % of total).
+
+[s]::
+ Annotate symbol.
+
+[S]::
+ Stop annotation, return to full profile display.
+
+[w]::
+ Toggle between weighted sum and individual count[E]r profile.
+
+[z]::
+ Toggle event count zeroing across display updates.
+
+[qQ]::
+ Quit.
+
+Pressing any unmapped key displays a menu, and prompts for input.
+
+
+SEE ALSO
+--------
+linkperf:perf-stat[1], linkperf:perf-list[1]
diff --git a/smartt-perf/Documentation/perf-trace-perl.txt b/smartt-perf/Documentation/perf-trace-perl.txt
new file mode 100644
index 0000000..ee6525e
--- /dev/null
+++ b/smartt-perf/Documentation/perf-trace-perl.txt
@@ -0,0 +1,217 @@
+perf-trace-perl(1)
+==================
+
+NAME
+----
+perf-trace-perl - Process trace data with a Perl script
+
+SYNOPSIS
+--------
+[verse]
+'perf trace' [-s [Perl]:script[.pl] ]
+
+DESCRIPTION
+-----------
+
+This perf trace option is used to process perf trace data using perf's
+built-in Perl interpreter. It reads and processes the input file and
+displays the results of the trace analysis implemented in the given
+Perl script, if any.
+
+STARTER SCRIPTS
+---------------
+
+You can avoid reading the rest of this document by running 'perf trace
+-g perl' in the same directory as an existing perf.data trace file.
+That will generate a starter script containing a handler for each of
+the event types in the trace file; it simply prints every available
+field for each event in the trace file.
+
+You can also look at the existing scripts in
+~/libexec/perf-core/scripts/perl for typical examples showing how to
+do basic things like aggregate event data, print results, etc. Also,
+the check-perf-trace.pl script, while not interesting for its results,
+attempts to exercise all of the main scripting features.
+
+EVENT HANDLERS
+--------------
+
+When perf trace is invoked using a trace script, a user-defined
+'handler function' is called for each event in the trace. If there's
+no handler function defined for a given event type, the event is
+ignored (or passed to a 'trace_handled' function, see below) and the
+next event is processed.
+
+Most of the event's field values are passed as arguments to the
+handler function; some of the less common ones aren't - those are
+available as calls back into the perf executable (see below).
+
+As an example, the following perf record command can be used to record
+all sched_wakeup events in the system:
+
+ # perf record -a -e sched:sched_wakeup
+
+Traces meant to be processed using a script should be recorded with
+the above option: -a to enable system-wide collection.
+
+The format file for the sched_wakep event defines the following fields
+(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
+
+----
+ format:
+ field:unsigned short common_type;
+ field:unsigned char common_flags;
+ field:unsigned char common_preempt_count;
+ field:int common_pid;
+ field:int common_lock_depth;
+
+ field:char comm[TASK_COMM_LEN];
+ field:pid_t pid;
+ field:int prio;
+ field:int success;
+ field:int target_cpu;
+----
+
+The handler function for this event would be defined as:
+
+----
+sub sched::sched_wakeup
+{
+ my ($event_name, $context, $common_cpu, $common_secs,
+ $common_nsecs, $common_pid, $common_comm,
+ $comm, $pid, $prio, $success, $target_cpu) = @_;
+}
+----
+
+The handler function takes the form subsystem::event_name.
+
+The $common_* arguments in the handler's argument list are the set of
+arguments passed to all event handlers; some of the fields correspond
+to the common_* fields in the format file, but some are synthesized,
+and some of the common_* fields aren't common enough to to be passed
+to every event as arguments but are available as library functions.
+
+Here's a brief description of each of the invariant event args:
+
+ $event_name the name of the event as text
+ $context an opaque 'cookie' used in calls back into perf
+ $common_cpu the cpu the event occurred on
+ $common_secs the secs portion of the event timestamp
+ $common_nsecs the nsecs portion of the event timestamp
+ $common_pid the pid of the current task
+ $common_comm the name of the current process
+
+All of the remaining fields in the event's format file have
+counterparts as handler function arguments of the same name, as can be
+seen in the example above.
+
+The above provides the basics needed to directly access every field of
+every event in a trace, which covers 90% of what you need to know to
+write a useful trace script. The sections below cover the rest.
+
+SCRIPT LAYOUT
+-------------
+
+Every perf trace Perl script should start by setting up a Perl module
+search path and 'use'ing a few support modules (see module
+descriptions below):
+
+----
+ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+ use lib "./Perf-Trace-Util/lib";
+ use Perf::Trace::Core;
+ use Perf::Trace::Context;
+ use Perf::Trace::Util;
+----
+
+The rest of the script can contain handler functions and support
+functions in any order.
+
+Aside from the event handler functions discussed above, every script
+can implement a set of optional functions:
+
+*trace_begin*, if defined, is called before any event is processed and
+gives scripts a chance to do setup tasks:
+
+----
+ sub trace_begin
+ {
+ }
+----
+
+*trace_end*, if defined, is called after all events have been
+ processed and gives scripts a chance to do end-of-script tasks, such
+ as display results:
+
+----
+sub trace_end
+{
+}
+----
+
+*trace_unhandled*, if defined, is called after for any event that
+ doesn't have a handler explicitly defined for it. The standard set
+ of common arguments are passed into it:
+
+----
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs,
+ $common_nsecs, $common_pid, $common_comm) = @_;
+}
+----
+
+The remaining sections provide descriptions of each of the available
+built-in perf trace Perl modules and their associated functions.
+
+AVAILABLE MODULES AND FUNCTIONS
+-------------------------------
+
+The following sections describe the functions and variables available
+via the various Perf::Trace::* Perl modules. To use the functions and
+variables from the given module, add the corresponding 'use
+Perf::Trace::XXX' line to your perf trace script.
+
+Perf::Trace::Core Module
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+These functions provide some essential functions to user scripts.
+
+The *flag_str* and *symbol_str* functions provide human-readable
+strings for flag and symbolic fields. These correspond to the strings
+and values parsed from the 'print fmt' fields of the event format
+files:
+
+ flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name
+ symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name
+
+Perf::Trace::Context Module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some of the 'common' fields in the event format file aren't all that
+common, but need to be made accessible to user scripts nonetheless.
+
+Perf::Trace::Context defines a set of functions that can be used to
+access this data in the context of the current event. Each of these
+functions expects a $context variable, which is the same as the
+$context variable passed into every event handler as the second
+argument.
+
+ common_pc($context) - returns common_preempt count for the current event
+ common_flags($context) - returns common_flags for the current event
+ common_lock_depth($context) - returns common_lock_depth for the current event
+
+Perf::Trace::Util Module
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Various utility functions for use with perf trace:
+
+ nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair
+ nsecs_secs($nsecs) - returns whole secs portion given nsecs
+ nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs
+ nsecs_str($nsecs) - returns printable string in the form secs.nsecs
+ avg($total, $n) - returns average given a sum and a total number of values
+
+SEE ALSO
+--------
+linkperf:perf-trace[1]
diff --git a/smartt-perf/Documentation/perf-trace-python.txt b/smartt-perf/Documentation/perf-trace-python.txt
new file mode 100644
index 0000000..693be80
--- /dev/null
+++ b/smartt-perf/Documentation/perf-trace-python.txt
@@ -0,0 +1,623 @@
+perf-trace-python(1)
+====================
+
+NAME
+----
+perf-trace-python - Process trace data with a Python script
+
+SYNOPSIS
+--------
+[verse]
+'perf trace' [-s [Python]:script[.py] ]
+
+DESCRIPTION
+-----------
+
+This perf trace option is used to process perf trace data using perf's
+built-in Python interpreter. It reads and processes the input file and
+displays the results of the trace analysis implemented in the given
+Python script, if any.
+
+A QUICK EXAMPLE
+---------------
+
+This section shows the process, start to finish, of creating a working
+Python script that aggregates and extracts useful information from a
+raw perf trace stream. You can avoid reading the rest of this
+document if an example is enough for you; the rest of the document
+provides more details on each step and lists the library functions
+available to script writers.
+
+This example actually details the steps that were used to create the
+'syscall-counts' script you see when you list the available perf trace
+scripts via 'perf trace -l'. As such, this script also shows how to
+integrate your script into the list of general-purpose 'perf trace'
+scripts listed by that command.
+
+The syscall-counts script is a simple script, but demonstrates all the
+basic ideas necessary to create a useful script. Here's an example
+of its output (syscall names are not yet supported, they will appear
+as numbers):
+
+----
+syscall events:
+
+event count
+---------------------------------------- -----------
+sys_write 455067
+sys_getdents 4072
+sys_close 3037
+sys_swapoff 1769
+sys_read 923
+sys_sched_setparam 826
+sys_open 331
+sys_newfstat 326
+sys_mmap 217
+sys_munmap 216
+sys_futex 141
+sys_select 102
+sys_poll 84
+sys_setitimer 12
+sys_writev 8
+15 8
+sys_lseek 7
+sys_rt_sigprocmask 6
+sys_wait4 3
+sys_ioctl 3
+sys_set_robust_list 1
+sys_exit 1
+56 1
+sys_access 1
+----
+
+Basically our task is to keep a per-syscall tally that gets updated
+every time a system call occurs in the system. Our script will do
+that, but first we need to record the data that will be processed by
+that script. Theoretically, there are a couple of ways we could do
+that:
+
+- we could enable every event under the tracing/events/syscalls
+ directory, but this is over 600 syscalls, well beyond the number
+ allowable by perf. These individual syscall events will however be
+ useful if we want to later use the guidance we get from the
+ general-purpose scripts to drill down and get more detail about
+ individual syscalls of interest.
+
+- we can enable the sys_enter and/or sys_exit syscalls found under
+ tracing/events/raw_syscalls. These are called for all syscalls; the
+ 'id' field can be used to distinguish between individual syscall
+ numbers.
+
+For this script, we only need to know that a syscall was entered; we
+don't care how it exited, so we'll use 'perf record' to record only
+the sys_enter events:
+
+----
+# perf record -a -e raw_syscalls:sys_enter
+
+^C[ perf record: Woken up 1 times to write data ]
+[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ]
+----
+
+The options basically say to collect data for every syscall event
+system-wide and multiplex the per-cpu output into a single stream.
+That single stream will be recorded in a file in the current directory
+called perf.data.
+
+Once we have a perf.data file containing our data, we can use the -g
+'perf trace' option to generate a Python script that will contain a
+callback handler for each event type found in the perf.data trace
+stream (for more details, see the STARTER SCRIPTS section).
+
+----
+# perf trace -g python
+generated Python script: perf-trace.py
+
+The output file created also in the current directory is named
+perf-trace.py. Here's the file in its entirety:
+
+# perf trace event handlers, generated by perf trace -g python
+# Licensed under the terms of the GNU GPL License version 2
+
+# The common_* event handler fields are the most useful fields common to
+# all events. They don't necessarily correspond to the 'common_*' fields
+# in the format files. Those fields not available as handler params can
+# be retrieved using Python functions of the form common_*(context).
+# See the perf-trace-python Documentation for the list of available functions.
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+
+def trace_begin():
+ print "in trace_begin"
+
+def trace_end():
+ print "in trace_end"
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+ print_header(event_name, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+
+ print "id=%d, args=%s\n" % \
+ (id, args),
+
+def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm):
+ print_header(event_name, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+
+def print_header(event_name, cpu, secs, nsecs, pid, comm):
+ print "%-20s %5u %05u.%09u %8u %-20s " % \
+ (event_name, cpu, secs, nsecs, pid, comm),
+----
+
+At the top is a comment block followed by some import statements and a
+path append which every perf trace script should include.
+
+Following that are a couple generated functions, trace_begin() and
+trace_end(), which are called at the beginning and the end of the
+script respectively (for more details, see the SCRIPT_LAYOUT section
+below).
+
+Following those are the 'event handler' functions generated one for
+every event in the 'perf record' output. The handler functions take
+the form subsystem__event_name, and contain named parameters, one for
+each field in the event; in this case, there's only one event,
+raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for
+more info on event handlers).
+
+The final couple of functions are, like the begin and end functions,
+generated for every script. The first, trace_unhandled(), is called
+every time the script finds an event in the perf.data file that
+doesn't correspond to any event handler in the script. This could
+mean either that the record step recorded event types that it wasn't
+really interested in, or the script was run against a trace file that
+doesn't correspond to the script.
+
+The script generated by -g option simply prints a line for each
+event found in the trace stream i.e. it basically just dumps the event
+and its parameter values to stdout. The print_header() function is
+simply a utility function used for that purpose. Let's rename the
+script and run it to see the default output:
+
+----
+# mv perf-trace.py syscall-counts.py
+# perf trace -s syscall-counts.py
+
+raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args=
+raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args=
+raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args=
+raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args=
+raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args=
+raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args=
+raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args=
+raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args=
+.
+.
+.
+----
+
+Of course, for this script, we're not interested in printing every
+trace event, but rather aggregating it in a useful way. So we'll get
+rid of everything to do with printing as well as the trace_begin() and
+trace_unhandled() functions, which we won't be using. That leaves us
+with this minimalistic skeleton:
+
+----
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+
+def trace_end():
+ print "in trace_end"
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+----
+
+In trace_end(), we'll simply print the results, but first we need to
+generate some results to print. To do that we need to have our
+sys_enter() handler do the necessary tallying until all events have
+been counted. A hash table indexed by syscall id is a good way to
+store that information; every time the sys_enter() handler is called,
+we simply increment a count associated with that hash entry indexed by
+that syscall id:
+
+----
+ syscalls = autodict()
+
+ try:
+ syscalls[id] += 1
+ except TypeError:
+ syscalls[id] = 1
+----
+
+The syscalls 'autodict' object is a special kind of Python dictionary
+(implemented in Core.py) that implements Perl's 'autovivifying' hashes
+in Python i.e. with autovivifying hashes, you can assign nested hash
+values without having to go to the trouble of creating intermediate
+levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create
+the intermediate hash levels and finally assign the value 1 to the
+hash entry for 'id' (because the value being assigned isn't a hash
+object itself, the initial value is assigned in the TypeError
+exception. Well, there may be a better way to do this in Python but
+that's what works for now).
+
+Putting that code into the raw_syscalls__sys_enter() handler, we
+effectively end up with a single-level dictionary keyed on syscall id
+and having the counts we've tallied as values.
+
+The print_syscall_totals() function iterates over the entries in the
+dictionary and displays a line for each entry containing the syscall
+name (the dictonary keys contain the syscall ids, which are passed to
+the Util function syscall_name(), which translates the raw syscall
+numbers to the corresponding syscall name strings). The output is
+displayed after all the events in the trace have been processed, by
+calling the print_syscall_totals() function from the trace_end()
+handler called at the end of script processing.
+
+The final script producing the output shown above is shown in its
+entirety below (syscall_name() helper is not yet available, you can
+only deal with id's for now):
+
+----
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import *
+
+syscalls = autodict()
+
+def trace_end():
+ print_syscall_totals()
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+ try:
+ syscalls[id] += 1
+ except TypeError:
+ syscalls[id] = 1
+
+def print_syscall_totals():
+ if for_comm is not None:
+ print "\nsyscall events for %s:\n\n" % (for_comm),
+ else:
+ print "\nsyscall events:\n\n",
+
+ print "%-40s %10s\n" % ("event", "count"),
+ print "%-40s %10s\n" % ("----------------------------------------", \
+ "-----------"),
+
+ for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
+ reverse = True):
+ print "%-40s %10d\n" % (syscall_name(id), val),
+----
+
+The script can be run just as before:
+
+ # perf trace -s syscall-counts.py
+
+So those are the essential steps in writing and running a script. The
+process can be generalized to any tracepoint or set of tracepoints
+you're interested in - basically find the tracepoint(s) you're
+interested in by looking at the list of available events shown by
+'perf list' and/or look in /sys/kernel/debug/tracing events for
+detailed event and field info, record the corresponding trace data
+using 'perf record', passing it the list of interesting events,
+generate a skeleton script using 'perf trace -g python' and modify the
+code to aggregate and display it for your particular needs.
+
+After you've done that you may end up with a general-purpose script
+that you want to keep around and have available for future use. By
+writing a couple of very simple shell scripts and putting them in the
+right place, you can have your script listed alongside the other
+scripts listed by the 'perf trace -l' command e.g.:
+
+----
+root@tropicana:~# perf trace -l
+List of available trace scripts:
+ workqueue-stats workqueue stats (ins/exe/create/destroy)
+ wakeup-latency system-wide min/max/avg wakeup latency
+ rw-by-file <comm> r/w activity for a program, by file
+ rw-by-pid system-wide r/w activity
+----
+
+A nice side effect of doing this is that you also then capture the
+probably lengthy 'perf record' command needed to record the events for
+the script.
+
+To have the script appear as a 'built-in' script, you write two simple
+scripts, one for recording and one for 'reporting'.
+
+The 'record' script is a shell script with the same base name as your
+script, but with -record appended. The shell script should be put
+into the perf/scripts/python/bin directory in the kernel source tree.
+In that script, you write the 'perf record' command-line needed for
+your script:
+
+----
+# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record
+
+#!/bin/bash
+perf record -a -e raw_syscalls:sys_enter
+----
+
+The 'report' script is also a shell script with the same base name as
+your script, but with -report appended. It should also be located in
+the perf/scripts/python/bin directory. In that script, you write the
+'perf trace -s' command-line needed for running your script:
+
+----
+# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report
+
+#!/bin/bash
+# description: system-wide syscall counts
+perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts.py
+----
+
+Note that the location of the Python script given in the shell script
+is in the libexec/perf-core/scripts/python directory - this is where
+the script will be copied by 'make install' when you install perf.
+For the installation to install your script there, your script needs
+to be located in the perf/scripts/python directory in the kernel
+source tree:
+
+----
+# ls -al kernel-source/tools/perf/scripts/python
+
+root@tropicana:/home/trz/src/tip# ls -al tools/perf/scripts/python
+total 32
+drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 .
+drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 ..
+drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin
+-rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-trace.py
+drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 Perf-Trace-Util
+-rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py
+----
+
+Once you've done that (don't forget to do a new 'make install',
+otherwise your script won't show up at run-time), 'perf trace -l'
+should show a new entry for your script:
+
+----
+root@tropicana:~# perf trace -l
+List of available trace scripts:
+ workqueue-stats workqueue stats (ins/exe/create/destroy)
+ wakeup-latency system-wide min/max/avg wakeup latency
+ rw-by-file <comm> r/w activity for a program, by file
+ rw-by-pid system-wide r/w activity
+ syscall-counts system-wide syscall counts
+----
+
+You can now perform the record step via 'perf trace record':
+
+ # perf trace record syscall-counts
+
+and display the output using 'perf trace report':
+
+ # perf trace report syscall-counts
+
+STARTER SCRIPTS
+---------------
+
+You can quickly get started writing a script for a particular set of
+trace data by generating a skeleton script using 'perf trace -g
+python' in the same directory as an existing perf.data trace file.
+That will generate a starter script containing a handler for each of
+the event types in the trace file; it simply prints every available
+field for each event in the trace file.
+
+You can also look at the existing scripts in
+~/libexec/perf-core/scripts/python for typical examples showing how to
+do basic things like aggregate event data, print results, etc. Also,
+the check-perf-trace.py script, while not interesting for its results,
+attempts to exercise all of the main scripting features.
+
+EVENT HANDLERS
+--------------
+
+When perf trace is invoked using a trace script, a user-defined
+'handler function' is called for each event in the trace. If there's
+no handler function defined for a given event type, the event is
+ignored (or passed to a 'trace_handled' function, see below) and the
+next event is processed.
+
+Most of the event's field values are passed as arguments to the
+handler function; some of the less common ones aren't - those are
+available as calls back into the perf executable (see below).
+
+As an example, the following perf record command can be used to record
+all sched_wakeup events in the system:
+
+ # perf record -a -e sched:sched_wakeup
+
+Traces meant to be processed using a script should be recorded with
+the above option: -a to enable system-wide collection.
+
+The format file for the sched_wakep event defines the following fields
+(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
+
+----
+ format:
+ field:unsigned short common_type;
+ field:unsigned char common_flags;
+ field:unsigned char common_preempt_count;
+ field:int common_pid;
+ field:int common_lock_depth;
+
+ field:char comm[TASK_COMM_LEN];
+ field:pid_t pid;
+ field:int prio;
+ field:int success;
+ field:int target_cpu;
+----
+
+The handler function for this event would be defined as:
+
+----
+def sched__sched_wakeup(event_name, context, common_cpu, common_secs,
+ common_nsecs, common_pid, common_comm,
+ comm, pid, prio, success, target_cpu):
+ pass
+----
+
+The handler function takes the form subsystem__event_name.
+
+The common_* arguments in the handler's argument list are the set of
+arguments passed to all event handlers; some of the fields correspond
+to the common_* fields in the format file, but some are synthesized,
+and some of the common_* fields aren't common enough to to be passed
+to every event as arguments but are available as library functions.
+
+Here's a brief description of each of the invariant event args:
+
+ event_name the name of the event as text
+ context an opaque 'cookie' used in calls back into perf
+ common_cpu the cpu the event occurred on
+ common_secs the secs portion of the event timestamp
+ common_nsecs the nsecs portion of the event timestamp
+ common_pid the pid of the current task
+ common_comm the name of the current process
+
+All of the remaining fields in the event's format file have
+counterparts as handler function arguments of the same name, as can be
+seen in the example above.
+
+The above provides the basics needed to directly access every field of
+every event in a trace, which covers 90% of what you need to know to
+write a useful trace script. The sections below cover the rest.
+
+SCRIPT LAYOUT
+-------------
+
+Every perf trace Python script should start by setting up a Python
+module search path and 'import'ing a few support modules (see module
+descriptions below):
+
+----
+ import os
+ import sys
+
+ sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+ from perf_trace_context import *
+ from Core import *
+----
+
+The rest of the script can contain handler functions and support
+functions in any order.
+
+Aside from the event handler functions discussed above, every script
+can implement a set of optional functions:
+
+*trace_begin*, if defined, is called before any event is processed and
+gives scripts a chance to do setup tasks:
+
+----
+def trace_begin:
+ pass
+----
+
+*trace_end*, if defined, is called after all events have been
+ processed and gives scripts a chance to do end-of-script tasks, such
+ as display results:
+
+----
+def trace_end:
+ pass
+----
+
+*trace_unhandled*, if defined, is called after for any event that
+ doesn't have a handler explicitly defined for it. The standard set
+ of common arguments are passed into it:
+
+----
+def trace_unhandled(event_name, context, common_cpu, common_secs,
+ common_nsecs, common_pid, common_comm):
+ pass
+----
+
+The remaining sections provide descriptions of each of the available
+built-in perf trace Python modules and their associated functions.
+
+AVAILABLE MODULES AND FUNCTIONS
+-------------------------------
+
+The following sections describe the functions and variables available
+via the various perf trace Python modules. To use the functions and
+variables from the given module, add the corresponding 'from XXXX
+import' line to your perf trace script.
+
+Core.py Module
+~~~~~~~~~~~~~~
+
+These functions provide some essential functions to user scripts.
+
+The *flag_str* and *symbol_str* functions provide human-readable
+strings for flag and symbolic fields. These correspond to the strings
+and values parsed from the 'print fmt' fields of the event format
+files:
+
+ flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name
+ symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name
+
+The *autodict* function returns a special kind of Python
+dictionary that implements Perl's 'autovivifying' hashes in Python
+i.e. with autovivifying hashes, you can assign nested hash values
+without having to go to the trouble of creating intermediate levels if
+they don't exist.
+
+ autodict() - returns an autovivifying dictionary instance
+
+
+perf_trace_context Module
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some of the 'common' fields in the event format file aren't all that
+common, but need to be made accessible to user scripts nonetheless.
+
+perf_trace_context defines a set of functions that can be used to
+access this data in the context of the current event. Each of these
+functions expects a context variable, which is the same as the
+context variable passed into every event handler as the second
+argument.
+
+ common_pc(context) - returns common_preempt count for the current event
+ common_flags(context) - returns common_flags for the current event
+ common_lock_depth(context) - returns common_lock_depth for the current event
+
+Util.py Module
+~~~~~~~~~~~~~~
+
+Various utility functions for use with perf trace:
+
+ nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair
+ nsecs_secs(nsecs) - returns whole secs portion given nsecs
+ nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs
+ nsecs_str(nsecs) - returns printable string in the form secs.nsecs
+ avg(total, n) - returns average given a sum and a total number of values
+
+SEE ALSO
+--------
+linkperf:perf-trace[1]
diff --git a/smartt-perf/Documentation/perf-trace.txt b/smartt-perf/Documentation/perf-trace.txt
new file mode 100644
index 0000000..122ec9d
--- /dev/null
+++ b/smartt-perf/Documentation/perf-trace.txt
@@ -0,0 +1,70 @@
+perf-trace(1)
+=============
+
+NAME
+----
+perf-trace - Read perf.data (created by perf record) and display trace output
+
+SYNOPSIS
+--------
+[verse]
+'perf trace' {record <script> | report <script> [args] }
+
+DESCRIPTION
+-----------
+This command reads the input file and displays the trace recorded.
+
+There are several variants of perf trace:
+
+ 'perf trace' to see a detailed trace of the workload that was
+ recorded.
+
+ You can also run a set of pre-canned scripts that aggregate and
+ summarize the raw trace data in various ways (the list of scripts is
+ available via 'perf trace -l'). The following variants allow you to
+ record and run those scripts:
+
+ 'perf trace record <script>' to record the events required for 'perf
+ trace report'. <script> is the name displayed in the output of
+ 'perf trace --list' i.e. the actual script name minus any language
+ extension.
+
+ 'perf trace report <script>' to run and display the results of
+ <script>. <script> is the name displayed in the output of 'perf
+ trace --list' i.e. the actual script name minus any language
+ extension. The perf.data output from a previous run of 'perf trace
+ record <script>' is used and should be present for this command to
+ succeed.
+
+ See the 'SEE ALSO' section for links to language-specific
+ information on how to write and run your own trace scripts.
+
+OPTIONS
+-------
+-D::
+--dump-raw-trace=::
+ Display verbose dump of the trace data.
+
+-L::
+--Latency=::
+ Show latency attributes (irqs/preemption disabled, etc).
+
+-l::
+--list=::
+ Display a list of available trace scripts.
+
+-s ['lang']::
+--script=::
+ Process trace data with the given script ([lang]:script[.ext]).
+ If the string 'lang' is specified in place of a script name, a
+ list of supported languages will be displayed instead.
+
+-g::
+--gen-script=::
+ Generate perf-trace.[ext] starter script for given language,
+ using current perf.data.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-trace-perl[1],
+linkperf:perf-trace-python[1]
diff --git a/smartt-perf/Documentation/perf.txt b/smartt-perf/Documentation/perf.txt
new file mode 100644
index 0000000..0eeb247
--- /dev/null
+++ b/smartt-perf/Documentation/perf.txt
@@ -0,0 +1,24 @@
+perf(1)
+=======
+
+NAME
+----
+perf - Performance analysis tools for Linux
+
+SYNOPSIS
+--------
+[verse]
+'perf' [--version] [--help] COMMAND [ARGS]
+
+DESCRIPTION
+-----------
+Performance counters for Linux are a new kernel-based subsystem
+that provide a framework for all things performance analysis. It
+covers hardware level (CPU/PMU, Performance Monitoring Unit) features
+and software features (software counters, tracepoints) as well.
+
+SEE ALSO
+--------
+linkperf:perf-stat[1], linkperf:perf-top[1],
+linkperf:perf-record[1], linkperf:perf-report[1],
+linkperf:perf-list[1]
diff --git a/smartt-perf/MANIFEST b/smartt-perf/MANIFEST
new file mode 100644
index 0000000..c12659d
--- /dev/null
+++ b/smartt-perf/MANIFEST
@@ -0,0 +1,13 @@
+tools/perf
+include/linux/perf_event.h
+include/linux/rbtree.h
+include/linux/list.h
+include/linux/hash.h
+include/linux/stringify.h
+lib/rbtree.c
+include/linux/swab.h
+arch/*/include/asm/unistd*.h
+arch/*/lib/memcpy*.S
+include/linux/poison.h
+include/linux/magic.h
+include/linux/hw_breakpoint.h
diff --git a/smartt-perf/Makefile b/smartt-perf/Makefile
new file mode 100644
index 0000000..a534b13
--- /dev/null
+++ b/smartt-perf/Makefile
@@ -0,0 +1,1309 @@
+ifeq ("$(origin O)", "command line")
+ OUTPUT := $(O)/
+endif
+
+# The default target of this Makefile is...
+all::
+
+ifneq ($(OUTPUT),)
+# check that the output directory actually exists
+OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
+$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
+endif
+
+# Define V=1 to have a more verbose compile.
+# Define V=2 to have an even more verbose compile.
+#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+#
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# This also implies MOZILLA_SHA1.
+#
+# Define CURLDIR=/foo/bar if your curl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
+#
+# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
+# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
+#
+# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
+# do not support the 'size specifiers' introduced by C99, namely ll, hh,
+# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
+# some C compilers supported these specifiers prior to C99 as an extension.
+#
+# Define NO_STRCASESTR if you don't have strcasestr.
+#
+# Define NO_MEMMEM if you don't have memmem.
+#
+# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
+# If your compiler also does not support long long or does not have
+# strtoull, define NO_STRTOULL.
+#
+# Define NO_SETENV if you don't have setenv in the C library.
+#
+# Define NO_UNSETENV if you don't have unsetenv in the C library.
+#
+# Define NO_MKDTEMP if you don't have mkdtemp in the C library.
+#
+# Define NO_SYS_SELECT_H if you don't have sys/select.h.
+#
+# Define NO_SYMLINK_HEAD if you never want .perf/HEAD to be a symbolic link.
+# Enable it on Windows. By default, symrefs are still used.
+#
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
+# tests. These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
+# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+# installed in /sw, but don't want PERF to link against any libraries
+# installed there. If defined you may specify your own (or Fink's)
+# include directories and library directories by defining CFLAGS
+# and LDFLAGS appropriately.
+#
+# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+# have DarwinPorts installed in /opt/local, but don't want PERF to
+# link against any libraries installed there. If defined you may
+# specify your own (or DarwinPort's) include directories and
+# library directories by defining CFLAGS and LDFLAGS appropriately.
+#
+# Define PPC_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for PowerPC.
+#
+# Define ARM_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for ARM.
+#
+# Define MOZILLA_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
+# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
+# choice) has very fast version optimized for i586.
+#
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+#
+# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
+#
+# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
+# Patrick Mauritz).
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_PTHREADS if you do not have or do not want to use Pthreads.
+#
+# Define NO_PREAD if you have a problem with pread() system call (e.g.
+# cygwin.dll before v1.5.22).
+#
+# Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
+# generally faster on your platform than accessing the working directory.
+#
+# Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support
+# the executable mode bit, but doesn't really do so.
+#
+# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
+#
+# Define NO_SOCKADDR_STORAGE if your platform does not have struct
+# sockaddr_storage.
+#
+# Define NO_ICONV if your libc does not properly support iconv.
+#
+# Define OLD_ICONV if your library has an old iconv(), where the second
+# (input buffer pointer) parameter is declared with type (const char **).
+#
+# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
+#
+# Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
+# that tells runtime paths to dynamic libraries;
+# "-Wl,-rpath=/path/lib" is used instead.
+#
+# Define USE_NSEC below if you want perf to care about sub-second file mtimes
+# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
+# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
+# randomly break unless your underlying filesystem supports those sub-second
+# times (my ext3 doesn't).
+#
+# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
+# "st_ctim"
+#
+# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec"
+# available. This automatically turns USE_NSEC off.
+#
+# Define USE_STDEV below if you want perf to care about the underlying device
+# change being considered an inode change from the update-index perspective.
+#
+# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
+# field that counts the on-disk footprint in 512-byte blocks.
+#
+# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
+#
+# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
+#
+# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
+# MakeMaker (e.g. using ActiveState under Cygwin).
+#
+# Define NO_PERL if you do not want Perl scripts or libraries at all.
+#
+# Define INTERNAL_QSORT to use Git's implementation of qsort(), which
+# is a simplified version of the merge sort used in glibc. This is
+# recommended if Git triggers O(n^2) behavior in your platform's qsort().
+#
+# Define NO_EXTERNAL_GREP if you don't want "perf grep" to ever call
+# your external grep (e.g., if your system lacks grep, if its grep is
+# broken, or spawning external process is slower than built-in grep perf has).
+#
+# Define LDFLAGS=-static to build a static binary.
+#
+# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds.
+#
+# Define NO_DWARF if you do not want debug-info analysis feature at all.
+
+$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
+ @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
+-include $(OUTPUT)PERF-VERSION-FILE
+
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
+uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
+uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
+uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
+uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
+
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
+ -e s/arm.*/arm/ -e s/sa110/arm/ \
+ -e s/s390x/s390/ -e s/parisc64/parisc/ \
+ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
+ -e s/sh[234].*/sh/ )
+
+# Additional ARCH settings for x86
+ifeq ($(ARCH),i386)
+ ARCH := x86
+endif
+ifeq ($(ARCH),x86_64)
+ RAW_ARCH := x86_64
+ ARCH := x86
+ ARCH_CFLAGS := -DARCH_X86_64
+ ARCH_INCLUDE = util/include/arch/x86/lib/memcpy_64.S
+endif
+
+# CFLAGS and LDFLAGS are for the users to override from the command line.
+
+#
+# Include saner warnings here, which can catch bugs:
+#
+
+EXTRA_WARNINGS := -Wformat
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes
+EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement
+
+ifeq ("$(origin DEBUG)", "command line")
+ PERF_DEBUG = $(DEBUG)
+endif
+ifndef PERF_DEBUG
+ CFLAGS_OPTIMIZE = -O6
+endif
+
+CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
+EXTLIBS = -lpthread -lrt -lelf -lm
+ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+ALL_LDFLAGS = $(LDFLAGS)
+STRIP ?= strip
+
+# Among the variables below, these:
+# perfexecdir
+# template_dir
+# mandir
+# infodir
+# htmldir
+# ETC_PERFCONFIG (but not sysconfdir)
+# can be specified as a relative path some/where/else;
+# this is interpreted as relative to $(prefix) and "perf" at
+# runtime figures out where they are based on the path to the executable.
+# This can help installing the suite in a relocatable way.
+
+# Make the path relative to DESTDIR, not to prefix
+ifndef DESTDIR
+prefix = $(HOME)
+endif
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+mandir = share/man
+infodir = share/info
+perfexecdir = libexec/perf-core
+sharedir = $(prefix)/share
+template_dir = share/perf-core/templates
+htmldir = share/doc/perf-doc
+ifeq ($(prefix),/usr)
+sysconfdir = /etc
+ETC_PERFCONFIG = $(sysconfdir)/perfconfig
+else
+sysconfdir = $(prefix)/etc
+ETC_PERFCONFIG = etc/perfconfig
+endif
+lib = lib
+
+export prefix bindir sharedir sysconfdir
+
+CC = $(CROSS_COMPILE)gcc
+AR = $(CROSS_COMPILE)ar
+RM = rm -f
+MKDIR = mkdir
+TAR = tar
+FIND = find
+INSTALL = install
+RPMBUILD = rpmbuild
+PTHREAD_LIBS = -lpthread
+
+# sparse is architecture-neutral, which means that we need to tell it
+# explicitly what architecture to check for. Fix this up for yours..
+SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
+
+ifeq ($(V), 2)
+ QUIET_STDERR = ">/dev/null"
+else
+ QUIET_STDERR = ">/dev/null 2>&1"
+endif
+
+-include feature-tests.mak
+
+ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y)
+ CFLAGS := $(CFLAGS) -fstack-protector-all
+endif
+
+ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wstack-protector),y)
+ CFLAGS := $(CFLAGS) -Wstack-protector
+endif
+
+ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wvolatile-register-var),y)
+ CFLAGS := $(CFLAGS) -Wvolatile-register-var
+endif
+
+### --- END CONFIGURATION SECTION ---
+
+# Those must not be GNU-specific; they are shared with perl/ which may
+# be built by a different compiler. (Note that this is an artifact now
+# but it still might be nice to keep that distinction.)
+BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include
+BASIC_LDFLAGS =
+
+# Guard against environment variables
+BUILTIN_OBJS =
+BUILT_INS =
+COMPAT_CFLAGS =
+COMPAT_OBJS =
+LIB_H =
+LIB_OBJS =
+SCRIPT_PERL =
+SCRIPT_SH =
+TEST_PROGRAMS =
+
+SCRIPT_SH += perf-archive.sh
+
+grep-libs = $(filter -l%,$(1))
+strip-libs = $(filter-out -l%,$(1))
+
+#
+# No Perl scripts right now:
+#
+
+# SCRIPT_PERL += perf-add--interactive.perl
+
+SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
+ $(patsubst %.perl,%,$(SCRIPT_PERL))
+
+# Empty...
+EXTRA_PROGRAMS =
+
+# ... and all the rest that could be moved out of bindir to perfexecdir
+PROGRAMS += $(EXTRA_PROGRAMS)
+
+#
+# Single 'perf' binary right now:
+#
+PROGRAMS += $(OUTPUT)perf
+
+# List built-in command $C whose implementation cmd_$C() is not in
+# builtin-$C.o but is linked in as part of some other command.
+#
+
+# what 'all' will build and 'install' will install, in perfexecdir
+ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
+
+# what 'all' will build but not install in perfexecdir
+OTHER_PROGRAMS = $(OUTPUT)perf$X
+
+# Set paths to tools early so that they can be used for version tests.
+ifndef SHELL_PATH
+ SHELL_PATH = /bin/sh
+endif
+ifndef PERL_PATH
+ PERL_PATH = /usr/bin/perl
+endif
+
+export PERL_PATH
+
+LIB_FILE=$(OUTPUT)libperf.a
+
+LIB_H += util/include/linux/perf_event.h
+LIB_H += util/include/linux/rbtree.h
+LIB_H += util/include/linux/list.h
+LIB_H += util/include/linux/hash.h
+LIB_H += util/include/linux/stringify.h
+LIB_H += util/include/linux/bitmap.h
+LIB_H += util/include/linux/bitops.h
+LIB_H += util/include/linux/compiler.h
+LIB_H += util/include/linux/ctype.h
+LIB_H += util/include/linux/kernel.h
+LIB_H += util/include/linux/list.h
+LIB_H += util/include/linux/module.h
+LIB_H += util/include/linux/poison.h
+LIB_H += util/include/linux/prefetch.h
+LIB_H += util/include/linux/rbtree.h
+LIB_H += util/include/linux/string.h
+LIB_H += util/include/linux/types.h
+LIB_H += util/include/linux/linkage.h
+LIB_H += util/include/asm/asm-offsets.h
+LIB_H += util/include/asm/bug.h
+LIB_H += util/include/asm/byteorder.h
+LIB_H += util/include/asm/hweight.h
+LIB_H += util/include/asm/swab.h
+LIB_H += util/include/asm/system.h
+LIB_H += util/include/asm/uaccess.h
+LIB_H += util/include/dwarf-regs.h
+LIB_H += util/include/asm/dwarf2.h
+LIB_H += util/include/asm/cpufeature.h
+LIB_H += perf.h
+LIB_H += util/cache.h
+LIB_H += util/callchain.h
+LIB_H += util/build-id.h
+LIB_H += util/debug.h
+LIB_H += util/debugfs.h
+LIB_H += util/event.h
+LIB_H += util/evsel.h
+LIB_H += util/exec_cmd.h
+LIB_H += util/types.h
+LIB_H += util/levenshtein.h
+LIB_H += util/map.h
+LIB_H += util/parse-options.h
+LIB_H += util/parse-events.h
+LIB_H += util/quote.h
+LIB_H += util/util.h
+LIB_H += util/xyarray.h
+LIB_H += util/header.h
+LIB_H += util/help.h
+LIB_H += util/session.h
+LIB_H += util/strbuf.h
+LIB_H += util/strlist.h
+LIB_H += util/svghelper.h
+LIB_H += util/run-command.h
+LIB_H += util/sigchain.h
+LIB_H += util/symbol.h
+LIB_H += util/color.h
+LIB_H += util/values.h
+LIB_H += util/sort.h
+LIB_H += util/hist.h
+LIB_H += util/thread.h
+LIB_H += util/trace-event.h
+LIB_H += util/probe-finder.h
+LIB_H += util/probe-event.h
+LIB_H += util/pstack.h
+LIB_H += util/cpumap.h
+LIB_H += util/connect.h
+LIB_H += $(ARCH_INCLUDE)
+
+LIB_OBJS += $(OUTPUT)util/abspath.o
+LIB_OBJS += $(OUTPUT)util/alias.o
+LIB_OBJS += $(OUTPUT)util/build-id.o
+LIB_OBJS += $(OUTPUT)util/config.o
+LIB_OBJS += $(OUTPUT)util/ctype.o
+LIB_OBJS += $(OUTPUT)util/debugfs.o
+LIB_OBJS += $(OUTPUT)util/environment.o
+LIB_OBJS += $(OUTPUT)util/event.o
+LIB_OBJS += $(OUTPUT)util/evsel.o
+LIB_OBJS += $(OUTPUT)util/exec_cmd.o
+LIB_OBJS += $(OUTPUT)util/help.o
+LIB_OBJS += $(OUTPUT)util/levenshtein.o
+LIB_OBJS += $(OUTPUT)util/parse-options.o
+LIB_OBJS += $(OUTPUT)util/parse-events.o
+LIB_OBJS += $(OUTPUT)util/path.o
+LIB_OBJS += $(OUTPUT)util/rbtree.o
+LIB_OBJS += $(OUTPUT)util/bitmap.o
+LIB_OBJS += $(OUTPUT)util/hweight.o
+LIB_OBJS += $(OUTPUT)util/run-command.o
+LIB_OBJS += $(OUTPUT)util/quote.o
+LIB_OBJS += $(OUTPUT)util/strbuf.o
+LIB_OBJS += $(OUTPUT)util/string.o
+LIB_OBJS += $(OUTPUT)util/strlist.o
+LIB_OBJS += $(OUTPUT)util/usage.o
+LIB_OBJS += $(OUTPUT)util/wrapper.o
+LIB_OBJS += $(OUTPUT)util/sigchain.o
+LIB_OBJS += $(OUTPUT)util/symbol.o
+LIB_OBJS += $(OUTPUT)util/color.o
+LIB_OBJS += $(OUTPUT)util/pager.o
+LIB_OBJS += $(OUTPUT)util/header.o
+LIB_OBJS += $(OUTPUT)util/callchain.o
+LIB_OBJS += $(OUTPUT)util/values.o
+LIB_OBJS += $(OUTPUT)util/debug.o
+LIB_OBJS += $(OUTPUT)util/map.o
+LIB_OBJS += $(OUTPUT)util/pstack.o
+LIB_OBJS += $(OUTPUT)util/session.o
+LIB_OBJS += $(OUTPUT)util/thread.o
+LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
+LIB_OBJS += $(OUTPUT)util/trace-event-read.o
+LIB_OBJS += $(OUTPUT)util/trace-event-info.o
+LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
+LIB_OBJS += $(OUTPUT)util/svghelper.o
+LIB_OBJS += $(OUTPUT)util/sort.o
+LIB_OBJS += $(OUTPUT)util/hist.o
+LIB_OBJS += $(OUTPUT)util/probe-event.o
+LIB_OBJS += $(OUTPUT)util/util.o
+LIB_OBJS += $(OUTPUT)util/xyarray.o
+LIB_OBJS += $(OUTPUT)util/cpumap.o
+LIB_OBJS += $(OUTPUT)util/connect.o
+
+BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
+
+BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
+
+# Benchmark modules
+BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o
+BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o
+ifeq ($(RAW_ARCH),x86_64)
+BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o
+endif
+BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o
+
+BUILTIN_OBJS += $(OUTPUT)builtin-diff.o
+BUILTIN_OBJS += $(OUTPUT)builtin-help.o
+BUILTIN_OBJS += $(OUTPUT)builtin-sched.o
+BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o
+BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o
+BUILTIN_OBJS += $(OUTPUT)builtin-list.o
+BUILTIN_OBJS += $(OUTPUT)builtin-record.o
+BUILTIN_OBJS += $(OUTPUT)builtin-report.o
+BUILTIN_OBJS += $(OUTPUT)builtin-stat.o
+BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o
+BUILTIN_OBJS += $(OUTPUT)builtin-top.o
+BUILTIN_OBJS += $(OUTPUT)builtin-script.o
+BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
+BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
+BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
+BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
+BUILTIN_OBJS += $(OUTPUT)builtin-test.o
+BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
+
+PERFLIBS = $(LIB_FILE)
+
+#
+# Platform specific tweaks
+#
+
+# We choose to avoid "if .. else if .. else .. endif endif"
+# because maintaining the nesting to match is a pain. If
+# we had "elif" things would have been much nicer...
+
+-include config.mak.autogen
+-include config.mak
+
+ifndef NO_DWARF
+FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
+ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
+ msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
+ NO_DWARF := 1
+endif # Dwarf support
+endif # NO_DWARF
+
+-include arch/$(ARCH)/Makefile
+
+ifeq ($(uname_S),Darwin)
+ ifndef NO_FINK
+ ifeq ($(shell test -d /sw/lib && echo y),y)
+ BASIC_CFLAGS += -I/sw/include
+ BASIC_LDFLAGS += -L/sw/lib
+ endif
+ endif
+ ifndef NO_DARWIN_PORTS
+ ifeq ($(shell test -d /opt/local/lib && echo y),y)
+ BASIC_CFLAGS += -I/opt/local/include
+ BASIC_LDFLAGS += -L/opt/local/lib
+ endif
+ endif
+ PTHREAD_LIBS =
+endif
+
+ifneq ($(OUTPUT),)
+ BASIC_CFLAGS += -I$(OUTPUT)
+endif
+
+FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
+ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y)
+ FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS)
+ ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y)
+ msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
+ else
+ msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
+ endif
+endif
+
+ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
+ BASIC_CFLAGS += -DLIBELF_NO_MMAP
+endif
+
+ifndef NO_DWARF
+ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
+ msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
+else
+ BASIC_CFLAGS += -DDWARF_SUPPORT
+ EXTLIBS += -lelf -ldw
+ LIB_OBJS += $(OUTPUT)util/probe-finder.o
+endif # PERF_HAVE_DWARF_REGS
+endif # NO_DWARF
+
+ifdef NO_NEWT
+ BASIC_CFLAGS += -DNO_NEWT_SUPPORT
+else
+ FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
+ ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y)
+ msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
+ BASIC_CFLAGS += -DNO_NEWT_SUPPORT
+ else
+ # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
+ BASIC_CFLAGS += -I/usr/include/slang
+ EXTLIBS += -lnewt -lslang
+ LIB_OBJS += $(OUTPUT)util/ui/setup.o
+ LIB_OBJS += $(OUTPUT)util/ui/browser.o
+ LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
+ LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
+ LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
+ LIB_OBJS += $(OUTPUT)util/ui/helpline.o
+ LIB_OBJS += $(OUTPUT)util/ui/progress.o
+ LIB_OBJS += $(OUTPUT)util/ui/util.o
+ LIB_H += util/ui/browser.h
+ LIB_H += util/ui/browsers/map.h
+ LIB_H += util/ui/helpline.h
+ LIB_H += util/ui/libslang.h
+ LIB_H += util/ui/progress.h
+ LIB_H += util/ui/util.h
+ endif
+endif
+
+ifdef NO_LIBPERL
+ BASIC_CFLAGS += -DNO_LIBPERL
+else
+ PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
+ PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
+ PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
+ PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
+ FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
+
+ ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y)
+ BASIC_CFLAGS += -DNO_LIBPERL
+ else
+ ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS)
+ EXTLIBS += $(PERL_EMBED_LIBADD)
+ LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o
+ LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o
+ endif
+endif
+
+ifdef NO_LIBPYTHON
+ BASIC_CFLAGS += -DNO_LIBPYTHON
+else
+ PYTHON_EMBED_LDOPTS = $(shell python-config --ldflags 2>/dev/null)
+ PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
+ PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS))
+ PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null`
+ FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
+ ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y)
+ BASIC_CFLAGS += -DNO_LIBPYTHON
+ else
+ ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS)
+ EXTLIBS += $(PYTHON_EMBED_LIBADD)
+ LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o
+ LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o
+ endif
+endif
+
+ifdef NO_DEMANGLE
+ BASIC_CFLAGS += -DNO_DEMANGLE
+else
+ ifdef HAVE_CPLUS_DEMANGLE
+ EXTLIBS += -liberty
+ BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
+ else
+ FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd
+ has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
+ ifeq ($(has_bfd),y)
+ EXTLIBS += -lbfd
+ else
+ FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty
+ has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY))
+ ifeq ($(has_bfd_iberty),y)
+ EXTLIBS += -lbfd -liberty
+ else
+ FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz
+ has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z))
+ ifeq ($(has_bfd_iberty_z),y)
+ EXTLIBS += -lbfd -liberty -lz
+ else
+ FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty
+ has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE))
+ ifeq ($(has_cplus_demangle),y)
+ EXTLIBS += -liberty
+ BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
+ else
+ msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling)
+ BASIC_CFLAGS += -DNO_DEMANGLE
+ endif
+ endif
+ endif
+ endif
+ endif
+endif
+
+
+ifdef NO_STRLCPY
+ BASIC_CFLAGS += -DNO_STRLCPY
+else
+ ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y)
+ BASIC_CFLAGS += -DNO_STRLCPY
+ endif
+endif
+
+ifndef CC_LD_DYNPATH
+ ifdef NO_R_TO_GCC_LINKER
+ # Some gcc does not accept and pass -R to the linker to specify
+ # the runtime dynamic library path.
+ CC_LD_DYNPATH = -Wl,-rpath,
+ else
+ CC_LD_DYNPATH = -R
+ endif
+endif
+
+ifdef NEEDS_SOCKET
+ EXTLIBS += -lsocket
+endif
+ifdef NEEDS_NSL
+ EXTLIBS += -lnsl
+endif
+ifdef NO_D_TYPE_IN_DIRENT
+ BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
+endif
+ifdef NO_D_INO_IN_DIRENT
+ BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
+endif
+ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+ BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
+endif
+ifdef USE_NSEC
+ BASIC_CFLAGS += -DUSE_NSEC
+endif
+ifdef USE_ST_TIMESPEC
+ BASIC_CFLAGS += -DUSE_ST_TIMESPEC
+endif
+ifdef NO_NSEC
+ BASIC_CFLAGS += -DNO_NSEC
+endif
+ifdef NO_C99_FORMAT
+ BASIC_CFLAGS += -DNO_C99_FORMAT
+endif
+ifdef SNPRINTF_RETURNS_BOGUS
+ COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
+ COMPAT_OBJS += $(OUTPUT)compat/snprintf.o
+endif
+ifdef FREAD_READS_DIRECTORIES
+ COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
+ COMPAT_OBJS += $(OUTPUT)compat/fopen.o
+endif
+ifdef NO_SYMLINK_HEAD
+ BASIC_CFLAGS += -DNO_SYMLINK_HEAD
+endif
+ifdef NO_STRCASESTR
+ COMPAT_CFLAGS += -DNO_STRCASESTR
+ COMPAT_OBJS += $(OUTPUT)compat/strcasestr.o
+endif
+ifdef NO_STRTOUMAX
+ COMPAT_CFLAGS += -DNO_STRTOUMAX
+ COMPAT_OBJS += $(OUTPUT)compat/strtoumax.o
+endif
+ifdef NO_STRTOULL
+ COMPAT_CFLAGS += -DNO_STRTOULL
+endif
+ifdef NO_SETENV
+ COMPAT_CFLAGS += -DNO_SETENV
+ COMPAT_OBJS += $(OUTPUT)compat/setenv.o
+endif
+ifdef NO_MKDTEMP
+ COMPAT_CFLAGS += -DNO_MKDTEMP
+ COMPAT_OBJS += $(OUTPUT)compat/mkdtemp.o
+endif
+ifdef NO_UNSETENV
+ COMPAT_CFLAGS += -DNO_UNSETENV
+ COMPAT_OBJS += $(OUTPUT)compat/unsetenv.o
+endif
+ifdef NO_SYS_SELECT_H
+ BASIC_CFLAGS += -DNO_SYS_SELECT_H
+endif
+ifdef NO_MMAP
+ COMPAT_CFLAGS += -DNO_MMAP
+ COMPAT_OBJS += $(OUTPUT)compat/mmap.o
+else
+ ifdef USE_WIN32_MMAP
+ COMPAT_CFLAGS += -DUSE_WIN32_MMAP
+ COMPAT_OBJS += $(OUTPUT)compat/win32mmap.o
+ endif
+endif
+ifdef NO_PREAD
+ COMPAT_CFLAGS += -DNO_PREAD
+ COMPAT_OBJS += $(OUTPUT)compat/pread.o
+endif
+ifdef NO_FAST_WORKING_DIRECTORY
+ BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
+endif
+ifdef NO_TRUSTABLE_FILEMODE
+ BASIC_CFLAGS += -DNO_TRUSTABLE_FILEMODE
+endif
+ifdef NO_IPV6
+ BASIC_CFLAGS += -DNO_IPV6
+endif
+ifdef NO_UINTMAX_T
+ BASIC_CFLAGS += -Duintmax_t=uint32_t
+endif
+ifdef NO_SOCKADDR_STORAGE
+ifdef NO_IPV6
+ BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in
+else
+ BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6
+endif
+endif
+ifdef NO_INET_NTOP
+ LIB_OBJS += $(OUTPUT)compat/inet_ntop.o
+endif
+ifdef NO_INET_PTON
+ LIB_OBJS += $(OUTPUT)compat/inet_pton.o
+endif
+
+ifdef NO_ICONV
+ BASIC_CFLAGS += -DNO_ICONV
+endif
+
+ifdef OLD_ICONV
+ BASIC_CFLAGS += -DOLD_ICONV
+endif
+
+ifdef NO_DEFLATE_BOUND
+ BASIC_CFLAGS += -DNO_DEFLATE_BOUND
+endif
+
+ifdef PPC_SHA1
+ SHA1_HEADER = "ppc/sha1.h"
+ LIB_OBJS += $(OUTPUT)ppc/sha1.o ppc/sha1ppc.o
+else
+ifdef ARM_SHA1
+ SHA1_HEADER = "arm/sha1.h"
+ LIB_OBJS += $(OUTPUT)arm/sha1.o $(OUTPUT)arm/sha1_arm.o
+else
+ifdef MOZILLA_SHA1
+ SHA1_HEADER = "mozilla-sha1/sha1.h"
+ LIB_OBJS += $(OUTPUT)mozilla-sha1/sha1.o
+else
+ SHA1_HEADER = <openssl/sha.h>
+ EXTLIBS += $(LIB_4_CRYPTO)
+endif
+endif
+endif
+ifdef NO_PERL_MAKEMAKER
+ export NO_PERL_MAKEMAKER
+endif
+ifdef NO_HSTRERROR
+ COMPAT_CFLAGS += -DNO_HSTRERROR
+ COMPAT_OBJS += $(OUTPUT)compat/hstrerror.o
+endif
+ifdef NO_MEMMEM
+ COMPAT_CFLAGS += -DNO_MEMMEM
+ COMPAT_OBJS += $(OUTPUT)compat/memmem.o
+endif
+ifdef INTERNAL_QSORT
+ COMPAT_CFLAGS += -DINTERNAL_QSORT
+ COMPAT_OBJS += $(OUTPUT)compat/qsort.o
+endif
+ifdef RUNTIME_PREFIX
+ COMPAT_CFLAGS += -DRUNTIME_PREFIX
+endif
+
+ifdef DIR_HAS_BSD_GROUP_SEMANTICS
+ COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
+endif
+ifdef NO_EXTERNAL_GREP
+ BASIC_CFLAGS += -DNO_EXTERNAL_GREP
+endif
+
+ifeq ($(PERL_PATH),)
+NO_PERL=NoThanks
+endif
+
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1 =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+ QUIET_CC = @echo ' ' CC $@;
+ QUIET_AR = @echo ' ' AR $@;
+ QUIET_LINK = @echo ' ' LINK $@;
+ QUIET_MKDIR = @echo ' ' MKDIR $@;
+ QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
+ QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_SUBDIR0 = +@subdir=
+ QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
+ $(MAKE) $(PRINT_DIR) -C $$subdir
+ export V
+ export QUIET_GEN
+ export QUIET_BUILT_IN
+endif
+endif
+
+ifdef ASCIIDOC8
+ export ASCIIDOC8
+endif
+
+# Shell quote (do not use $(call) to accommodate ancient setups);
+
+SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
+ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG))
+
+DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
+bindir_SQ = $(subst ','\'',$(bindir))
+bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
+mandir_SQ = $(subst ','\'',$(mandir))
+infodir_SQ = $(subst ','\'',$(infodir))
+perfexecdir_SQ = $(subst ','\'',$(perfexecdir))
+template_dir_SQ = $(subst ','\'',$(template_dir))
+htmldir_SQ = $(subst ','\'',$(htmldir))
+prefix_SQ = $(subst ','\'',$(prefix))
+
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+
+LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive $(EXTLIBS)
+
+BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
+ $(COMPAT_CFLAGS)
+LIB_OBJS += $(COMPAT_OBJS)
+
+ALL_CFLAGS += $(BASIC_CFLAGS)
+ALL_CFLAGS += $(ARCH_CFLAGS)
+ALL_LDFLAGS += $(BASIC_LDFLAGS)
+
+export TAR INSTALL DESTDIR SHELL_PATH
+
+
+### Build rules
+
+SHELL = $(SHELL_PATH)
+
+all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS
+ifneq (,$X)
+ $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
+endif
+
+all::
+
+please_set_SHELL_PATH_to_a_more_modern_shell:
+ @$$(:)
+
+shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
+
+strip: $(PROGRAMS) $(OUTPUT)perf$X
+ $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf$X
+
+$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \
+ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
+ $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
+
+$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \
+ $(BUILTIN_OBJS) $(LIBS) -o $@
+
+$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
+ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
+ '-DPERF_MAN_PATH="$(mandir_SQ)"' \
+ '-DPERF_INFO_PATH="$(infodir_SQ)"' $<
+
+$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
+ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
+ '-DPERF_MAN_PATH="$(mandir_SQ)"' \
+ '-DPERF_INFO_PATH="$(infodir_SQ)"' $<
+
+$(BUILT_INS): $(OUTPUT)perf$X
+ $(QUIET_BUILT_IN)$(RM) $@ && \
+ ln perf$X $@ 2>/dev/null || \
+ ln -s perf$X $@ 2>/dev/null || \
+ cp perf$X $@
+
+$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt
+
+$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt)
+ $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@
+
+$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
+ $(QUIET_GEN)$(RM) $(OUTPUT)$@ $(OUTPUT)$@+ && \
+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+ -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+ -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
+ -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \
+ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+ $@.sh > $(OUTPUT)$@+ && \
+ chmod +x $(OUTPUT)$@+ && \
+ mv $(OUTPUT)$@+ $(OUTPUT)$@
+
+configure: configure.ac
+ $(QUIET_GEN)$(RM) $@ $<+ && \
+ sed -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \
+ $< > $<+ && \
+ autoconf -o $@ $<+ && \
+ $(RM) $<+
+
+# These can record PERF_VERSION
+$(OUTPUT)perf.o perf.spec \
+ $(patsubst %.sh,%,$(SCRIPT_SH)) \
+ $(patsubst %.perl,%,$(SCRIPT_PERL)) \
+ : $(OUTPUT)PERF-VERSION-FILE
+
+$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
+$(OUTPUT)%.o: %.S
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+
+$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
+ '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
+ '-DBINDIR="$(bindir_relative_SQ)"' \
+ '-DPREFIX="$(prefix_SQ)"' \
+ $<
+
+$(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
+
+$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
+
+$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
+
+$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
+
+$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
+
+$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
+
+$(OUTPUT)util/rbtree.o: util/rbtree.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
+
+$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
+
+$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
+
+$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
+
+$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
+
+$(OUTPUT)perf-%$X: %.o $(PERFLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+
+$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
+$(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
+builtin-revert.o wt-status.o: wt-status.h
+
+# we compile into subdirectories. if the target directory is not the source directory, they might not exists. So
+# we depend the various files onto their directories.
+DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h
+$(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS)))
+# In the second step, we make a rule to actually create these directories
+$(sort $(dir $(DIRECTORY_DEPS))):
+ $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null
+
+$(LIB_FILE): $(LIB_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
+
+doc:
+ $(MAKE) -C Documentation all
+
+man:
+ $(MAKE) -C Documentation man
+
+html:
+ $(MAKE) -C Documentation html
+
+info:
+ $(MAKE) -C Documentation info
+
+pdf:
+ $(MAKE) -C Documentation pdf
+
+TAGS:
+ $(RM) TAGS
+ $(FIND) . -name '*.[hcS]' -print | xargs etags -a
+
+tags:
+ $(RM) tags
+ $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
+
+cscope:
+ $(RM) cscope*
+ $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
+
+### Detect prefix changes
+TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
+ $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
+
+$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS
+ @FLAGS='$(TRACK_CFLAGS)'; \
+ if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \
+ echo 1>&2 " * new build flags or prefix"; \
+ echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \
+ fi
+
+# We need to apply sq twice, once to protect from the shell
+# that runs $(OUTPUT)PERF-BUILD-OPTIONS, and then again to protect it
+# and the first level quoting from the shell that runs "echo".
+$(OUTPUT)PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS
+ @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
+ @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
+ @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
+ @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
+
+### Testing rules
+
+#
+# None right now:
+#
+# TEST_PROGRAMS += test-something$X
+
+all:: $(TEST_PROGRAMS)
+
+# GNU make supports exporting all variables by "export" without parameters.
+# However, the environment gets quite big, and some programs have problems
+# with that.
+
+export NO_SVN_TESTS
+
+check: $(OUTPUT)common-cmds.h
+ if sparse; \
+ then \
+ for i in *.c */*.c; \
+ do \
+ sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \
+ done; \
+ else \
+ echo 2>&1 "Did you mean 'make test'?"; \
+ exit 1; \
+ fi
+
+remove-dashes:
+ ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
+
+### Installation rules
+
+ifneq ($(filter /%,$(firstword $(template_dir))),)
+template_instdir = $(template_dir)
+else
+template_instdir = $(prefix)/$(template_dir)
+endif
+export template_instdir
+
+ifneq ($(filter /%,$(firstword $(perfexecdir))),)
+perfexec_instdir = $(perfexecdir)
+else
+perfexec_instdir = $(prefix)/$(perfexecdir)
+endif
+perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir))
+export perfexec_instdir
+
+install: all
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
+ $(INSTALL) $(OUTPUT)perf$X '$(DESTDIR_SQ)$(bindir_SQ)'
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
+ $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+ $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
+ $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'
+ $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
+ $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'
+ $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'
+ $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
+
+ifdef BUILT_INS
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+ $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+ifneq (,$X)
+ $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) $(OUTPUT)perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';)
+endif
+endif
+
+install-doc:
+ $(MAKE) -C Documentation install
+
+install-man:
+ $(MAKE) -C Documentation install-man
+
+install-html:
+ $(MAKE) -C Documentation install-html
+
+install-info:
+ $(MAKE) -C Documentation install-info
+
+install-pdf:
+ $(MAKE) -C Documentation install-pdf
+
+quick-install-doc:
+ $(MAKE) -C Documentation quick-install
+
+quick-install-man:
+ $(MAKE) -C Documentation quick-install-man
+
+quick-install-html:
+ $(MAKE) -C Documentation quick-install-html
+
+
+### Maintainer's dist rules
+#
+# None right now
+#
+#
+# perf.spec: perf.spec.in
+# sed -e 's/@@VERSION@@/$(PERF_VERSION)/g' < $< > $@+
+# mv $@+ $@
+#
+# PERF_TARNAME=perf-$(PERF_VERSION)
+# dist: perf.spec perf-archive$(X) configure
+# ./perf-archive --format=tar \
+# --prefix=$(PERF_TARNAME)/ HEAD^{tree} > $(PERF_TARNAME).tar
+# @mkdir -p $(PERF_TARNAME)
+# @cp perf.spec configure $(PERF_TARNAME)
+# @echo $(PERF_VERSION) > $(PERF_TARNAME)/version
+# $(TAR) rf $(PERF_TARNAME).tar \
+# $(PERF_TARNAME)/perf.spec \
+# $(PERF_TARNAME)/configure \
+# $(PERF_TARNAME)/version
+# @$(RM) -r $(PERF_TARNAME)
+# gzip -f -9 $(PERF_TARNAME).tar
+#
+# htmldocs = perf-htmldocs-$(PERF_VERSION)
+# manpages = perf-manpages-$(PERF_VERSION)
+# dist-doc:
+# $(RM) -r .doc-tmp-dir
+# mkdir .doc-tmp-dir
+# $(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc
+# cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar .
+# gzip -n -9 -f $(htmldocs).tar
+# :
+# $(RM) -r .doc-tmp-dir
+# mkdir -p .doc-tmp-dir/man1 .doc-tmp-dir/man5 .doc-tmp-dir/man7
+# $(MAKE) -C Documentation DESTDIR=./ \
+# man1dir=../.doc-tmp-dir/man1 \
+# man5dir=../.doc-tmp-dir/man5 \
+# man7dir=../.doc-tmp-dir/man7 \
+# install
+# cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
+# gzip -n -9 -f $(manpages).tar
+# $(RM) -r .doc-tmp-dir
+#
+# rpm: dist
+# $(RPMBUILD) -ta $(PERF_TARNAME).tar.gz
+
+### Cleaning rules
+
+distclean: clean
+# $(RM) configure
+
+clean:
+ $(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE)
+ $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X
+ $(RM) $(TEST_PROGRAMS)
+ $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
+ $(RM) -r autom4te.cache
+ $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
+ $(RM) -r $(PERF_TARNAME) .doc-tmp-dir
+ $(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz
+ $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
+ $(MAKE) -C Documentation/ clean
+ $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-BUILD-OPTIONS
+
+.PHONY: all install clean strip
+.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
+.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS
+.PHONY: .FORCE-PERF-BUILD-OPTIONS
+
+### Make sure built-ins do not have dups and listed in perf.c
+#
+check-builtins::
+ ./check-builtins.sh
+
+### Test suite coverage testing
+#
+# None right now
+#
+# .PHONY: coverage coverage-clean coverage-build coverage-report
+#
+# coverage:
+# $(MAKE) coverage-build
+# $(MAKE) coverage-report
+#
+# coverage-clean:
+# rm -f *.gcda *.gcno
+#
+# COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
+# COVERAGE_LDFLAGS = $(CFLAGS) -O0 -lgcov
+#
+# coverage-build: coverage-clean
+# $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
+# $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
+# -j1 test
+#
+# coverage-report:
+# gcov -b *.c */*.c
+# grep '^function.*called 0 ' *.c.gcov */*.c.gcov \
+# | sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \
+# | tee coverage-untested-functions
diff --git a/smartt-perf/arch/arm/Makefile b/smartt-perf/arch/arm/Makefile
new file mode 100644
index 0000000..15130b5
--- /dev/null
+++ b/smartt-perf/arch/arm/Makefile
@@ -0,0 +1,4 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
+endif
diff --git a/smartt-perf/arch/arm/util/dwarf-regs.c b/smartt-perf/arch/arm/util/dwarf-regs.c
new file mode 100644
index 0000000..fff6450
--- /dev/null
+++ b/smartt-perf/arch/arm/util/dwarf-regs.c
@@ -0,0 +1,64 @@
+/*
+ * Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright (C) 2010 Will Deacon, ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <libio.h>
+#include <dwarf-regs.h>
+
+struct pt_regs_dwarfnum {
+ const char *name;
+ unsigned int dwarfnum;
+};
+
+#define STR(s) #s
+#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
+#define GPR_DWARFNUM_NAME(num) \
+ {.name = STR(%r##num), .dwarfnum = num}
+#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
+
+/*
+ * Reference:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf
+ */
+static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
+ GPR_DWARFNUM_NAME(0),
+ GPR_DWARFNUM_NAME(1),
+ GPR_DWARFNUM_NAME(2),
+ GPR_DWARFNUM_NAME(3),
+ GPR_DWARFNUM_NAME(4),
+ GPR_DWARFNUM_NAME(5),
+ GPR_DWARFNUM_NAME(6),
+ GPR_DWARFNUM_NAME(7),
+ GPR_DWARFNUM_NAME(8),
+ GPR_DWARFNUM_NAME(9),
+ GPR_DWARFNUM_NAME(10),
+ REG_DWARFNUM_NAME("%fp", 11),
+ REG_DWARFNUM_NAME("%ip", 12),
+ REG_DWARFNUM_NAME("%sp", 13),
+ REG_DWARFNUM_NAME("%lr", 14),
+ REG_DWARFNUM_NAME("%pc", 15),
+ REG_DWARFNUM_END,
+};
+
+/**
+ * get_arch_regstr() - lookup register name from it's DWARF register number
+ * @n: the DWARF register number
+ *
+ * get_arch_regstr() returns the name of the register in struct
+ * regdwarfnum_table from it's DWARF register number. If the register is not
+ * found in the table, this returns NULL;
+ */
+const char *get_arch_regstr(unsigned int n)
+{
+ const struct pt_regs_dwarfnum *roff;
+ for (roff = regdwarfnum_table; roff->name != NULL; roff++)
+ if (roff->dwarfnum == n)
+ return roff->name;
+ return NULL;
+}
diff --git a/smartt-perf/arch/powerpc/Makefile b/smartt-perf/arch/powerpc/Makefile
new file mode 100644
index 0000000..15130b5
--- /dev/null
+++ b/smartt-perf/arch/powerpc/Makefile
@@ -0,0 +1,4 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
+endif
diff --git a/smartt-perf/arch/powerpc/util/dwarf-regs.c b/smartt-perf/arch/powerpc/util/dwarf-regs.c
new file mode 100644
index 0000000..48ae0c5
--- /dev/null
+++ b/smartt-perf/arch/powerpc/util/dwarf-regs.c
@@ -0,0 +1,88 @@
+/*
+ * Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright (C) 2010 Ian Munsie, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <libio.h>
+#include <dwarf-regs.h>
+
+
+struct pt_regs_dwarfnum {
+ const char *name;
+ unsigned int dwarfnum;
+};
+
+#define STR(s) #s
+#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
+#define GPR_DWARFNUM_NAME(num) \
+ {.name = STR(%gpr##num), .dwarfnum = num}
+#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
+
+/*
+ * Reference:
+ * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
+ */
+static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
+ GPR_DWARFNUM_NAME(0),
+ GPR_DWARFNUM_NAME(1),
+ GPR_DWARFNUM_NAME(2),
+ GPR_DWARFNUM_NAME(3),
+ GPR_DWARFNUM_NAME(4),
+ GPR_DWARFNUM_NAME(5),
+ GPR_DWARFNUM_NAME(6),
+ GPR_DWARFNUM_NAME(7),
+ GPR_DWARFNUM_NAME(8),
+ GPR_DWARFNUM_NAME(9),
+ GPR_DWARFNUM_NAME(10),
+ GPR_DWARFNUM_NAME(11),
+ GPR_DWARFNUM_NAME(12),
+ GPR_DWARFNUM_NAME(13),
+ GPR_DWARFNUM_NAME(14),
+ GPR_DWARFNUM_NAME(15),
+ GPR_DWARFNUM_NAME(16),
+ GPR_DWARFNUM_NAME(17),
+ GPR_DWARFNUM_NAME(18),
+ GPR_DWARFNUM_NAME(19),
+ GPR_DWARFNUM_NAME(20),
+ GPR_DWARFNUM_NAME(21),
+ GPR_DWARFNUM_NAME(22),
+ GPR_DWARFNUM_NAME(23),
+ GPR_DWARFNUM_NAME(24),
+ GPR_DWARFNUM_NAME(25),
+ GPR_DWARFNUM_NAME(26),
+ GPR_DWARFNUM_NAME(27),
+ GPR_DWARFNUM_NAME(28),
+ GPR_DWARFNUM_NAME(29),
+ GPR_DWARFNUM_NAME(30),
+ GPR_DWARFNUM_NAME(31),
+ REG_DWARFNUM_NAME("%msr", 66),
+ REG_DWARFNUM_NAME("%ctr", 109),
+ REG_DWARFNUM_NAME("%link", 108),
+ REG_DWARFNUM_NAME("%xer", 101),
+ REG_DWARFNUM_NAME("%dar", 119),
+ REG_DWARFNUM_NAME("%dsisr", 118),
+ REG_DWARFNUM_END,
+};
+
+/**
+ * get_arch_regstr() - lookup register name from it's DWARF register number
+ * @n: the DWARF register number
+ *
+ * get_arch_regstr() returns the name of the register in struct
+ * regdwarfnum_table from it's DWARF register number. If the register is not
+ * found in the table, this returns NULL;
+ */
+const char *get_arch_regstr(unsigned int n)
+{
+ const struct pt_regs_dwarfnum *roff;
+ for (roff = regdwarfnum_table; roff->name != NULL; roff++)
+ if (roff->dwarfnum == n)
+ return roff->name;
+ return NULL;
+}
diff --git a/smartt-perf/arch/s390/Makefile b/smartt-perf/arch/s390/Makefile
new file mode 100644
index 0000000..15130b5
--- /dev/null
+++ b/smartt-perf/arch/s390/Makefile
@@ -0,0 +1,4 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
+endif
diff --git a/smartt-perf/arch/s390/util/dwarf-regs.c b/smartt-perf/arch/s390/util/dwarf-regs.c
new file mode 100644
index 0000000..e19653e
--- /dev/null
+++ b/smartt-perf/arch/s390/util/dwarf-regs.c
@@ -0,0 +1,22 @@
+/*
+ * Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright IBM Corp. 2010
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *
+ */
+
+#include <libio.h>
+#include <dwarf-regs.h>
+
+#define NUM_GPRS 16
+
+static const char *gpr_names[NUM_GPRS] = {
+ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
+};
+
+const char *get_arch_regstr(unsigned int n)
+{
+ return (n >= NUM_GPRS) ? NULL : gpr_names[n];
+}
diff --git a/smartt-perf/arch/sh/Makefile b/smartt-perf/arch/sh/Makefile
new file mode 100644
index 0000000..15130b5
--- /dev/null
+++ b/smartt-perf/arch/sh/Makefile
@@ -0,0 +1,4 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
+endif
diff --git a/smartt-perf/arch/sh/util/dwarf-regs.c b/smartt-perf/arch/sh/util/dwarf-regs.c
new file mode 100644
index 0000000..a11edb0
--- /dev/null
+++ b/smartt-perf/arch/sh/util/dwarf-regs.c
@@ -0,0 +1,55 @@
+/*
+ * Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright (C) 2010 Matt Fleming <matt@console-pimps.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <libio.h>
+#include <dwarf-regs.h>
+
+/*
+ * Generic dwarf analysis helpers
+ */
+
+#define SH_MAX_REGS 18
+const char *sh_regs_table[SH_MAX_REGS] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ "r8",
+ "r9",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "r14",
+ "r15",
+ "pc",
+ "pr",
+};
+
+/* Return architecture dependent register string (for kprobe-tracer) */
+const char *get_arch_regstr(unsigned int n)
+{
+ return (n <= SH_MAX_REGS) ? sh_regs_table[n] : NULL;
+}
diff --git a/smartt-perf/arch/sparc/Makefile b/smartt-perf/arch/sparc/Makefile
new file mode 100644
index 0000000..15130b5
--- /dev/null
+++ b/smartt-perf/arch/sparc/Makefile
@@ -0,0 +1,4 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
+endif
diff --git a/smartt-perf/arch/sparc/util/dwarf-regs.c b/smartt-perf/arch/sparc/util/dwarf-regs.c
new file mode 100644
index 0000000..0ab8848
--- /dev/null
+++ b/smartt-perf/arch/sparc/util/dwarf-regs.c
@@ -0,0 +1,43 @@
+/*
+ * Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright (C) 2010 David S. Miller <davem@davemloft.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <libio.h>
+#include <dwarf-regs.h>
+
+#define SPARC_MAX_REGS 96
+
+const char *sparc_regs_table[SPARC_MAX_REGS] = {
+ "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7",
+ "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7",
+ "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7",
+ "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7",
+ "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+ "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
+ "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
+ "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31",
+ "%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39",
+ "%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47",
+ "%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55",
+ "%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63",
+};
+
+/**
+ * get_arch_regstr() - lookup register name from it's DWARF register number
+ * @n: the DWARF register number
+ *
+ * get_arch_regstr() returns the name of the register in struct
+ * regdwarfnum_table from it's DWARF register number. If the register is not
+ * found in the table, this returns NULL;
+ */
+const char *get_arch_regstr(unsigned int n)
+{
+ return (n <= SPARC_MAX_REGS) ? sparc_regs_table[n] : NULL;
+}
diff --git a/smartt-perf/arch/x86/Makefile b/smartt-perf/arch/x86/Makefile
new file mode 100644
index 0000000..15130b5
--- /dev/null
+++ b/smartt-perf/arch/x86/Makefile
@@ -0,0 +1,4 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
+endif
diff --git a/smartt-perf/arch/x86/util/dwarf-regs.c b/smartt-perf/arch/x86/util/dwarf-regs.c
new file mode 100644
index 0000000..a794d30
--- /dev/null
+++ b/smartt-perf/arch/x86/util/dwarf-regs.c
@@ -0,0 +1,75 @@
+/*
+ * dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
+ * Extracted from probe-finder.c
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <libio.h>
+#include <dwarf-regs.h>
+
+/*
+ * Generic dwarf analysis helpers
+ */
+
+#define X86_32_MAX_REGS 8
+const char *x86_32_regs_table[X86_32_MAX_REGS] = {
+ "%ax",
+ "%cx",
+ "%dx",
+ "%bx",
+ "$stack", /* Stack address instead of %sp */
+ "%bp",
+ "%si",
+ "%di",
+};
+
+#define X86_64_MAX_REGS 16
+const char *x86_64_regs_table[X86_64_MAX_REGS] = {
+ "%ax",
+ "%dx",
+ "%cx",
+ "%bx",
+ "%si",
+ "%di",
+ "%bp",
+ "%sp",
+ "%r8",
+ "%r9",
+ "%r10",
+ "%r11",
+ "%r12",
+ "%r13",
+ "%r14",
+ "%r15",
+};
+
+/* TODO: switching by dwarf address size */
+#ifdef __x86_64__
+#define ARCH_MAX_REGS X86_64_MAX_REGS
+#define arch_regs_table x86_64_regs_table
+#else
+#define ARCH_MAX_REGS X86_32_MAX_REGS
+#define arch_regs_table x86_32_regs_table
+#endif
+
+/* Return architecture dependent register string (for kprobe-tracer) */
+const char *get_arch_regstr(unsigned int n)
+{
+ return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
+}
diff --git a/smartt-perf/bench/bench.h b/smartt-perf/bench/bench.h
new file mode 100644
index 0000000..f7781c6
--- /dev/null
+++ b/smartt-perf/bench/bench.h
@@ -0,0 +1,17 @@
+#ifndef BENCH_H
+#define BENCH_H
+
+extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
+extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
+extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used);
+
+#define BENCH_FORMAT_DEFAULT_STR "default"
+#define BENCH_FORMAT_DEFAULT 0
+#define BENCH_FORMAT_SIMPLE_STR "simple"
+#define BENCH_FORMAT_SIMPLE 1
+
+#define BENCH_FORMAT_UNKNOWN -1
+
+extern int bench_format;
+
+#endif
diff --git a/smartt-perf/bench/mem-memcpy-arch.h b/smartt-perf/bench/mem-memcpy-arch.h
new file mode 100644
index 0000000..a72e36c
--- /dev/null
+++ b/smartt-perf/bench/mem-memcpy-arch.h
@@ -0,0 +1,12 @@
+
+#ifdef ARCH_X86_64
+
+#define MEMCPY_FN(fn, name, desc) \
+ extern void *fn(void *, const void *, size_t);
+
+#include "mem-memcpy-x86-64-asm-def.h"
+
+#undef MEMCPY_FN
+
+#endif
+
diff --git a/smartt-perf/bench/mem-memcpy-x86-64-asm-def.h b/smartt-perf/bench/mem-memcpy-x86-64-asm-def.h
new file mode 100644
index 0000000..d588b87
--- /dev/null
+++ b/smartt-perf/bench/mem-memcpy-x86-64-asm-def.h
@@ -0,0 +1,4 @@
+
+MEMCPY_FN(__memcpy,
+ "x86-64-unrolled",
+ "unrolled memcpy() in arch/x86/lib/memcpy_64.S")
diff --git a/smartt-perf/bench/mem-memcpy-x86-64-asm.S b/smartt-perf/bench/mem-memcpy-x86-64-asm.S
new file mode 100644
index 0000000..298438c
--- /dev/null
+++ b/smartt-perf/bench/mem-memcpy-x86-64-asm.S
@@ -0,0 +1,2 @@
+
+#include "../util/include/arch/x86/lib/memcpy_64.S"
diff --git a/smartt-perf/bench/mem-memcpy.c b/smartt-perf/bench/mem-memcpy.c
new file mode 100644
index 0000000..db82021
--- /dev/null
+++ b/smartt-perf/bench/mem-memcpy.c
@@ -0,0 +1,297 @@
+/*
+ * mem-memcpy.c
+ *
+ * memcpy: Simple memory copy in various ways
+ *
+ * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ */
+#include <ctype.h>
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/parse-options.h"
+#include "../util/header.h"
+#include "bench.h"
+#include "mem-memcpy-arch.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#define K 1024
+
+static const char *length_str = "1MB";
+static const char *routine = "default";
+static bool use_clock;
+static int clock_fd;
+static bool only_prefault;
+static bool no_prefault;
+
+static const struct option options[] = {
+ OPT_STRING('l', "length", &length_str, "1MB",
+ "Specify length of memory to copy. "
+ "available unit: B, MB, GB (upper and lower)"),
+ OPT_STRING('r', "routine", &routine, "default",
+ "Specify routine to copy"),
+ OPT_BOOLEAN('c', "clock", &use_clock,
+ "Use CPU clock for measuring"),
+ OPT_BOOLEAN('o', "only-prefault", &only_prefault,
+ "Show only the result with page faults before memcpy()"),
+ OPT_BOOLEAN('n', "no-prefault", &no_prefault,
+ "Show only the result without page faults before memcpy()"),
+ OPT_END()
+};
+
+typedef void *(*memcpy_t)(void *, const void *, size_t);
+
+struct routine {
+ const char *name;
+ const char *desc;
+ memcpy_t fn;
+};
+
+struct routine routines[] = {
+ { "default",
+ "Default memcpy() provided by glibc",
+ memcpy },
+#ifdef ARCH_X86_64
+
+#define MEMCPY_FN(fn, name, desc) { name, desc, fn },
+#include "mem-memcpy-x86-64-asm-def.h"
+#undef MEMCPY_FN
+
+#endif
+
+ { NULL,
+ NULL,
+ NULL }
+};
+
+static const char * const bench_mem_memcpy_usage[] = {
+ "perf bench mem memcpy <options>",
+ NULL
+};
+
+static struct perf_event_attr clock_attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CPU_CYCLES
+};
+
+static void init_clock(void)
+{
+ clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0);
+
+ if (clock_fd < 0 && errno == ENOSYS)
+ die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+ else
+ BUG_ON(clock_fd < 0);
+}
+
+static u64 get_clock(void)
+{
+ int ret;
+ u64 clk;
+
+ ret = read(clock_fd, &clk, sizeof(u64));
+ BUG_ON(ret != sizeof(u64));
+
+ return clk;
+}
+
+static double timeval2double(struct timeval *ts)
+{
+ return (double)ts->tv_sec +
+ (double)ts->tv_usec / (double)1000000;
+}
+
+static void alloc_mem(void **dst, void **src, size_t length)
+{
+ *dst = zalloc(length);
+ if (!dst)
+ die("memory allocation failed - maybe length is too large?\n");
+
+ *src = zalloc(length);
+ if (!src)
+ die("memory allocation failed - maybe length is too large?\n");
+}
+
+static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
+{
+ u64 clock_start = 0ULL, clock_end = 0ULL;
+ void *src = NULL, *dst = NULL;
+
+ alloc_mem(&src, &dst, len);
+
+ if (prefault)
+ fn(dst, src, len);
+
+ clock_start = get_clock();
+ fn(dst, src, len);
+ clock_end = get_clock();
+
+ free(src);
+ free(dst);
+ return clock_end - clock_start;
+}
+
+static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
+{
+ struct timeval tv_start, tv_end, tv_diff;
+ void *src = NULL, *dst = NULL;
+
+ alloc_mem(&src, &dst, len);
+
+ if (prefault)
+ fn(dst, src, len);
+
+ BUG_ON(gettimeofday(&tv_start, NULL));
+ fn(dst, src, len);
+ BUG_ON(gettimeofday(&tv_end, NULL));
+
+ timersub(&tv_end, &tv_start, &tv_diff);
+
+ free(src);
+ free(dst);
+ return (double)((double)len / timeval2double(&tv_diff));
+}
+
+#define pf (no_prefault ? 0 : 1)
+
+#define print_bps(x) do { \
+ if (x < K) \
+ printf(" %14lf B/Sec", x); \
+ else if (x < K * K) \
+ printf(" %14lfd KB/Sec", x / K); \
+ else if (x < K * K * K) \
+ printf(" %14lf MB/Sec", x / K / K); \
+ else \
+ printf(" %14lf GB/Sec", x / K / K / K); \
+ } while (0)
+
+int bench_mem_memcpy(int argc, const char **argv,
+ const char *prefix __used)
+{
+ int i;
+ size_t len;
+ double result_bps[2];
+ u64 result_clock[2];
+
+ argc = parse_options(argc, argv, options,
+ bench_mem_memcpy_usage, 0);
+
+ if (use_clock)
+ init_clock();
+
+ len = (size_t)perf_atoll((char *)length_str);
+
+ result_clock[0] = result_clock[1] = 0ULL;
+ result_bps[0] = result_bps[1] = 0.0;
+
+ if ((s64)len <= 0) {
+ fprintf(stderr, "Invalid length:%s\n", length_str);
+ return 1;
+ }
+
+ /* same to without specifying either of prefault and no-prefault */
+ if (only_prefault && no_prefault)
+ only_prefault = no_prefault = false;
+
+ for (i = 0; routines[i].name; i++) {
+ if (!strcmp(routines[i].name, routine))
+ break;
+ }
+ if (!routines[i].name) {
+ printf("Unknown routine:%s\n", routine);
+ printf("Available routines...\n");
+ for (i = 0; routines[i].name; i++) {
+ printf("\t%s ... %s\n",
+ routines[i].name, routines[i].desc);
+ }
+ return 1;
+ }
+
+ if (bench_format == BENCH_FORMAT_DEFAULT)
+ printf("# Copying %s Bytes ...\n\n", length_str);
+
+ if (!only_prefault && !no_prefault) {
+ /* show both of results */
+ if (use_clock) {
+ result_clock[0] =
+ do_memcpy_clock(routines[i].fn, len, false);
+ result_clock[1] =
+ do_memcpy_clock(routines[i].fn, len, true);
+ } else {
+ result_bps[0] =
+ do_memcpy_gettimeofday(routines[i].fn,
+ len, false);
+ result_bps[1] =
+ do_memcpy_gettimeofday(routines[i].fn,
+ len, true);
+ }
+ } else {
+ if (use_clock) {
+ result_clock[pf] =
+ do_memcpy_clock(routines[i].fn,
+ len, only_prefault);
+ } else {
+ result_bps[pf] =
+ do_memcpy_gettimeofday(routines[i].fn,
+ len, only_prefault);
+ }
+ }
+
+ switch (bench_format) {
+ case BENCH_FORMAT_DEFAULT:
+ if (!only_prefault && !no_prefault) {
+ if (use_clock) {
+ printf(" %14lf Clock/Byte\n",
+ (double)result_clock[0]
+ / (double)len);
+ printf(" %14lf Clock/Byte (with prefault)\n",
+ (double)result_clock[1]
+ / (double)len);
+ } else {
+ print_bps(result_bps[0]);
+ printf("\n");
+ print_bps(result_bps[1]);
+ printf(" (with prefault)\n");
+ }
+ } else {
+ if (use_clock) {
+ printf(" %14lf Clock/Byte",
+ (double)result_clock[pf]
+ / (double)len);
+ } else
+ print_bps(result_bps[pf]);
+
+ printf("%s\n", only_prefault ? " (with prefault)" : "");
+ }
+ break;
+ case BENCH_FORMAT_SIMPLE:
+ if (!only_prefault && !no_prefault) {
+ if (use_clock) {
+ printf("%lf %lf\n",
+ (double)result_clock[0] / (double)len,
+ (double)result_clock[1] / (double)len);
+ } else {
+ printf("%lf %lf\n",
+ result_bps[0], result_bps[1]);
+ }
+ } else {
+ if (use_clock) {
+ printf("%lf\n", (double)result_clock[pf]
+ / (double)len);
+ } else
+ printf("%lf\n", result_bps[pf]);
+ }
+ break;
+ default:
+ /* reaching this means there's some disaster: */
+ die("unknown format: %d\n", bench_format);
+ break;
+ }
+
+ return 0;
+}
diff --git a/smartt-perf/bench/sched-messaging.c b/smartt-perf/bench/sched-messaging.c
new file mode 100644
index 0000000..d1d1b30
--- /dev/null
+++ b/smartt-perf/bench/sched-messaging.c
@@ -0,0 +1,336 @@
+/*
+ *
+ * sched-messaging.c
+ *
+ * messaging: Benchmark for scheduler and IPC mechanisms
+ *
+ * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au>
+ * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ *
+ */
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/parse-options.h"
+#include "../builtin.h"
+#include "bench.h"
+
+/* Test groups of 20 processes spraying to 20 receivers */
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <limits.h>
+
+#define DATASIZE 100
+
+static bool use_pipes = false;
+static unsigned int loops = 100;
+static bool thread_mode = false;
+static unsigned int num_groups = 10;
+
+struct sender_context {
+ unsigned int num_fds;
+ int ready_out;
+ int wakefd;
+ int out_fds[0];
+};
+
+struct receiver_context {
+ unsigned int num_packets;
+ int in_fds[2];
+ int ready_out;
+ int wakefd;
+};
+
+static void barf(const char *msg)
+{
+ fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
+ exit(1);
+}
+
+static void fdpair(int fds[2])
+{
+ if (use_pipes) {
+ if (pipe(fds) == 0)
+ return;
+ } else {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
+ return;
+ }
+
+ barf(use_pipes ? "pipe()" : "socketpair()");
+}
+
+/* Block until we're ready to go */
+static void ready(int ready_out, int wakefd)
+{
+ char dummy;
+ struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
+
+ /* Tell them we're ready. */
+ if (write(ready_out, &dummy, 1) != 1)
+ barf("CLIENT: ready write");
+
+ /* Wait for "GO" signal */
+ if (poll(&pollfd, 1, -1) != 1)
+ barf("poll");
+}
+
+/* Sender sprays loops messages down each file descriptor */
+static void *sender(struct sender_context *ctx)
+{
+ char data[DATASIZE];
+ unsigned int i, j;
+
+ ready(ctx->ready_out, ctx->wakefd);
+
+ /* Now pump to every receiver. */
+ for (i = 0; i < loops; i++) {
+ for (j = 0; j < ctx->num_fds; j++) {
+ int ret, done = 0;
+
+again:
+ ret = write(ctx->out_fds[j], data + done,
+ sizeof(data)-done);
+ if (ret < 0)
+ barf("SENDER: write");
+ done += ret;
+ if (done < DATASIZE)
+ goto again;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* One receiver per fd */
+static void *receiver(struct receiver_context* ctx)
+{
+ unsigned int i;
+
+ if (!thread_mode)
+ close(ctx->in_fds[1]);
+
+ /* Wait for start... */
+ ready(ctx->ready_out, ctx->wakefd);
+
+ /* Receive them all */
+ for (i = 0; i < ctx->num_packets; i++) {
+ char data[DATASIZE];
+ int ret, done = 0;
+
+again:
+ ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
+ if (ret < 0)
+ barf("SERVER: read");
+ done += ret;
+ if (done < DATASIZE)
+ goto again;
+ }
+
+ return NULL;
+}
+
+static pthread_t create_worker(void *ctx, void *(*func)(void *))
+{
+ pthread_attr_t attr;
+ pthread_t childid;
+ int err;
+
+ if (!thread_mode) {
+ /* process mode */
+ /* Fork the receiver. */
+ switch (fork()) {
+ case -1:
+ barf("fork()");
+ break;
+ case 0:
+ (*func) (ctx);
+ exit(0);
+ break;
+ default:
+ break;
+ }
+
+ return (pthread_t)0;
+ }
+
+ if (pthread_attr_init(&attr) != 0)
+ barf("pthread_attr_init:");
+
+#ifndef __ia64__
+ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
+ barf("pthread_attr_setstacksize");
+#endif
+
+ err = pthread_create(&childid, &attr, func, ctx);
+ if (err != 0) {
+ fprintf(stderr, "pthread_create failed: %s (%d)\n",
+ strerror(err), err);
+ exit(-1);
+ }
+ return childid;
+}
+
+static void reap_worker(pthread_t id)
+{
+ int proc_status;
+ void *thread_status;
+
+ if (!thread_mode) {
+ /* process mode */
+ wait(&proc_status);
+ if (!WIFEXITED(proc_status))
+ exit(1);
+ } else {
+ pthread_join(id, &thread_status);
+ }
+}
+
+/* One group of senders and receivers */
+static unsigned int group(pthread_t *pth,
+ unsigned int num_fds,
+ int ready_out,
+ int wakefd)
+{
+ unsigned int i;
+ struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
+ + num_fds * sizeof(int));
+
+ if (!snd_ctx)
+ barf("malloc()");
+
+ for (i = 0; i < num_fds; i++) {
+ int fds[2];
+ struct receiver_context *ctx = malloc(sizeof(*ctx));
+
+ if (!ctx)
+ barf("malloc()");
+
+
+ /* Create the pipe between client and server */
+ fdpair(fds);
+
+ ctx->num_packets = num_fds * loops;
+ ctx->in_fds[0] = fds[0];
+ ctx->in_fds[1] = fds[1];
+ ctx->ready_out = ready_out;
+ ctx->wakefd = wakefd;
+
+ pth[i] = create_worker(ctx, (void *)receiver);
+
+ snd_ctx->out_fds[i] = fds[1];
+ if (!thread_mode)
+ close(fds[0]);
+ }
+
+ /* Now we have all the fds, fork the senders */
+ for (i = 0; i < num_fds; i++) {
+ snd_ctx->ready_out = ready_out;
+ snd_ctx->wakefd = wakefd;
+ snd_ctx->num_fds = num_fds;
+
+ pth[num_fds+i] = create_worker(snd_ctx, (void *)sender);
+ }
+
+ /* Close the fds we have left */
+ if (!thread_mode)
+ for (i = 0; i < num_fds; i++)
+ close(snd_ctx->out_fds[i]);
+
+ /* Return number of children to reap */
+ return num_fds * 2;
+}
+
+static const struct option options[] = {
+ OPT_BOOLEAN('p', "pipe", &use_pipes,
+ "Use pipe() instead of socketpair()"),
+ OPT_BOOLEAN('t', "thread", &thread_mode,
+ "Be multi thread instead of multi process"),
+ OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
+ OPT_UINTEGER('l', "loop", &loops, "Specify number of loops"),
+ OPT_END()
+};
+
+static const char * const bench_sched_message_usage[] = {
+ "perf bench sched messaging <options>",
+ NULL
+};
+
+int bench_sched_messaging(int argc, const char **argv,
+ const char *prefix __used)
+{
+ unsigned int i, total_children;
+ struct timeval start, stop, diff;
+ unsigned int num_fds = 20;
+ int readyfds[2], wakefds[2];
+ char dummy;
+ pthread_t *pth_tab;
+
+ argc = parse_options(argc, argv, options,
+ bench_sched_message_usage, 0);
+
+ pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
+ if (!pth_tab)
+ barf("main:malloc()");
+
+ fdpair(readyfds);
+ fdpair(wakefds);
+
+ total_children = 0;
+ for (i = 0; i < num_groups; i++)
+ total_children += group(pth_tab+total_children, num_fds,
+ readyfds[1], wakefds[0]);
+
+ /* Wait for everyone to be ready */
+ for (i = 0; i < total_children; i++)
+ if (read(readyfds[0], &dummy, 1) != 1)
+ barf("Reading for readyfds");
+
+ gettimeofday(&start, NULL);
+
+ /* Kick them off */
+ if (write(wakefds[1], &dummy, 1) != 1)
+ barf("Writing to start them");
+
+ /* Reap them all */
+ for (i = 0; i < total_children; i++)
+ reap_worker(pth_tab[i]);
+
+ gettimeofday(&stop, NULL);
+
+ timersub(&stop, &start, &diff);
+
+ switch (bench_format) {
+ case BENCH_FORMAT_DEFAULT:
+ printf("# %d sender and receiver %s per group\n",
+ num_fds, thread_mode ? "threads" : "processes");
+ printf("# %d groups == %d %s run\n\n",
+ num_groups, num_groups * 2 * num_fds,
+ thread_mode ? "threads" : "processes");
+ printf(" %14s: %lu.%03lu [sec]\n", "Total time",
+ diff.tv_sec,
+ (unsigned long) (diff.tv_usec/1000));
+ break;
+ case BENCH_FORMAT_SIMPLE:
+ printf("%lu.%03lu\n", diff.tv_sec,
+ (unsigned long) (diff.tv_usec/1000));
+ break;
+ default:
+ /* reaching here is something disaster */
+ fprintf(stderr, "Unknown format:%d\n", bench_format);
+ exit(1);
+ break;
+ }
+
+ return 0;
+}
diff --git a/smartt-perf/bench/sched-pipe.c b/smartt-perf/bench/sched-pipe.c
new file mode 100644
index 0000000..d9ab3ce
--- /dev/null
+++ b/smartt-perf/bench/sched-pipe.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * sched-pipe.c
+ *
+ * pipe: Benchmark for pipe()
+ *
+ * Based on pipe-test-1m.c by Ingo Molnar <mingo@redhat.com>
+ * http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c
+ * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ *
+ */
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/parse-options.h"
+#include "../builtin.h"
+#include "bench.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <linux/unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#define LOOPS_DEFAULT 1000000
+static int loops = LOOPS_DEFAULT;
+
+static const struct option options[] = {
+ OPT_INTEGER('l', "loop", &loops,
+ "Specify number of loops"),
+ OPT_END()
+};
+
+static const char * const bench_sched_pipe_usage[] = {
+ "perf bench sched pipe <options>",
+ NULL
+};
+
+int bench_sched_pipe(int argc, const char **argv,
+ const char *prefix __used)
+{
+ int pipe_1[2], pipe_2[2];
+ int m = 0, i;
+ struct timeval start, stop, diff;
+ unsigned long long result_usec = 0;
+
+ /*
+ * why does "ret" exist?
+ * discarding returned value of read(), write()
+ * causes error in building environment for perf
+ */
+ int ret, wait_stat;
+ pid_t pid, retpid;
+
+ argc = parse_options(argc, argv, options,
+ bench_sched_pipe_usage, 0);
+
+ assert(!pipe(pipe_1));
+ assert(!pipe(pipe_2));
+
+ pid = fork();
+ assert(pid >= 0);
+
+ gettimeofday(&start, NULL);
+
+ if (!pid) {
+ for (i = 0; i < loops; i++) {
+ ret = read(pipe_1[0], &m, sizeof(int));
+ ret = write(pipe_2[1], &m, sizeof(int));
+ }
+ } else {
+ for (i = 0; i < loops; i++) {
+ ret = write(pipe_1[1], &m, sizeof(int));
+ ret = read(pipe_2[0], &m, sizeof(int));
+ }
+ }
+
+ gettimeofday(&stop, NULL);
+ timersub(&stop, &start, &diff);
+
+ if (pid) {
+ retpid = waitpid(pid, &wait_stat, 0);
+ assert((retpid == pid) && WIFEXITED(wait_stat));
+ } else {
+ exit(0);
+ }
+
+ switch (bench_format) {
+ case BENCH_FORMAT_DEFAULT:
+ printf("# Executed %d pipe operations between two tasks\n\n",
+ loops);
+
+ result_usec = diff.tv_sec * 1000000;
+ result_usec += diff.tv_usec;
+
+ printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
+ diff.tv_sec,
+ (unsigned long) (diff.tv_usec/1000));
+
+ printf(" %14lf usecs/op\n",
+ (double)result_usec / (double)loops);
+ printf(" %14d ops/sec\n",
+ (int)((double)loops /
+ ((double)result_usec / (double)1000000)));
+ break;
+
+ case BENCH_FORMAT_SIMPLE:
+ printf("%lu.%03lu\n",
+ diff.tv_sec,
+ (unsigned long) (diff.tv_usec / 1000));
+ break;
+
+ default:
+ /* reaching here is something disaster */
+ fprintf(stderr, "Unknown format:%d\n", bench_format);
+ exit(1);
+ break;
+ }
+
+ return 0;
+}
diff --git a/smartt-perf/builtin-annotate.c b/smartt-perf/builtin-annotate.c
new file mode 100644
index 0000000..8879463
--- /dev/null
+++ b/smartt-perf/builtin-annotate.c
@@ -0,0 +1,481 @@
+/*
+ * builtin-annotate.c
+ *
+ * Builtin annotate command: Analyze the perf.data input file,
+ * look up and read DSOs and symbol information and display
+ * a histogram of results, along various sorting keys.
+ */
+#include "builtin.h"
+
+#include "util/util.h"
+
+#include "util/color.h"
+#include <linux/list.h>
+#include "util/cache.h"
+#include <linux/rbtree.h>
+#include "util/symbol.h"
+
+#include "perf.h"
+#include "util/debug.h"
+
+#include "util/event.h"
+#include "util/parse-options.h"
+#include "util/parse-events.h"
+#include "util/thread.h"
+#include "util/sort.h"
+#include "util/hist.h"
+#include "util/session.h"
+
+static char const *input_name = "perf.data";
+
+static bool force, use_tui, use_stdio;
+
+static bool full_paths;
+
+static bool print_line;
+
+static const char *sym_hist_filter;
+
+static int hists__add_entry(struct hists *self, struct addr_location *al)
+{
+ struct hist_entry *he;
+
+ if (sym_hist_filter != NULL &&
+ (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
+ /* We're only interested in a symbol named sym_hist_filter */
+ if (al->sym != NULL) {
+ rb_erase(&al->sym->rb_node,
+ &al->map->dso->symbols[al->map->type]);
+ symbol__delete(al->sym);
+ }
+ return 0;
+ }
+
+ he = __hists__add_entry(self, al, NULL, 1);
+ if (he == NULL)
+ return -ENOMEM;
+
+ return hist_entry__inc_addr_samples(he, al->addr);
+}
+
+static int process_sample_event(event_t *event, struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct addr_location al;
+
+ if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
+ pr_warning("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ if (!al.filtered && hists__add_entry(&session->hists, &al)) {
+ pr_warning("problem incrementing symbol count, "
+ "skipping event\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int objdump_line__print(struct objdump_line *self,
+ struct list_head *head,
+ struct hist_entry *he, u64 len)
+{
+ struct symbol *sym = he->ms.sym;
+ static const char *prev_line;
+ static const char *prev_color;
+
+ if (self->offset != -1) {
+ const char *path = NULL;
+ unsigned int hits = 0;
+ double percent = 0.0;
+ const char *color;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_ext *sym_ext = priv->ext;
+ struct sym_hist *h = priv->hist;
+ s64 offset = self->offset;
+ struct objdump_line *next = objdump__get_next_ip_line(head, self);
+
+ while (offset < (s64)len &&
+ (next == NULL || offset < next->offset)) {
+ if (sym_ext) {
+ if (path == NULL)
+ path = sym_ext[offset].path;
+ percent += sym_ext[offset].percent;
+ } else
+ hits += h->ip[offset];
+
+ ++offset;
+ }
+
+ if (sym_ext == NULL && h->sum)
+ percent = 100.0 * hits / h->sum;
+
+ color = get_percent_color(percent);
+
+ /*
+ * Also color the filename and line if needed, with
+ * the same color than the percentage. Don't print it
+ * twice for close colored ip with the same filename:line
+ */
+ if (path) {
+ if (!prev_line || strcmp(prev_line, path)
+ || color != prev_color) {
+ color_fprintf(stdout, color, " %s", path);
+ prev_line = path;
+ prev_color = color;
+ }
+ }
+
+ color_fprintf(stdout, color, " %7.2f", percent);
+ printf(" : ");
+ color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
+ } else {
+ if (!*self->line)
+ printf(" :\n");
+ else
+ printf(" : %s\n", self->line);
+ }
+
+ return 0;
+}
+
+static struct rb_root root_sym_ext;
+
+static void insert_source_line(struct sym_ext *sym_ext)
+{
+ struct sym_ext *iter;
+ struct rb_node **p = &root_sym_ext.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct sym_ext, node);
+
+ if (sym_ext->percent > iter->percent)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&sym_ext->node, parent, p);
+ rb_insert_color(&sym_ext->node, &root_sym_ext);
+}
+
+static void free_source_line(struct hist_entry *he, int len)
+{
+ struct sym_priv *priv = symbol__priv(he->ms.sym);
+ struct sym_ext *sym_ext = priv->ext;
+ int i;
+
+ if (!sym_ext)
+ return;
+
+ for (i = 0; i < len; i++)
+ free(sym_ext[i].path);
+ free(sym_ext);
+
+ priv->ext = NULL;
+ root_sym_ext = RB_ROOT;
+}
+
+/* Get the filename:line for the colored entries */
+static void
+get_source_line(struct hist_entry *he, int len, const char *filename)
+{
+ struct symbol *sym = he->ms.sym;
+ u64 start;
+ int i;
+ char cmd[PATH_MAX * 2];
+ struct sym_ext *sym_ext;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_hist *h = priv->hist;
+
+ if (!h->sum)
+ return;
+
+ sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
+ if (!priv->ext)
+ return;
+
+ start = he->ms.map->unmap_ip(he->ms.map, sym->start);
+
+ for (i = 0; i < len; i++) {
+ char *path = NULL;
+ size_t line_len;
+ u64 offset;
+ FILE *fp;
+
+ sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
+ if (sym_ext[i].percent <= 0.5)
+ continue;
+
+ offset = start + i;
+ sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
+ fp = popen(cmd, "r");
+ if (!fp)
+ continue;
+
+ if (getline(&path, &line_len, fp) < 0 || !line_len)
+ goto next;
+
+ sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
+ if (!sym_ext[i].path)
+ goto next;
+
+ strcpy(sym_ext[i].path, path);
+ insert_source_line(&sym_ext[i]);
+
+ next:
+ pclose(fp);
+ }
+}
+
+static void print_summary(const char *filename)
+{
+ struct sym_ext *sym_ext;
+ struct rb_node *node;
+
+ printf("\nSorted summary for file %s\n", filename);
+ printf("----------------------------------------------\n\n");
+
+ if (RB_EMPTY_ROOT(&root_sym_ext)) {
+ printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
+ return;
+ }
+
+ node = rb_first(&root_sym_ext);
+ while (node) {
+ double percent;
+ const char *color;
+ char *path;
+
+ sym_ext = rb_entry(node, struct sym_ext, node);
+ percent = sym_ext->percent;
+ color = get_percent_color(percent);
+ path = sym_ext->path;
+
+ color_fprintf(stdout, color, " %7.2f %s", percent, path);
+ node = rb_next(node);
+ }
+}
+
+static void hist_entry__print_hits(struct hist_entry *self)
+{
+ struct symbol *sym = self->ms.sym;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_hist *h = priv->hist;
+ u64 len = sym->end - sym->start, offset;
+
+ for (offset = 0; offset < len; ++offset)
+ if (h->ip[offset] != 0)
+ printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
+ sym->start + offset, h->ip[offset]);
+ printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
+}
+
+static int hist_entry__tty_annotate(struct hist_entry *he)
+{
+ struct map *map = he->ms.map;
+ struct dso *dso = map->dso;
+ struct symbol *sym = he->ms.sym;
+ const char *filename = dso->long_name, *d_filename;
+ u64 len;
+ LIST_HEAD(head);
+ struct objdump_line *pos, *n;
+
+ if (hist_entry__annotate(he, &head, 0) < 0)
+ return -1;
+
+ if (full_paths)
+ d_filename = filename;
+ else
+ d_filename = basename(filename);
+
+ len = sym->end - sym->start;
+
+ if (print_line) {
+ get_source_line(he, len, filename);
+ print_summary(filename);
+ }
+
+ printf("\n\n------------------------------------------------\n");
+ printf(" Percent | Source code & Disassembly of %s\n", d_filename);
+ printf("------------------------------------------------\n");
+
+ if (verbose)
+ hist_entry__print_hits(he);
+
+ list_for_each_entry_safe(pos, n, &head, node) {
+ objdump_line__print(pos, &head, he, len);
+ list_del(&pos->node);
+ objdump_line__free(pos);
+ }
+
+ if (print_line)
+ free_source_line(he, len);
+
+ return 0;
+}
+
+static void hists__find_annotations(struct hists *self)
+{
+ struct rb_node *nd = rb_first(&self->entries), *next;
+ int key = KEY_RIGHT;
+
+ while (nd) {
+ struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
+ struct sym_priv *priv;
+
+ if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
+ goto find_next;
+
+ priv = symbol__priv(he->ms.sym);
+ if (priv->hist == NULL) {
+find_next:
+ if (key == KEY_LEFT)
+ nd = rb_prev(nd);
+ else
+ nd = rb_next(nd);
+ continue;
+ }
+
+ if (use_browser > 0) {
+ key = hist_entry__tui_annotate(he);
+ switch (key) {
+ case KEY_RIGHT:
+ next = rb_next(nd);
+ break;
+ case KEY_LEFT:
+ next = rb_prev(nd);
+ break;
+ default:
+ return;
+ }
+
+ if (next != NULL)
+ nd = next;
+ } else {
+ hist_entry__tty_annotate(he);
+ nd = rb_next(nd);
+ /*
+ * Since we have a hist_entry per IP for the same
+ * symbol, free he->ms.sym->hist to signal we already
+ * processed this symbol.
+ */
+ free(priv->hist);
+ priv->hist = NULL;
+ }
+ }
+}
+
+static struct perf_event_ops event_ops = {
+ .sample = process_sample_event,
+ .mmap = event__process_mmap,
+ .comm = event__process_comm,
+ .fork = event__process_task,
+ .ordered_samples = true,
+ .ordering_requires_timestamps = true,
+};
+
+static int __cmd_annotate(void)
+{
+ int ret;
+ struct perf_session *session;
+
+ session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
+ if (session == NULL)
+ return -ENOMEM;
+
+ ret = perf_session__process_events(session, &event_ops);
+ if (ret)
+ goto out_delete;
+
+ if (dump_trace) {
+ perf_session__fprintf_nr_events(session, stdout);
+ goto out_delete;
+ }
+
+ if (verbose > 3)
+ perf_session__fprintf(session, stdout);
+
+ if (verbose > 2)
+ perf_session__fprintf_dsos(session, stdout);
+
+ hists__collapse_resort(&session->hists);
+ hists__output_resort(&session->hists);
+ hists__find_annotations(&session->hists);
+out_delete:
+ perf_session__delete(session);
+
+ return ret;
+}
+
+static const char * const annotate_usage[] = {
+ "perf annotate [<options>] <command>",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
+ "only consider symbols in these dsos"),
+ OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
+ "symbol to annotate"),
+ OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
+ OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
+ OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
+ "load module symbols - WARNING: use only with -k and LIVE kernel"),
+ OPT_BOOLEAN('l', "print-line", &print_line,
+ "print matching source lines (may be slow)"),
+ OPT_BOOLEAN('P', "full-paths", &full_paths,
+ "Don't shorten the displayed pathnames"),
+ OPT_END()
+};
+
+int cmd_annotate(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, options, annotate_usage, 0);
+
+ if (use_stdio)
+ use_browser = 0;
+ else if (use_tui)
+ use_browser = 1;
+
+ setup_browser();
+
+ symbol_conf.priv_size = sizeof(struct sym_priv);
+ symbol_conf.try_vmlinux_path = true;
+
+ if (symbol__init() < 0)
+ return -1;
+
+ setup_sorting(annotate_usage, options);
+
+ if (argc) {
+ /*
+ * Special case: if there's an argument left then assume tha
+ * it's a symbol filter:
+ */
+ if (argc > 1)
+ usage_with_options(annotate_usage, options);
+
+ sym_hist_filter = argv[0];
+ }
+
+ if (field_sep && *field_sep == '.') {
+ pr_err("'.' is the only non valid --field-separator argument\n");
+ return -1;
+ }
+
+ return __cmd_annotate();
+}
diff --git a/smartt-perf/builtin-bench.c b/smartt-perf/builtin-bench.c
new file mode 100644
index 0000000..fcb9626
--- /dev/null
+++ b/smartt-perf/builtin-bench.c
@@ -0,0 +1,245 @@
+/*
+ *
+ * builtin-bench.c
+ *
+ * General benchmarking subsystem provided by perf
+ *
+ * Copyright (C) 2009, Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ *
+ */
+
+/*
+ *
+ * Available subsystem list:
+ * sched ... scheduler and IPC mechanism
+ * mem ... memory access performance
+ *
+ */
+
+#include "perf.h"
+#include "util/util.h"
+#include "util/parse-options.h"
+#include "builtin.h"
+#include "bench/bench.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct bench_suite {
+ const char *name;
+ const char *summary;
+ int (*fn)(int, const char **, const char *);
+};
+ \
+/* sentinel: easy for help */
+#define suite_all { "all", "test all suite (pseudo suite)", NULL }
+
+static struct bench_suite sched_suites[] = {
+ { "messaging",
+ "Benchmark for scheduler and IPC mechanisms",
+ bench_sched_messaging },
+ { "pipe",
+ "Flood of communication over pipe() between two processes",
+ bench_sched_pipe },
+ suite_all,
+ { NULL,
+ NULL,
+ NULL }
+};
+
+static struct bench_suite mem_suites[] = {
+ { "memcpy",
+ "Simple memory copy in various ways",
+ bench_mem_memcpy },
+ suite_all,
+ { NULL,
+ NULL,
+ NULL }
+};
+
+struct bench_subsys {
+ const char *name;
+ const char *summary;
+ struct bench_suite *suites;
+};
+
+static struct bench_subsys subsystems[] = {
+ { "sched",
+ "scheduler and IPC mechanism",
+ sched_suites },
+ { "mem",
+ "memory access performance",
+ mem_suites },
+ { "all", /* sentinel: easy for help */
+ "test all subsystem (pseudo subsystem)",
+ NULL },
+ { NULL,
+ NULL,
+ NULL }
+};
+
+static void dump_suites(int subsys_index)
+{
+ int i;
+
+ printf("# List of available suites for %s...\n\n",
+ subsystems[subsys_index].name);
+
+ for (i = 0; subsystems[subsys_index].suites[i].name; i++)
+ printf("%14s: %s\n",
+ subsystems[subsys_index].suites[i].name,
+ subsystems[subsys_index].suites[i].summary);
+
+ printf("\n");
+ return;
+}
+
+static const char *bench_format_str;
+int bench_format = BENCH_FORMAT_DEFAULT;
+
+static const struct option bench_options[] = {
+ OPT_STRING('f', "format", &bench_format_str, "default",
+ "Specify format style"),
+ OPT_END()
+};
+
+static const char * const bench_usage[] = {
+ "perf bench [<common options>] <subsystem> <suite> [<options>]",
+ NULL
+};
+
+static void print_usage(void)
+{
+ int i;
+
+ printf("Usage: \n");
+ for (i = 0; bench_usage[i]; i++)
+ printf("\t%s\n", bench_usage[i]);
+ printf("\n");
+
+ printf("# List of available subsystems...\n\n");
+
+ for (i = 0; subsystems[i].name; i++)
+ printf("%14s: %s\n",
+ subsystems[i].name, subsystems[i].summary);
+ printf("\n");
+}
+
+static int bench_str2int(const char *str)
+{
+ if (!str)
+ return BENCH_FORMAT_DEFAULT;
+
+ if (!strcmp(str, BENCH_FORMAT_DEFAULT_STR))
+ return BENCH_FORMAT_DEFAULT;
+ else if (!strcmp(str, BENCH_FORMAT_SIMPLE_STR))
+ return BENCH_FORMAT_SIMPLE;
+
+ return BENCH_FORMAT_UNKNOWN;
+}
+
+static void all_suite(struct bench_subsys *subsys) /* FROM HERE */
+{
+ int i;
+ const char *argv[2];
+ struct bench_suite *suites = subsys->suites;
+
+ argv[1] = NULL;
+ /*
+ * TODO:
+ * preparing preset parameters for
+ * embedded, ordinary PC, HPC, etc...
+ * will be helpful
+ */
+ for (i = 0; suites[i].fn; i++) {
+ printf("# Running %s/%s benchmark...\n",
+ subsys->name,
+ suites[i].name);
+
+ argv[1] = suites[i].name;
+ suites[i].fn(1, argv, NULL);
+ printf("\n");
+ }
+}
+
+static void all_subsystem(void)
+{
+ int i;
+ for (i = 0; subsystems[i].suites; i++)
+ all_suite(&subsystems[i]);
+}
+
+int cmd_bench(int argc, const char **argv, const char *prefix __used)
+{
+ int i, j, status = 0;
+
+ if (argc < 2) {
+ /* No subsystem specified. */
+ print_usage();
+ goto end;
+ }
+
+ argc = parse_options(argc, argv, bench_options, bench_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ bench_format = bench_str2int(bench_format_str);
+ if (bench_format == BENCH_FORMAT_UNKNOWN) {
+ printf("Unknown format descriptor:%s\n", bench_format_str);
+ goto end;
+ }
+
+ if (argc < 1) {
+ print_usage();
+ goto end;
+ }
+
+ if (!strcmp(argv[0], "all")) {
+ all_subsystem();
+ goto end;
+ }
+
+ for (i = 0; subsystems[i].name; i++) {
+ if (strcmp(subsystems[i].name, argv[0]))
+ continue;
+
+ if (argc < 2) {
+ /* No suite specified. */
+ dump_suites(i);
+ goto end;
+ }
+
+ if (!strcmp(argv[1], "all")) {
+ all_suite(&subsystems[i]);
+ goto end;
+ }
+
+ for (j = 0; subsystems[i].suites[j].name; j++) {
+ if (strcmp(subsystems[i].suites[j].name, argv[1]))
+ continue;
+
+ if (bench_format == BENCH_FORMAT_DEFAULT)
+ printf("# Running %s/%s benchmark...\n",
+ subsystems[i].name,
+ subsystems[i].suites[j].name);
+ status = subsystems[i].suites[j].fn(argc - 1,
+ argv + 1, prefix);
+ goto end;
+ }
+
+ if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
+ dump_suites(i);
+ goto end;
+ }
+
+ printf("Unknown suite:%s for %s\n", argv[1], argv[0]);
+ status = 1;
+ goto end;
+ }
+
+ printf("Unknown subsystem:%s\n", argv[0]);
+ status = 1;
+
+end:
+ return status;
+}
diff --git a/smartt-perf/builtin-buildid-cache.c b/smartt-perf/builtin-buildid-cache.c
new file mode 100644
index 0000000..29ad20e
--- /dev/null
+++ b/smartt-perf/builtin-buildid-cache.c
@@ -0,0 +1,132 @@
+/*
+ * builtin-buildid-cache.c
+ *
+ * Builtin buildid-cache command: Manages build-id cache
+ *
+ * Copyright (C) 2010, Red Hat Inc.
+ * Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+#include "builtin.h"
+#include "perf.h"
+#include "util/cache.h"
+#include "util/debug.h"
+#include "util/header.h"
+#include "util/parse-options.h"
+#include "util/strlist.h"
+#include "util/symbol.h"
+
+static char const *add_name_list_str, *remove_name_list_str;
+
+static const char * const buildid_cache_usage[] = {
+ "perf buildid-cache [<options>]",
+ NULL
+};
+
+static const struct option buildid_cache_options[] = {
+ OPT_STRING('a', "add", &add_name_list_str,
+ "file list", "file(s) to add"),
+ OPT_STRING('r', "remove", &remove_name_list_str, "file list",
+ "file(s) to remove"),
+ OPT_INCR('v', "verbose", &verbose, "be more verbose"),
+ OPT_END()
+};
+
+static int build_id_cache__add_file(const char *filename, const char *debugdir)
+{
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+ u8 build_id[BUILD_ID_SIZE];
+ int err;
+
+ if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+ pr_debug("Couldn't read a build-id in %s\n", filename);
+ return -1;
+ }
+
+ build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
+ err = build_id_cache__add_s(sbuild_id, debugdir, filename, false);
+ if (verbose)
+ pr_info("Adding %s %s: %s\n", sbuild_id, filename,
+ err ? "FAIL" : "Ok");
+ return err;
+}
+
+static int build_id_cache__remove_file(const char *filename __used,
+ const char *debugdir __used)
+{
+ u8 build_id[BUILD_ID_SIZE];
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ int err;
+
+ if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+ pr_debug("Couldn't read a build-id in %s\n", filename);
+ return -1;
+ }
+
+ build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
+ err = build_id_cache__remove_s(sbuild_id, debugdir);
+ if (verbose)
+ pr_info("Removing %s %s: %s\n", sbuild_id, filename,
+ err ? "FAIL" : "Ok");
+
+ return err;
+}
+
+static int __cmd_buildid_cache(void)
+{
+ struct strlist *list;
+ struct str_node *pos;
+ char debugdir[PATH_MAX];
+
+ snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
+
+ if (add_name_list_str) {
+ list = strlist__new(true, add_name_list_str);
+ if (list) {
+ strlist__for_each(pos, list)
+ if (build_id_cache__add_file(pos->s, debugdir)) {
+ if (errno == EEXIST) {
+ pr_debug("%s already in the cache\n",
+ pos->s);
+ continue;
+ }
+ pr_warning("Couldn't add %s: %s\n",
+ pos->s, strerror(errno));
+ }
+
+ strlist__delete(list);
+ }
+ }
+
+ if (remove_name_list_str) {
+ list = strlist__new(true, remove_name_list_str);
+ if (list) {
+ strlist__for_each(pos, list)
+ if (build_id_cache__remove_file(pos->s, debugdir)) {
+ if (errno == ENOENT) {
+ pr_debug("%s wasn't in the cache\n",
+ pos->s);
+ continue;
+ }
+ pr_warning("Couldn't remove %s: %s\n",
+ pos->s, strerror(errno));
+ }
+
+ strlist__delete(list);
+ }
+ }
+
+ return 0;
+}
+
+int cmd_buildid_cache(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, buildid_cache_options,
+ buildid_cache_usage, 0);
+
+ if (symbol__init() < 0)
+ return -1;
+
+ setup_pager();
+ return __cmd_buildid_cache();
+}
diff --git a/smartt-perf/builtin-buildid-list.c b/smartt-perf/builtin-buildid-list.c
new file mode 100644
index 0000000..5af32ae
--- /dev/null
+++ b/smartt-perf/builtin-buildid-list.c
@@ -0,0 +1,60 @@
+/*
+ * builtin-buildid-list.c
+ *
+ * Builtin buildid-list command: list buildids in perf.data
+ *
+ * Copyright (C) 2009, Red Hat Inc.
+ * Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+#include "builtin.h"
+#include "perf.h"
+#include "util/build-id.h"
+#include "util/cache.h"
+#include "util/debug.h"
+#include "util/parse-options.h"
+#include "util/session.h"
+#include "util/symbol.h"
+
+static char const *input_name = "perf.data";
+static bool force;
+static bool with_hits;
+
+static const char * const buildid_list_usage[] = {
+ "perf buildid-list [<options>]",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"),
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose"),
+ OPT_END()
+};
+
+static int __cmd_buildid_list(void)
+{
+ struct perf_session *session;
+
+ session = perf_session__new(input_name, O_RDONLY, force, false,
+ &build_id__mark_dso_hit_ops);
+ if (session == NULL)
+ return -1;
+
+ if (with_hits)
+ perf_session__process_events(session, &build_id__mark_dso_hit_ops);
+
+ perf_session__fprintf_dsos_buildid(session, stdout, with_hits);
+
+ perf_session__delete(session);
+ return 0;
+}
+
+int cmd_buildid_list(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, options, buildid_list_usage, 0);
+ setup_pager();
+ return __cmd_buildid_list();
+}
diff --git a/smartt-perf/builtin-diff.c b/smartt-perf/builtin-diff.c
new file mode 100644
index 0000000..3153e49
--- /dev/null
+++ b/smartt-perf/builtin-diff.c
@@ -0,0 +1,232 @@
+/*
+ * builtin-diff.c
+ *
+ * Builtin diff command: Analyze two perf.data input files, look up and read
+ * DSOs and symbol information, sort them and produce a diff.
+ */
+#include "builtin.h"
+
+#include "util/debug.h"
+#include "util/event.h"
+#include "util/hist.h"
+#include "util/session.h"
+#include "util/sort.h"
+#include "util/symbol.h"
+#include "util/util.h"
+
+#include <stdlib.h>
+
+static char const *input_old = "perf.data.old",
+ *input_new = "perf.data";
+static char diff__default_sort_order[] = "dso,symbol";
+static bool force;
+static bool show_displacement;
+
+static int hists__add_entry(struct hists *self,
+ struct addr_location *al, u64 period)
+{
+ if (__hists__add_entry(self, al, NULL, period) != NULL)
+ return 0;
+ return -ENOMEM;
+}
+
+static int diff__process_sample_event(event_t *event,
+ struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct addr_location al;
+
+ if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
+ pr_warning("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ if (al.filtered || al.sym == NULL)
+ return 0;
+
+ if (hists__add_entry(&session->hists, &al, sample->period)) {
+ pr_warning("problem incrementing symbol period, skipping event\n");
+ return -1;
+ }
+
+ session->hists.stats.total_period += sample->period;
+ return 0;
+}
+
+static struct perf_event_ops event_ops = {
+ .sample = diff__process_sample_event,
+ .mmap = event__process_mmap,
+ .comm = event__process_comm,
+ .exit = event__process_task,
+ .fork = event__process_task,
+ .lost = event__process_lost,
+ .ordered_samples = true,
+ .ordering_requires_timestamps = true,
+};
+
+static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
+ struct hist_entry *he)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+ if (hist_entry__cmp(he, iter) < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, root);
+}
+
+static void hists__resort_entries(struct hists *self)
+{
+ unsigned long position = 1;
+ struct rb_root tmp = RB_ROOT;
+ struct rb_node *next = rb_first(&self->entries);
+
+ while (next != NULL) {
+ struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node);
+
+ next = rb_next(&n->rb_node);
+ rb_erase(&n->rb_node, &self->entries);
+ n->position = position++;
+ perf_session__insert_hist_entry_by_name(&tmp, n);
+ }
+
+ self->entries = tmp;
+}
+
+static void hists__set_positions(struct hists *self)
+{
+ hists__output_resort(self);
+ hists__resort_entries(self);
+}
+
+static struct hist_entry *hists__find_entry(struct hists *self,
+ struct hist_entry *he)
+{
+ struct rb_node *n = self->entries.rb_node;
+
+ while (n) {
+ struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
+ int64_t cmp = hist_entry__cmp(he, iter);
+
+ if (cmp < 0)
+ n = n->rb_left;
+ else if (cmp > 0)
+ n = n->rb_right;
+ else
+ return iter;
+ }
+
+ return NULL;
+}
+
+static void hists__match(struct hists *older, struct hists *newer)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node);
+ pos->pair = hists__find_entry(older, pos);
+ }
+}
+
+static int __cmd_diff(void)
+{
+ int ret, i;
+ struct perf_session *session[2];
+
+ session[0] = perf_session__new(input_old, O_RDONLY, force, false, &event_ops);
+ session[1] = perf_session__new(input_new, O_RDONLY, force, false, &event_ops);
+ if (session[0] == NULL || session[1] == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < 2; ++i) {
+ ret = perf_session__process_events(session[i], &event_ops);
+ if (ret)
+ goto out_delete;
+ }
+
+ hists__output_resort(&session[1]->hists);
+ if (show_displacement)
+ hists__set_positions(&session[0]->hists);
+
+ hists__match(&session[0]->hists, &session[1]->hists);
+ hists__fprintf(&session[1]->hists, &session[0]->hists,
+ show_displacement, stdout);
+out_delete:
+ for (i = 0; i < 2; ++i)
+ perf_session__delete(session[i]);
+ return ret;
+}
+
+static const char * const diff_usage[] = {
+ "perf diff [<options>] [old_file] [new_file]",
+ NULL,
+};
+
+static const struct option options[] = {
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('M', "displacement", &show_displacement,
+ "Show position displacement relative to baseline"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
+ OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
+ "load module symbols - WARNING: use only with -k and LIVE kernel"),
+ OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
+ "only consider symbols in these dsos"),
+ OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
+ "only consider symbols in these comms"),
+ OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
+ "only consider these symbols"),
+ OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
+ "sort by key(s): pid, comm, dso, symbol, parent"),
+ OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
+ "separator for columns, no spaces will be added between "
+ "columns '.' is reserved."),
+ OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+ "Look for files with symbols relative to this directory"),
+ OPT_END()
+};
+
+int cmd_diff(int argc, const char **argv, const char *prefix __used)
+{
+ sort_order = diff__default_sort_order;
+ argc = parse_options(argc, argv, options, diff_usage, 0);
+ if (argc) {
+ if (argc > 2)
+ usage_with_options(diff_usage, options);
+ if (argc == 2) {
+ input_old = argv[0];
+ input_new = argv[1];
+ } else
+ input_new = argv[0];
+ } else if (symbol_conf.default_guest_vmlinux_name ||
+ symbol_conf.default_guest_kallsyms) {
+ input_old = "perf.data.host";
+ input_new = "perf.data.guest";
+ }
+
+ symbol_conf.exclude_other = false;
+ if (symbol__init() < 0)
+ return -1;
+
+ setup_sorting(diff_usage, options);
+ setup_pager();
+
+ sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL);
+ sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL);
+ sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL);
+
+ return __cmd_diff();
+}
diff --git a/smartt-perf/builtin-help.c b/smartt-perf/builtin-help.c
new file mode 100644
index 0000000..6d5a8a7
--- /dev/null
+++ b/smartt-perf/builtin-help.c
@@ -0,0 +1,459 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help command
+ */
+#include "perf.h"
+#include "util/cache.h"
+#include "builtin.h"
+#include "util/exec_cmd.h"
+#include "common-cmds.h"
+#include "util/parse-options.h"
+#include "util/run-command.h"
+#include "util/help.h"
+
+static struct man_viewer_list {
+ struct man_viewer_list *next;
+ char name[FLEX_ARRAY];
+} *man_viewer_list;
+
+static struct man_viewer_info_list {
+ struct man_viewer_info_list *next;
+ const char *info;
+ char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
+enum help_format {
+ HELP_FORMAT_MAN,
+ HELP_FORMAT_INFO,
+ HELP_FORMAT_WEB,
+};
+
+static bool show_all = false;
+static enum help_format help_format = HELP_FORMAT_MAN;
+static struct option builtin_help_options[] = {
+ OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+ OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+ OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
+ HELP_FORMAT_WEB),
+ OPT_SET_UINT('i', "info", &help_format, "show info page",
+ HELP_FORMAT_INFO),
+ OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+ "perf help [--all] [--man|--web|--info] [command]",
+ NULL
+};
+
+static enum help_format parse_help_format(const char *format)
+{
+ if (!strcmp(format, "man"))
+ return HELP_FORMAT_MAN;
+ if (!strcmp(format, "info"))
+ return HELP_FORMAT_INFO;
+ if (!strcmp(format, "web") || !strcmp(format, "html"))
+ return HELP_FORMAT_WEB;
+ die("unrecognized help format '%s'", format);
+}
+
+static const char *get_man_viewer_info(const char *name)
+{
+ struct man_viewer_info_list *viewer;
+
+ for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) {
+ if (!strcasecmp(name, viewer->name))
+ return viewer->info;
+ }
+ return NULL;
+}
+
+static int check_emacsclient_version(void)
+{
+ struct strbuf buffer = STRBUF_INIT;
+ struct child_process ec_process;
+ const char *argv_ec[] = { "emacsclient", "--version", NULL };
+ int version;
+
+ /* emacsclient prints its version number on stderr */
+ memset(&ec_process, 0, sizeof(ec_process));
+ ec_process.argv = argv_ec;
+ ec_process.err = -1;
+ ec_process.stdout_to_stderr = 1;
+ if (start_command(&ec_process)) {
+ fprintf(stderr, "Failed to start emacsclient.\n");
+ return -1;
+ }
+ strbuf_read(&buffer, ec_process.err, 20);
+ close(ec_process.err);
+
+ /*
+ * Don't bother checking return value, because "emacsclient --version"
+ * seems to always exits with code 1.
+ */
+ finish_command(&ec_process);
+
+ if (prefixcmp(buffer.buf, "emacsclient")) {
+ fprintf(stderr, "Failed to parse emacsclient version.\n");
+ strbuf_release(&buffer);
+ return -1;
+ }
+
+ strbuf_remove(&buffer, 0, strlen("emacsclient"));
+ version = atoi(buffer.buf);
+
+ if (version < 22) {
+ fprintf(stderr,
+ "emacsclient version '%d' too old (< 22).\n",
+ version);
+ strbuf_release(&buffer);
+ return -1;
+ }
+
+ strbuf_release(&buffer);
+ return 0;
+}
+
+static void exec_woman_emacs(const char *path, const char *page)
+{
+ if (!check_emacsclient_version()) {
+ /* This works only with emacsclient version >= 22. */
+ struct strbuf man_page = STRBUF_INIT;
+
+ if (!path)
+ path = "emacsclient";
+ strbuf_addf(&man_page, "(woman \"%s\")", page);
+ execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+ warning("failed to exec '%s': %s", path, strerror(errno));
+ }
+}
+
+static void exec_man_konqueror(const char *path, const char *page)
+{
+ const char *display = getenv("DISPLAY");
+ if (display && *display) {
+ struct strbuf man_page = STRBUF_INIT;
+ const char *filename = "kfmclient";
+
+ /* It's simpler to launch konqueror using kfmclient. */
+ if (path) {
+ const char *file = strrchr(path, '/');
+ if (file && !strcmp(file + 1, "konqueror")) {
+ char *new = strdup(path);
+ char *dest = strrchr(new, '/');
+
+ /* strlen("konqueror") == strlen("kfmclient") */
+ strcpy(dest + 1, "kfmclient");
+ path = new;
+ }
+ if (file)
+ filename = file;
+ } else
+ path = "kfmclient";
+ strbuf_addf(&man_page, "man:%s(1)", page);
+ execlp(path, filename, "newTab", man_page.buf, NULL);
+ warning("failed to exec '%s': %s", path, strerror(errno));
+ }
+}
+
+static void exec_man_man(const char *path, const char *page)
+{
+ if (!path)
+ path = "man";
+ execlp(path, "man", page, NULL);
+ warning("failed to exec '%s': %s", path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
+{
+ struct strbuf shell_cmd = STRBUF_INIT;
+ strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+ execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+ warning("failed to exec '%s': %s", cmd, strerror(errno));
+}
+
+static void add_man_viewer(const char *name)
+{
+ struct man_viewer_list **p = &man_viewer_list;
+ size_t len = strlen(name);
+
+ while (*p)
+ p = &((*p)->next);
+ *p = zalloc(sizeof(**p) + len + 1);
+ strncpy((*p)->name, name, len);
+}
+
+static int supported_man_viewer(const char *name, size_t len)
+{
+ return (!strncasecmp("man", name, len) ||
+ !strncasecmp("woman", name, len) ||
+ !strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+ size_t len,
+ const char *value)
+{
+ struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1);
+
+ strncpy(new->name, name, len);
+ new->info = strdup(value);
+ new->next = man_viewer_info_list;
+ man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+ size_t len,
+ const char *value)
+{
+ if (supported_man_viewer(name, len))
+ do_add_man_viewer_info(name, len, value);
+ else
+ warning("'%s': path for unsupported man viewer.\n"
+ "Please consider using 'man.<tool>.cmd' instead.",
+ name);
+
+ return 0;
+}
+
+static int add_man_viewer_cmd(const char *name,
+ size_t len,
+ const char *value)
+{
+ if (supported_man_viewer(name, len))
+ warning("'%s': cmd for supported man viewer.\n"
+ "Please consider using 'man.<tool>.path' instead.",
+ name);
+ else
+ do_add_man_viewer_info(name, len, value);
+
+ return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+ const char *name = var + 4;
+ const char *subkey = strrchr(name, '.');
+
+ if (!subkey)
+ return error("Config with no key for man viewer: %s", name);
+
+ if (!strcmp(subkey, ".path")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return add_man_viewer_path(name, subkey - name, value);
+ }
+ if (!strcmp(subkey, ".cmd")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return add_man_viewer_cmd(name, subkey - name, value);
+ }
+
+ warning("'%s': unsupported man viewer sub key.", subkey);
+ return 0;
+}
+
+static int perf_help_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "help.format")) {
+ if (!value)
+ return config_error_nonbool(var);
+ help_format = parse_help_format(value);
+ return 0;
+ }
+ if (!strcmp(var, "man.viewer")) {
+ if (!value)
+ return config_error_nonbool(var);
+ add_man_viewer(value);
+ return 0;
+ }
+ if (!prefixcmp(var, "man."))
+ return add_man_viewer_info(var, value);
+
+ return perf_default_config(var, value, cb);
+}
+
+static struct cmdnames main_cmds, other_cmds;
+
+void list_common_cmds_help(void)
+{
+ unsigned int i, longest = 0;
+
+ for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+ if (longest < strlen(common_cmds[i].name))
+ longest = strlen(common_cmds[i].name);
+ }
+
+ puts(" The most commonly used perf commands are:");
+ for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+ printf(" %-*s ", longest, common_cmds[i].name);
+ puts(common_cmds[i].help);
+ }
+}
+
+static int is_perf_command(const char *s)
+{
+ return is_in_cmdlist(&main_cmds, s) ||
+ is_in_cmdlist(&other_cmds, s);
+}
+
+static const char *prepend(const char *prefix, const char *cmd)
+{
+ size_t pre_len = strlen(prefix);
+ size_t cmd_len = strlen(cmd);
+ char *p = malloc(pre_len + cmd_len + 1);
+ memcpy(p, prefix, pre_len);
+ strcpy(p + pre_len, cmd);
+ return p;
+}
+
+static const char *cmd_to_page(const char *perf_cmd)
+{
+ if (!perf_cmd)
+ return "perf";
+ else if (!prefixcmp(perf_cmd, "perf"))
+ return perf_cmd;
+ else
+ return prepend("perf-", perf_cmd);
+}
+
+static void setup_man_path(void)
+{
+ struct strbuf new_path = STRBUF_INIT;
+ const char *old_path = getenv("MANPATH");
+
+ /* We should always put ':' after our path. If there is no
+ * old_path, the ':' at the end will let 'man' to try
+ * system-wide paths after ours to find the manual page. If
+ * there is old_path, we need ':' as delimiter. */
+ strbuf_addstr(&new_path, system_path(PERF_MAN_PATH));
+ strbuf_addch(&new_path, ':');
+ if (old_path)
+ strbuf_addstr(&new_path, old_path);
+
+ setenv("MANPATH", new_path.buf, 1);
+
+ strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+ const char *info = get_man_viewer_info(name);
+
+ if (!strcasecmp(name, "man"))
+ exec_man_man(info, page);
+ else if (!strcasecmp(name, "woman"))
+ exec_woman_emacs(info, page);
+ else if (!strcasecmp(name, "konqueror"))
+ exec_man_konqueror(info, page);
+ else if (info)
+ exec_man_cmd(info, page);
+ else
+ warning("'%s': unknown man viewer.", name);
+}
+
+static void show_man_page(const char *perf_cmd)
+{
+ struct man_viewer_list *viewer;
+ const char *page = cmd_to_page(perf_cmd);
+ const char *fallback = getenv("PERF_MAN_VIEWER");
+
+ setup_man_path();
+ for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+ exec_viewer(viewer->name, page); /* will return when unable */
+
+ if (fallback)
+ exec_viewer(fallback, page);
+ exec_viewer("man", page);
+ die("no man viewer handled the request");
+}
+
+static void show_info_page(const char *perf_cmd)
+{
+ const char *page = cmd_to_page(perf_cmd);
+ setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
+ execlp("info", "info", "perfman", page, NULL);
+}
+
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+ struct stat st;
+ const char *html_path = system_path(PERF_HTML_PATH);
+
+ /* Check that we have a perf documentation directory. */
+ if (stat(mkpath("%s/perf.html", html_path), &st)
+ || !S_ISREG(st.st_mode))
+ die("'%s': not a documentation directory.", html_path);
+
+ strbuf_init(page_path, 0);
+ strbuf_addf(page_path, "%s/%s.html", html_path, page);
+}
+
+/*
+ * If open_html is not defined in a platform-specific way (see for
+ * example compat/mingw.h), we use the script web--browse to display
+ * HTML.
+ */
+#ifndef open_html
+static void open_html(const char *path)
+{
+ execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL);
+}
+#endif
+
+static void show_html_page(const char *perf_cmd)
+{
+ const char *page = cmd_to_page(perf_cmd);
+ struct strbuf page_path; /* it leaks but we exec bellow */
+
+ get_html_page_path(&page_path, page);
+
+ open_html(page_path.buf);
+}
+
+int cmd_help(int argc, const char **argv, const char *prefix __used)
+{
+ const char *alias;
+
+ load_command_list("perf-", &main_cmds, &other_cmds);
+
+ perf_config(perf_help_config, NULL);
+
+ argc = parse_options(argc, argv, builtin_help_options,
+ builtin_help_usage, 0);
+
+ if (show_all) {
+ printf("\n usage: %s\n\n", perf_usage_string);
+ list_commands("perf commands", &main_cmds, &other_cmds);
+ printf(" %s\n\n", perf_more_info_string);
+ return 0;
+ }
+
+ if (!argv[0]) {
+ printf("\n usage: %s\n\n", perf_usage_string);
+ list_common_cmds_help();
+ printf("\n %s\n\n", perf_more_info_string);
+ return 0;
+ }
+
+ alias = alias_lookup(argv[0]);
+ if (alias && !is_perf_command(argv[0])) {
+ printf("`perf %s' is aliased to `%s'\n", argv[0], alias);
+ return 0;
+ }
+
+ switch (help_format) {
+ case HELP_FORMAT_MAN:
+ show_man_page(argv[0]);
+ break;
+ case HELP_FORMAT_INFO:
+ show_info_page(argv[0]);
+ break;
+ case HELP_FORMAT_WEB:
+ show_html_page(argv[0]);
+ default:
+ break;
+ }
+
+ return 0;
+}
diff --git a/smartt-perf/builtin-inject.c b/smartt-perf/builtin-inject.c
new file mode 100644
index 0000000..0c78ffa
--- /dev/null
+++ b/smartt-perf/builtin-inject.c
@@ -0,0 +1,237 @@
+/*
+ * builtin-inject.c
+ *
+ * Builtin inject command: Examine the live mode (stdin) event stream
+ * and repipe it to stdout while optionally injecting additional
+ * events into it.
+ */
+#include "builtin.h"
+
+#include "perf.h"
+#include "util/session.h"
+#include "util/debug.h"
+
+#include "util/parse-options.h"
+
+static char const *input_name = "-";
+static bool inject_build_ids;
+
+static int event__repipe_synth(event_t *event,
+ struct perf_session *session __used)
+{
+ uint32_t size;
+ void *buf = event;
+
+ size = event->header.size;
+
+ while (size) {
+ int ret = write(STDOUT_FILENO, buf, size);
+ if (ret < 0)
+ return -errno;
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return 0;
+}
+
+static int event__repipe(event_t *event, struct sample_data *sample __used,
+ struct perf_session *session)
+{
+ return event__repipe_synth(event, session);
+}
+
+static int event__repipe_mmap(event_t *self, struct sample_data *sample,
+ struct perf_session *session)
+{
+ int err;
+
+ err = event__process_mmap(self, sample, session);
+ event__repipe(self, sample, session);
+
+ return err;
+}
+
+static int event__repipe_task(event_t *self, struct sample_data *sample,
+ struct perf_session *session)
+{
+ int err;
+
+ err = event__process_task(self, sample, session);
+ event__repipe(self, sample, session);
+
+ return err;
+}
+
+static int event__repipe_tracing_data(event_t *self,
+ struct perf_session *session)
+{
+ int err;
+
+ event__repipe_synth(self, session);
+ err = event__process_tracing_data(self, session);
+
+ return err;
+}
+
+static int dso__read_build_id(struct dso *self)
+{
+ if (self->has_build_id)
+ return 0;
+
+ if (filename__read_build_id(self->long_name, self->build_id,
+ sizeof(self->build_id)) > 0) {
+ self->has_build_id = true;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int dso__inject_build_id(struct dso *self, struct perf_session *session)
+{
+ u16 misc = PERF_RECORD_MISC_USER;
+ struct machine *machine;
+ int err;
+
+ if (dso__read_build_id(self) < 0) {
+ pr_debug("no build_id found for %s\n", self->long_name);
+ return -1;
+ }
+
+ machine = perf_session__find_host_machine(session);
+ if (machine == NULL) {
+ pr_err("Can't find machine for session\n");
+ return -1;
+ }
+
+ if (self->kernel)
+ misc = PERF_RECORD_MISC_KERNEL;
+
+ err = event__synthesize_build_id(self, misc, event__repipe,
+ machine, session);
+ if (err) {
+ pr_err("Can't synthesize build_id event for %s\n", self->long_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int event__inject_buildid(event_t *event, struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct addr_location al;
+ struct thread *thread;
+ u8 cpumode;
+
+ cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+
+ thread = perf_session__findnew(session, event->ip.pid);
+ if (thread == NULL) {
+ pr_err("problem processing %d event, skipping it.\n",
+ event->header.type);
+ goto repipe;
+ }
+
+ thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
+ event->ip.pid, event->ip.ip, &al);
+
+ if (al.map != NULL) {
+ if (!al.map->dso->hit) {
+ al.map->dso->hit = 1;
+ if (map__load(al.map, NULL) >= 0) {
+ dso__inject_build_id(al.map->dso, session);
+ /*
+ * If this fails, too bad, let the other side
+ * account this as unresolved.
+ */
+ } else
+ pr_warning("no symbols found in %s, maybe "
+ "install a debug package?\n",
+ al.map->dso->long_name);
+ }
+ }
+
+repipe:
+ event__repipe(event, sample, session);
+ return 0;
+}
+
+struct perf_event_ops inject_ops = {
+ .sample = event__repipe,
+ .mmap = event__repipe,
+ .comm = event__repipe,
+ .fork = event__repipe,
+ .exit = event__repipe,
+ .lost = event__repipe,
+ .read = event__repipe,
+ .throttle = event__repipe,
+ .unthrottle = event__repipe,
+ .attr = event__repipe_synth,
+ .event_type = event__repipe_synth,
+ .tracing_data = event__repipe_synth,
+ .build_id = event__repipe_synth,
+};
+
+extern volatile int session_done;
+
+static void sig_handler(int sig __attribute__((__unused__)))
+{
+ session_done = 1;
+}
+
+static int __cmd_inject(void)
+{
+ struct perf_session *session;
+ int ret = -EINVAL;
+
+ signal(SIGINT, sig_handler);
+
+ if (inject_build_ids) {
+ inject_ops.sample = event__inject_buildid;
+ inject_ops.mmap = event__repipe_mmap;
+ inject_ops.fork = event__repipe_task;
+ inject_ops.tracing_data = event__repipe_tracing_data;
+ }
+
+ session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops);
+ if (session == NULL)
+ return -ENOMEM;
+
+ ret = perf_session__process_events(session, &inject_ops);
+
+ perf_session__delete(session);
+
+ return ret;
+}
+
+static const char * const report_usage[] = {
+ "perf inject [<options>]",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_BOOLEAN('b', "build-ids", &inject_build_ids,
+ "Inject build-ids into the output stream"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show build ids, etc)"),
+ OPT_END()
+};
+
+int cmd_inject(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, options, report_usage, 0);
+
+ /*
+ * Any (unrecognized) arguments left?
+ */
+ if (argc)
+ usage_with_options(report_usage, options);
+
+ if (symbol__init() < 0)
+ return -1;
+
+ return __cmd_inject();
+}
diff --git a/smartt-perf/builtin-kmem.c b/smartt-perf/builtin-kmem.c
new file mode 100644
index 0000000..d97256d
--- /dev/null
+++ b/smartt-perf/builtin-kmem.c
@@ -0,0 +1,777 @@
+#include "builtin.h"
+#include "perf.h"
+
+#include "util/util.h"
+#include "util/cache.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/header.h"
+#include "util/session.h"
+
+#include "util/parse-options.h"
+#include "util/trace-event.h"
+
+#include "util/debug.h"
+
+#include <linux/rbtree.h>
+
+struct alloc_stat;
+typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
+
+static char const *input_name = "perf.data";
+
+static int alloc_flag;
+static int caller_flag;
+
+static int alloc_lines = -1;
+static int caller_lines = -1;
+
+static bool raw_ip;
+
+static char default_sort_order[] = "frag,hit,bytes";
+
+static int *cpunode_map;
+static int max_cpu_num;
+
+struct alloc_stat {
+ u64 call_site;
+ u64 ptr;
+ u64 bytes_req;
+ u64 bytes_alloc;
+ u32 hit;
+ u32 pingpong;
+
+ short alloc_cpu;
+
+ struct rb_node node;
+};
+
+static struct rb_root root_alloc_stat;
+static struct rb_root root_alloc_sorted;
+static struct rb_root root_caller_stat;
+static struct rb_root root_caller_sorted;
+
+static unsigned long total_requested, total_allocated;
+static unsigned long nr_allocs, nr_cross_allocs;
+
+#define PATH_SYS_NODE "/sys/devices/system/node"
+
+static void init_cpunode_map(void)
+{
+ FILE *fp;
+ int i;
+
+ fp = fopen("/sys/devices/system/cpu/kernel_max", "r");
+ if (!fp) {
+ max_cpu_num = 4096;
+ return;
+ }
+
+ if (fscanf(fp, "%d", &max_cpu_num) < 1)
+ die("Failed to read 'kernel_max' from sysfs");
+ max_cpu_num++;
+
+ cpunode_map = calloc(max_cpu_num, sizeof(int));
+ if (!cpunode_map)
+ die("calloc");
+ for (i = 0; i < max_cpu_num; i++)
+ cpunode_map[i] = -1;
+ fclose(fp);
+}
+
+static void setup_cpunode_map(void)
+{
+ struct dirent *dent1, *dent2;
+ DIR *dir1, *dir2;
+ unsigned int cpu, mem;
+ char buf[PATH_MAX];
+
+ init_cpunode_map();
+
+ dir1 = opendir(PATH_SYS_NODE);
+ if (!dir1)
+ return;
+
+ while ((dent1 = readdir(dir1)) != NULL) {
+ if (dent1->d_type != DT_DIR ||
+ sscanf(dent1->d_name, "node%u", &mem) < 1)
+ continue;
+
+ snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name);
+ dir2 = opendir(buf);
+ if (!dir2)
+ continue;
+ while ((dent2 = readdir(dir2)) != NULL) {
+ if (dent2->d_type != DT_LNK ||
+ sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
+ continue;
+ cpunode_map[cpu] = mem;
+ }
+ }
+}
+
+static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
+ int bytes_req, int bytes_alloc, int cpu)
+{
+ struct rb_node **node = &root_alloc_stat.rb_node;
+ struct rb_node *parent = NULL;
+ struct alloc_stat *data = NULL;
+
+ while (*node) {
+ parent = *node;
+ data = rb_entry(*node, struct alloc_stat, node);
+
+ if (ptr > data->ptr)
+ node = &(*node)->rb_right;
+ else if (ptr < data->ptr)
+ node = &(*node)->rb_left;
+ else
+ break;
+ }
+
+ if (data && data->ptr == ptr) {
+ data->hit++;
+ data->bytes_req += bytes_req;
+ data->bytes_alloc += bytes_alloc;
+ } else {
+ data = malloc(sizeof(*data));
+ if (!data)
+ die("malloc");
+ data->ptr = ptr;
+ data->pingpong = 0;
+ data->hit = 1;
+ data->bytes_req = bytes_req;
+ data->bytes_alloc = bytes_alloc;
+
+ rb_link_node(&data->node, parent, node);
+ rb_insert_color(&data->node, &root_alloc_stat);
+ }
+ data->call_site = call_site;
+ data->alloc_cpu = cpu;
+}
+
+static void insert_caller_stat(unsigned long call_site,
+ int bytes_req, int bytes_alloc)
+{
+ struct rb_node **node = &root_caller_stat.rb_node;
+ struct rb_node *parent = NULL;
+ struct alloc_stat *data = NULL;
+
+ while (*node) {
+ parent = *node;
+ data = rb_entry(*node, struct alloc_stat, node);
+
+ if (call_site > data->call_site)
+ node = &(*node)->rb_right;
+ else if (call_site < data->call_site)
+ node = &(*node)->rb_left;
+ else
+ break;
+ }
+
+ if (data && data->call_site == call_site) {
+ data->hit++;
+ data->bytes_req += bytes_req;
+ data->bytes_alloc += bytes_alloc;
+ } else {
+ data = malloc(sizeof(*data));
+ if (!data)
+ die("malloc");
+ data->call_site = call_site;
+ data->pingpong = 0;
+ data->hit = 1;
+ data->bytes_req = bytes_req;
+ data->bytes_alloc = bytes_alloc;
+
+ rb_link_node(&data->node, parent, node);
+ rb_insert_color(&data->node, &root_caller_stat);
+ }
+}
+
+static void process_alloc_event(void *data,
+ struct event *event,
+ int cpu,
+ u64 timestamp __used,
+ struct thread *thread __used,
+ int node)
+{
+ unsigned long call_site;
+ unsigned long ptr;
+ int bytes_req;
+ int bytes_alloc;
+ int node1, node2;
+
+ ptr = raw_field_value(event, "ptr", data);
+ call_site = raw_field_value(event, "call_site", data);
+ bytes_req = raw_field_value(event, "bytes_req", data);
+ bytes_alloc = raw_field_value(event, "bytes_alloc", data);
+
+ insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
+ insert_caller_stat(call_site, bytes_req, bytes_alloc);
+
+ total_requested += bytes_req;
+ total_allocated += bytes_alloc;
+
+ if (node) {
+ node1 = cpunode_map[cpu];
+ node2 = raw_field_value(event, "node", data);
+ if (node1 != node2)
+ nr_cross_allocs++;
+ }
+ nr_allocs++;
+}
+
+static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
+static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
+
+static struct alloc_stat *search_alloc_stat(unsigned long ptr,
+ unsigned long call_site,
+ struct rb_root *root,
+ sort_fn_t sort_fn)
+{
+ struct rb_node *node = root->rb_node;
+ struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
+
+ while (node) {
+ struct alloc_stat *data;
+ int cmp;
+
+ data = rb_entry(node, struct alloc_stat, node);
+
+ cmp = sort_fn(&key, data);
+ if (cmp < 0)
+ node = node->rb_left;
+ else if (cmp > 0)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
+}
+
+static void process_free_event(void *data,
+ struct event *event,
+ int cpu,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ unsigned long ptr;
+ struct alloc_stat *s_alloc, *s_caller;
+
+ ptr = raw_field_value(event, "ptr", data);
+
+ s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
+ if (!s_alloc)
+ return;
+
+ if (cpu != s_alloc->alloc_cpu) {
+ s_alloc->pingpong++;
+
+ s_caller = search_alloc_stat(0, s_alloc->call_site,
+ &root_caller_stat, callsite_cmp);
+ assert(s_caller);
+ s_caller->pingpong++;
+ }
+ s_alloc->alloc_cpu = -1;
+}
+
+static void
+process_raw_event(event_t *raw_event __used, void *data,
+ int cpu, u64 timestamp, struct thread *thread)
+{
+ struct event *event;
+ int type;
+
+ type = trace_parse_common_type(data);
+ event = trace_find_event(type);
+
+ if (!strcmp(event->name, "kmalloc") ||
+ !strcmp(event->name, "kmem_cache_alloc")) {
+ process_alloc_event(data, event, cpu, timestamp, thread, 0);
+ return;
+ }
+
+ if (!strcmp(event->name, "kmalloc_node") ||
+ !strcmp(event->name, "kmem_cache_alloc_node")) {
+ process_alloc_event(data, event, cpu, timestamp, thread, 1);
+ return;
+ }
+
+ if (!strcmp(event->name, "kfree") ||
+ !strcmp(event->name, "kmem_cache_free")) {
+ process_free_event(data, event, cpu, timestamp, thread);
+ return;
+ }
+}
+
+static int process_sample_event(event_t *event, struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct thread *thread = perf_session__findnew(session, event->ip.pid);
+
+ if (thread == NULL) {
+ pr_debug("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+
+ process_raw_event(event, sample->raw_data, sample->cpu,
+ sample->time, thread);
+
+ return 0;
+}
+
+static struct perf_event_ops event_ops = {
+ .sample = process_sample_event,
+ .comm = event__process_comm,
+ .ordered_samples = true,
+};
+
+static double fragmentation(unsigned long n_req, unsigned long n_alloc)
+{
+ if (n_alloc == 0)
+ return 0.0;
+ else
+ return 100.0 - (100.0 * n_req / n_alloc);
+}
+
+static void __print_result(struct rb_root *root, struct perf_session *session,
+ int n_lines, int is_caller)
+{
+ struct rb_node *next;
+ struct machine *machine;
+
+ printf("%.102s\n", graph_dotted_line);
+ printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
+ printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n");
+ printf("%.102s\n", graph_dotted_line);
+
+ next = rb_first(root);
+
+ machine = perf_session__find_host_machine(session);
+ if (!machine) {
+ pr_err("__print_result: couldn't find kernel information\n");
+ return;
+ }
+ while (next && n_lines--) {
+ struct alloc_stat *data = rb_entry(next, struct alloc_stat,
+ node);
+ struct symbol *sym = NULL;
+ struct map *map;
+ char buf[BUFSIZ];
+ u64 addr;
+
+ if (is_caller) {
+ addr = data->call_site;
+ if (!raw_ip)
+ sym = machine__find_kernel_function(machine, addr, &map, NULL);
+ } else
+ addr = data->ptr;
+
+ if (sym != NULL)
+ snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
+ addr - map->unmap_ip(map, sym->start));
+ else
+ snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
+ printf(" %-34s |", buf);
+
+ printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n",
+ (unsigned long long)data->bytes_alloc,
+ (unsigned long)data->bytes_alloc / data->hit,
+ (unsigned long long)data->bytes_req,
+ (unsigned long)data->bytes_req / data->hit,
+ (unsigned long)data->hit,
+ (unsigned long)data->pingpong,
+ fragmentation(data->bytes_req, data->bytes_alloc));
+
+ next = rb_next(next);
+ }
+
+ if (n_lines == -1)
+ printf(" ... | ... | ... | ... | ... | ... \n");
+
+ printf("%.102s\n", graph_dotted_line);
+}
+
+static void print_summary(void)
+{
+ printf("\nSUMMARY\n=======\n");
+ printf("Total bytes requested: %lu\n", total_requested);
+ printf("Total bytes allocated: %lu\n", total_allocated);
+ printf("Total bytes wasted on internal fragmentation: %lu\n",
+ total_allocated - total_requested);
+ printf("Internal fragmentation: %f%%\n",
+ fragmentation(total_requested, total_allocated));
+ printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
+}
+
+static void print_result(struct perf_session *session)
+{
+ if (caller_flag)
+ __print_result(&root_caller_sorted, session, caller_lines, 1);
+ if (alloc_flag)
+ __print_result(&root_alloc_sorted, session, alloc_lines, 0);
+ print_summary();
+}
+
+struct sort_dimension {
+ const char name[20];
+ sort_fn_t cmp;
+ struct list_head list;
+};
+
+static LIST_HEAD(caller_sort);
+static LIST_HEAD(alloc_sort);
+
+static void sort_insert(struct rb_root *root, struct alloc_stat *data,
+ struct list_head *sort_list)
+{
+ struct rb_node **new = &(root->rb_node);
+ struct rb_node *parent = NULL;
+ struct sort_dimension *sort;
+
+ while (*new) {
+ struct alloc_stat *this;
+ int cmp = 0;
+
+ this = rb_entry(*new, struct alloc_stat, node);
+ parent = *new;
+
+ list_for_each_entry(sort, sort_list, list) {
+ cmp = sort->cmp(data, this);
+ if (cmp)
+ break;
+ }
+
+ if (cmp > 0)
+ new = &((*new)->rb_left);
+ else
+ new = &((*new)->rb_right);
+ }
+
+ rb_link_node(&data->node, parent, new);
+ rb_insert_color(&data->node, root);
+}
+
+static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
+ struct list_head *sort_list)
+{
+ struct rb_node *node;
+ struct alloc_stat *data;
+
+ for (;;) {
+ node = rb_first(root);
+ if (!node)
+ break;
+
+ rb_erase(node, root);
+ data = rb_entry(node, struct alloc_stat, node);
+ sort_insert(root_sorted, data, sort_list);
+ }
+}
+
+static void sort_result(void)
+{
+ __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
+ __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
+}
+
+static int __cmd_kmem(void)
+{
+ int err = -EINVAL;
+ struct perf_session *session = perf_session__new(input_name, O_RDONLY,
+ 0, false, &event_ops);
+ if (session == NULL)
+ return -ENOMEM;
+
+ if (perf_session__create_kernel_maps(session) < 0)
+ goto out_delete;
+
+ if (!perf_session__has_traces(session, "kmem record"))
+ goto out_delete;
+
+ setup_pager();
+ err = perf_session__process_events(session, &event_ops);
+ if (err != 0)
+ goto out_delete;
+ sort_result();
+ print_result(session);
+out_delete:
+ perf_session__delete(session);
+ return err;
+}
+
+static const char * const kmem_usage[] = {
+ "perf kmem [<options>] {record|stat}",
+ NULL
+};
+
+static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
+{
+ if (l->ptr < r->ptr)
+ return -1;
+ else if (l->ptr > r->ptr)
+ return 1;
+ return 0;
+}
+
+static struct sort_dimension ptr_sort_dimension = {
+ .name = "ptr",
+ .cmp = ptr_cmp,
+};
+
+static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
+{
+ if (l->call_site < r->call_site)
+ return -1;
+ else if (l->call_site > r->call_site)
+ return 1;
+ return 0;
+}
+
+static struct sort_dimension callsite_sort_dimension = {
+ .name = "callsite",
+ .cmp = callsite_cmp,
+};
+
+static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
+{
+ if (l->hit < r->hit)
+ return -1;
+ else if (l->hit > r->hit)
+ return 1;
+ return 0;
+}
+
+static struct sort_dimension hit_sort_dimension = {
+ .name = "hit",
+ .cmp = hit_cmp,
+};
+
+static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
+{
+ if (l->bytes_alloc < r->bytes_alloc)
+ return -1;
+ else if (l->bytes_alloc > r->bytes_alloc)
+ return 1;
+ return 0;
+}
+
+static struct sort_dimension bytes_sort_dimension = {
+ .name = "bytes",
+ .cmp = bytes_cmp,
+};
+
+static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
+{
+ double x, y;
+
+ x = fragmentation(l->bytes_req, l->bytes_alloc);
+ y = fragmentation(r->bytes_req, r->bytes_alloc);
+
+ if (x < y)
+ return -1;
+ else if (x > y)
+ return 1;
+ return 0;
+}
+
+static struct sort_dimension frag_sort_dimension = {
+ .name = "frag",
+ .cmp = frag_cmp,
+};
+
+static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
+{
+ if (l->pingpong < r->pingpong)
+ return -1;
+ else if (l->pingpong > r->pingpong)
+ return 1;
+ return 0;
+}
+
+static struct sort_dimension pingpong_sort_dimension = {
+ .name = "pingpong",
+ .cmp = pingpong_cmp,
+};
+
+static struct sort_dimension *avail_sorts[] = {
+ &ptr_sort_dimension,
+ &callsite_sort_dimension,
+ &hit_sort_dimension,
+ &bytes_sort_dimension,
+ &frag_sort_dimension,
+ &pingpong_sort_dimension,
+};
+
+#define NUM_AVAIL_SORTS \
+ (int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
+
+static int sort_dimension__add(const char *tok, struct list_head *list)
+{
+ struct sort_dimension *sort;
+ int i;
+
+ for (i = 0; i < NUM_AVAIL_SORTS; i++) {
+ if (!strcmp(avail_sorts[i]->name, tok)) {
+ sort = malloc(sizeof(*sort));
+ if (!sort)
+ die("malloc");
+ memcpy(sort, avail_sorts[i], sizeof(*sort));
+ list_add_tail(&sort->list, list);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int setup_sorting(struct list_head *sort_list, const char *arg)
+{
+ char *tok;
+ char *str = strdup(arg);
+
+ if (!str)
+ die("strdup");
+
+ while (true) {
+ tok = strsep(&str, ",");
+ if (!tok)
+ break;
+ if (sort_dimension__add(tok, sort_list) < 0) {
+ error("Unknown --sort key: '%s'", tok);
+ return -1;
+ }
+ }
+
+ free(str);
+ return 0;
+}
+
+static int parse_sort_opt(const struct option *opt __used,
+ const char *arg, int unset __used)
+{
+ if (!arg)
+ return -1;
+
+ if (caller_flag > alloc_flag)
+ return setup_sorting(&caller_sort, arg);
+ else
+ return setup_sorting(&alloc_sort, arg);
+
+ return 0;
+}
+
+static int parse_caller_opt(const struct option *opt __used,
+ const char *arg __used, int unset __used)
+{
+ caller_flag = (alloc_flag + 1);
+ return 0;
+}
+
+static int parse_alloc_opt(const struct option *opt __used,
+ const char *arg __used, int unset __used)
+{
+ alloc_flag = (caller_flag + 1);
+ return 0;
+}
+
+static int parse_line_opt(const struct option *opt __used,
+ const char *arg, int unset __used)
+{
+ int lines;
+
+ if (!arg)
+ return -1;
+
+ lines = strtoul(arg, NULL, 10);
+
+ if (caller_flag > alloc_flag)
+ caller_lines = lines;
+ else
+ alloc_lines = lines;
+
+ return 0;
+}
+
+static const struct option kmem_options[] = {
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
+ "show per-callsite statistics",
+ parse_caller_opt),
+ OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
+ "show per-allocation statistics",
+ parse_alloc_opt),
+ OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
+ "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
+ parse_sort_opt),
+ OPT_CALLBACK('l', "line", NULL, "num",
+ "show n lines",
+ parse_line_opt),
+ OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
+ OPT_END()
+};
+
+static const char *record_args[] = {
+ "record",
+ "-a",
+ "-R",
+ "-f",
+ "-c", "1",
+ "-e", "kmem:kmalloc",
+ "-e", "kmem:kmalloc_node",
+ "-e", "kmem:kfree",
+ "-e", "kmem:kmem_cache_alloc",
+ "-e", "kmem:kmem_cache_alloc_node",
+ "-e", "kmem:kmem_cache_free",
+};
+
+static int __cmd_record(int argc, const char **argv)
+{
+ unsigned int rec_argc, i, j;
+ const char **rec_argv;
+
+ rec_argc = ARRAY_SIZE(record_args) + argc - 1;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+
+ if (rec_argv == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(record_args); i++)
+ rec_argv[i] = strdup(record_args[i]);
+
+ for (j = 1; j < (unsigned int)argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ return cmd_record(i, rec_argv, NULL);
+}
+
+int cmd_kmem(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
+
+ if (!argc)
+ usage_with_options(kmem_usage, kmem_options);
+
+ symbol__init();
+
+ if (!strncmp(argv[0], "rec", 3)) {
+ return __cmd_record(argc, argv);
+ } else if (!strcmp(argv[0], "stat")) {
+ setup_cpunode_map();
+
+ if (list_empty(&caller_sort))
+ setup_sorting(&caller_sort, default_sort_order);
+ if (list_empty(&alloc_sort))
+ setup_sorting(&alloc_sort, default_sort_order);
+
+ return __cmd_kmem();
+ } else
+ usage_with_options(kmem_usage, kmem_options);
+
+ return 0;
+}
+
diff --git a/smartt-perf/builtin-kvm.c b/smartt-perf/builtin-kvm.c
new file mode 100644
index 0000000..34d1e85
--- /dev/null
+++ b/smartt-perf/builtin-kvm.c
@@ -0,0 +1,144 @@
+#include "builtin.h"
+#include "perf.h"
+
+#include "util/util.h"
+#include "util/cache.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/header.h"
+#include "util/session.h"
+
+#include "util/parse-options.h"
+#include "util/trace-event.h"
+
+#include "util/debug.h"
+
+#include <sys/prctl.h>
+
+#include <semaphore.h>
+#include <pthread.h>
+#include <math.h>
+
+static const char *file_name;
+static char name_buffer[256];
+
+bool perf_host = 1;
+bool perf_guest;
+
+static const char * const kvm_usage[] = {
+ "perf kvm [<options>] {top|record|report|diff|buildid-list}",
+ NULL
+};
+
+static const struct option kvm_options[] = {
+ OPT_STRING('i', "input", &file_name, "file",
+ "Input file name"),
+ OPT_STRING('o', "output", &file_name, "file",
+ "Output file name"),
+ OPT_BOOLEAN(0, "guest", &perf_guest,
+ "Collect guest os data"),
+ OPT_BOOLEAN(0, "host", &perf_host,
+ "Collect guest os data"),
+ OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
+ "guest mount directory under which every guest os"
+ " instance has a subdir"),
+ OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name,
+ "file", "file saving guest os vmlinux"),
+ OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms,
+ "file", "file saving guest os /proc/kallsyms"),
+ OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
+ "file", "file saving guest os /proc/modules"),
+ OPT_END()
+};
+
+static int __cmd_record(int argc, const char **argv)
+{
+ int rec_argc, i = 0, j;
+ const char **rec_argv;
+
+ rec_argc = argc + 2;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+ rec_argv[i++] = strdup("record");
+ rec_argv[i++] = strdup("-o");
+ rec_argv[i++] = strdup(file_name);
+ for (j = 1; j < argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ BUG_ON(i != rec_argc);
+
+ return cmd_record(i, rec_argv, NULL);
+}
+
+static int __cmd_report(int argc, const char **argv)
+{
+ int rec_argc, i = 0, j;
+ const char **rec_argv;
+
+ rec_argc = argc + 2;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+ rec_argv[i++] = strdup("report");
+ rec_argv[i++] = strdup("-i");
+ rec_argv[i++] = strdup(file_name);
+ for (j = 1; j < argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ BUG_ON(i != rec_argc);
+
+ return cmd_report(i, rec_argv, NULL);
+}
+
+static int __cmd_buildid_list(int argc, const char **argv)
+{
+ int rec_argc, i = 0, j;
+ const char **rec_argv;
+
+ rec_argc = argc + 2;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+ rec_argv[i++] = strdup("buildid-list");
+ rec_argv[i++] = strdup("-i");
+ rec_argv[i++] = strdup(file_name);
+ for (j = 1; j < argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ BUG_ON(i != rec_argc);
+
+ return cmd_buildid_list(i, rec_argv, NULL);
+}
+
+int cmd_kvm(int argc, const char **argv, const char *prefix __used)
+{
+ perf_host = perf_guest = 0;
+
+ argc = parse_options(argc, argv, kvm_options, kvm_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (!argc)
+ usage_with_options(kvm_usage, kvm_options);
+
+ if (!perf_host)
+ perf_guest = 1;
+
+ if (!file_name) {
+ if (perf_host && !perf_guest)
+ sprintf(name_buffer, "perf.data.host");
+ else if (!perf_host && perf_guest)
+ sprintf(name_buffer, "perf.data.guest");
+ else
+ sprintf(name_buffer, "perf.data.kvm");
+ file_name = name_buffer;
+ }
+
+ if (!strncmp(argv[0], "rec", 3))
+ return __cmd_record(argc, argv);
+ else if (!strncmp(argv[0], "rep", 3))
+ return __cmd_report(argc, argv);
+ else if (!strncmp(argv[0], "diff", 4))
+ return cmd_diff(argc, argv, NULL);
+ else if (!strncmp(argv[0], "top", 3))
+ return cmd_top(argc, argv, NULL);
+ else if (!strncmp(argv[0], "buildid-list", 12))
+ return __cmd_buildid_list(argc, argv);
+ else
+ usage_with_options(kvm_usage, kvm_options);
+
+ return 0;
+}
diff --git a/smartt-perf/builtin-list.c b/smartt-perf/builtin-list.c
new file mode 100644
index 0000000..d88c696
--- /dev/null
+++ b/smartt-perf/builtin-list.c
@@ -0,0 +1,21 @@
+/*
+ * builtin-list.c
+ *
+ * Builtin list command: list all event types
+ *
+ * Copyright (C) 2009, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2009, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
+ */
+#include "builtin.h"
+
+#include "perf.h"
+
+#include "util/parse-events.h"
+#include "util/cache.h"
+
+int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)
+{
+ setup_pager();
+ print_events();
+ return 0;
+}
diff --git a/smartt-perf/builtin-lock.c b/smartt-perf/builtin-lock.c
new file mode 100644
index 0000000..2b36def
--- /dev/null
+++ b/smartt-perf/builtin-lock.c
@@ -0,0 +1,1004 @@
+#include "builtin.h"
+#include "perf.h"
+
+#include "util/util.h"
+#include "util/cache.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/header.h"
+
+#include "util/parse-options.h"
+#include "util/trace-event.h"
+
+#include "util/debug.h"
+#include "util/session.h"
+
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <math.h>
+#include <limits.h>
+
+#include <linux/list.h>
+#include <linux/hash.h>
+
+static struct perf_session *session;
+
+/* based on kernel/lockdep.c */
+#define LOCKHASH_BITS 12
+#define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)
+
+static struct list_head lockhash_table[LOCKHASH_SIZE];
+
+#define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)
+#define lockhashentry(key) (lockhash_table + __lockhashfn((key)))
+
+struct lock_stat {
+ struct list_head hash_entry;
+ struct rb_node rb; /* used for sorting */
+
+ /*
+ * FIXME: raw_field_value() returns unsigned long long,
+ * so address of lockdep_map should be dealed as 64bit.
+ * Is there more better solution?
+ */
+ void *addr; /* address of lockdep_map, used as ID */
+ char *name; /* for strcpy(), we cannot use const */
+
+ unsigned int nr_acquire;
+ unsigned int nr_acquired;
+ unsigned int nr_contended;
+ unsigned int nr_release;
+
+ unsigned int nr_readlock;
+ unsigned int nr_trylock;
+ /* these times are in nano sec. */
+ u64 wait_time_total;
+ u64 wait_time_min;
+ u64 wait_time_max;
+
+ int discard; /* flag of blacklist */
+};
+
+/*
+ * States of lock_seq_stat
+ *
+ * UNINITIALIZED is required for detecting first event of acquire.
+ * As the nature of lock events, there is no guarantee
+ * that the first event for the locks are acquire,
+ * it can be acquired, contended or release.
+ */
+#define SEQ_STATE_UNINITIALIZED 0 /* initial state */
+#define SEQ_STATE_RELEASED 1
+#define SEQ_STATE_ACQUIRING 2
+#define SEQ_STATE_ACQUIRED 3
+#define SEQ_STATE_READ_ACQUIRED 4
+#define SEQ_STATE_CONTENDED 5
+
+/*
+ * MAX_LOCK_DEPTH
+ * Imported from include/linux/sched.h.
+ * Should this be synchronized?
+ */
+#define MAX_LOCK_DEPTH 48
+
+/*
+ * struct lock_seq_stat:
+ * Place to put on state of one lock sequence
+ * 1) acquire -> acquired -> release
+ * 2) acquire -> contended -> acquired -> release
+ * 3) acquire (with read or try) -> release
+ * 4) Are there other patterns?
+ */
+struct lock_seq_stat {
+ struct list_head list;
+ int state;
+ u64 prev_event_time;
+ void *addr;
+
+ int read_count;
+};
+
+struct thread_stat {
+ struct rb_node rb;
+
+ u32 tid;
+ struct list_head seq_list;
+};
+
+static struct rb_root thread_stats;
+
+static struct thread_stat *thread_stat_find(u32 tid)
+{
+ struct rb_node *node;
+ struct thread_stat *st;
+
+ node = thread_stats.rb_node;
+ while (node) {
+ st = container_of(node, struct thread_stat, rb);
+ if (st->tid == tid)
+ return st;
+ else if (tid < st->tid)
+ node = node->rb_left;
+ else
+ node = node->rb_right;
+ }
+
+ return NULL;
+}
+
+static void thread_stat_insert(struct thread_stat *new)
+{
+ struct rb_node **rb = &thread_stats.rb_node;
+ struct rb_node *parent = NULL;
+ struct thread_stat *p;
+
+ while (*rb) {
+ p = container_of(*rb, struct thread_stat, rb);
+ parent = *rb;
+
+ if (new->tid < p->tid)
+ rb = &(*rb)->rb_left;
+ else if (new->tid > p->tid)
+ rb = &(*rb)->rb_right;
+ else
+ BUG_ON("inserting invalid thread_stat\n");
+ }
+
+ rb_link_node(&new->rb, parent, rb);
+ rb_insert_color(&new->rb, &thread_stats);
+}
+
+static struct thread_stat *thread_stat_findnew_after_first(u32 tid)
+{
+ struct thread_stat *st;
+
+ st = thread_stat_find(tid);
+ if (st)
+ return st;
+
+ st = zalloc(sizeof(struct thread_stat));
+ if (!st)
+ die("memory allocation failed\n");
+
+ st->tid = tid;
+ INIT_LIST_HEAD(&st->seq_list);
+
+ thread_stat_insert(st);
+
+ return st;
+}
+
+static struct thread_stat *thread_stat_findnew_first(u32 tid);
+static struct thread_stat *(*thread_stat_findnew)(u32 tid) =
+ thread_stat_findnew_first;
+
+static struct thread_stat *thread_stat_findnew_first(u32 tid)
+{
+ struct thread_stat *st;
+
+ st = zalloc(sizeof(struct thread_stat));
+ if (!st)
+ die("memory allocation failed\n");
+ st->tid = tid;
+ INIT_LIST_HEAD(&st->seq_list);
+
+ rb_link_node(&st->rb, NULL, &thread_stats.rb_node);
+ rb_insert_color(&st->rb, &thread_stats);
+
+ thread_stat_findnew = thread_stat_findnew_after_first;
+ return st;
+}
+
+/* build simple key function one is bigger than two */
+#define SINGLE_KEY(member) \
+ static int lock_stat_key_ ## member(struct lock_stat *one, \
+ struct lock_stat *two) \
+ { \
+ return one->member > two->member; \
+ }
+
+SINGLE_KEY(nr_acquired)
+SINGLE_KEY(nr_contended)
+SINGLE_KEY(wait_time_total)
+SINGLE_KEY(wait_time_min)
+SINGLE_KEY(wait_time_max)
+
+struct lock_key {
+ /*
+ * name: the value for specify by user
+ * this should be simpler than raw name of member
+ * e.g. nr_acquired -> acquired, wait_time_total -> wait_total
+ */
+ const char *name;
+ int (*key)(struct lock_stat*, struct lock_stat*);
+};
+
+static const char *sort_key = "acquired";
+
+static int (*compare)(struct lock_stat *, struct lock_stat *);
+
+static struct rb_root result; /* place to store sorted data */
+
+#define DEF_KEY_LOCK(name, fn_suffix) \
+ { #name, lock_stat_key_ ## fn_suffix }
+struct lock_key keys[] = {
+ DEF_KEY_LOCK(acquired, nr_acquired),
+ DEF_KEY_LOCK(contended, nr_contended),
+ DEF_KEY_LOCK(wait_total, wait_time_total),
+ DEF_KEY_LOCK(wait_min, wait_time_min),
+ DEF_KEY_LOCK(wait_max, wait_time_max),
+
+ /* extra comparisons much complicated should be here */
+
+ { NULL, NULL }
+};
+
+static void select_key(void)
+{
+ int i;
+
+ for (i = 0; keys[i].name; i++) {
+ if (!strcmp(keys[i].name, sort_key)) {
+ compare = keys[i].key;
+ return;
+ }
+ }
+
+ die("Unknown compare key:%s\n", sort_key);
+}
+
+static void insert_to_result(struct lock_stat *st,
+ int (*bigger)(struct lock_stat *, struct lock_stat *))
+{
+ struct rb_node **rb = &result.rb_node;
+ struct rb_node *parent = NULL;
+ struct lock_stat *p;
+
+ while (*rb) {
+ p = container_of(*rb, struct lock_stat, rb);
+ parent = *rb;
+
+ if (bigger(st, p))
+ rb = &(*rb)->rb_left;
+ else
+ rb = &(*rb)->rb_right;
+ }
+
+ rb_link_node(&st->rb, parent, rb);
+ rb_insert_color(&st->rb, &result);
+}
+
+/* returns left most element of result, and erase it */
+static struct lock_stat *pop_from_result(void)
+{
+ struct rb_node *node = result.rb_node;
+
+ if (!node)
+ return NULL;
+
+ while (node->rb_left)
+ node = node->rb_left;
+
+ rb_erase(node, &result);
+ return container_of(node, struct lock_stat, rb);
+}
+
+static struct lock_stat *lock_stat_findnew(void *addr, const char *name)
+{
+ struct list_head *entry = lockhashentry(addr);
+ struct lock_stat *ret, *new;
+
+ list_for_each_entry(ret, entry, hash_entry) {
+ if (ret->addr == addr)
+ return ret;
+ }
+
+ new = zalloc(sizeof(struct lock_stat));
+ if (!new)
+ goto alloc_failed;
+
+ new->addr = addr;
+ new->name = zalloc(sizeof(char) * strlen(name) + 1);
+ if (!new->name)
+ goto alloc_failed;
+ strcpy(new->name, name);
+
+ new->wait_time_min = ULLONG_MAX;
+
+ list_add(&new->hash_entry, entry);
+ return new;
+
+alloc_failed:
+ die("memory allocation failed\n");
+}
+
+static char const *input_name = "perf.data";
+
+struct raw_event_sample {
+ u32 size;
+ char data[0];
+};
+
+struct trace_acquire_event {
+ void *addr;
+ const char *name;
+ int flag;
+};
+
+struct trace_acquired_event {
+ void *addr;
+ const char *name;
+};
+
+struct trace_contended_event {
+ void *addr;
+ const char *name;
+};
+
+struct trace_release_event {
+ void *addr;
+ const char *name;
+};
+
+struct trace_lock_handler {
+ void (*acquire_event)(struct trace_acquire_event *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+
+ void (*acquired_event)(struct trace_acquired_event *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+
+ void (*contended_event)(struct trace_contended_event *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+
+ void (*release_event)(struct trace_release_event *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+};
+
+static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr)
+{
+ struct lock_seq_stat *seq;
+
+ list_for_each_entry(seq, &ts->seq_list, list) {
+ if (seq->addr == addr)
+ return seq;
+ }
+
+ seq = zalloc(sizeof(struct lock_seq_stat));
+ if (!seq)
+ die("Not enough memory\n");
+ seq->state = SEQ_STATE_UNINITIALIZED;
+ seq->addr = addr;
+
+ list_add(&seq->list, &ts->seq_list);
+ return seq;
+}
+
+enum broken_state {
+ BROKEN_ACQUIRE,
+ BROKEN_ACQUIRED,
+ BROKEN_CONTENDED,
+ BROKEN_RELEASE,
+ BROKEN_MAX,
+};
+
+static int bad_hist[BROKEN_MAX];
+
+enum acquire_flags {
+ TRY_LOCK = 1,
+ READ_LOCK = 2,
+};
+
+static void
+report_lock_acquire_event(struct trace_acquire_event *acquire_event,
+ struct event *__event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct lock_stat *ls;
+ struct thread_stat *ts;
+ struct lock_seq_stat *seq;
+
+ ls = lock_stat_findnew(acquire_event->addr, acquire_event->name);
+ if (ls->discard)
+ return;
+
+ ts = thread_stat_findnew(thread->pid);
+ seq = get_seq(ts, acquire_event->addr);
+
+ switch (seq->state) {
+ case SEQ_STATE_UNINITIALIZED:
+ case SEQ_STATE_RELEASED:
+ if (!acquire_event->flag) {
+ seq->state = SEQ_STATE_ACQUIRING;
+ } else {
+ if (acquire_event->flag & TRY_LOCK)
+ ls->nr_trylock++;
+ if (acquire_event->flag & READ_LOCK)
+ ls->nr_readlock++;
+ seq->state = SEQ_STATE_READ_ACQUIRED;
+ seq->read_count = 1;
+ ls->nr_acquired++;
+ }
+ break;
+ case SEQ_STATE_READ_ACQUIRED:
+ if (acquire_event->flag & READ_LOCK) {
+ seq->read_count++;
+ ls->nr_acquired++;
+ goto end;
+ } else {
+ goto broken;
+ }
+ break;
+ case SEQ_STATE_ACQUIRED:
+ case SEQ_STATE_ACQUIRING:
+ case SEQ_STATE_CONTENDED:
+broken:
+ /* broken lock sequence, discard it */
+ ls->discard = 1;
+ bad_hist[BROKEN_ACQUIRE]++;
+ list_del(&seq->list);
+ free(seq);
+ goto end;
+ break;
+ default:
+ BUG_ON("Unknown state of lock sequence found!\n");
+ break;
+ }
+
+ ls->nr_acquire++;
+ seq->prev_event_time = timestamp;
+end:
+ return;
+}
+
+static void
+report_lock_acquired_event(struct trace_acquired_event *acquired_event,
+ struct event *__event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct lock_stat *ls;
+ struct thread_stat *ts;
+ struct lock_seq_stat *seq;
+ u64 contended_term;
+
+ ls = lock_stat_findnew(acquired_event->addr, acquired_event->name);
+ if (ls->discard)
+ return;
+
+ ts = thread_stat_findnew(thread->pid);
+ seq = get_seq(ts, acquired_event->addr);
+
+ switch (seq->state) {
+ case SEQ_STATE_UNINITIALIZED:
+ /* orphan event, do nothing */
+ return;
+ case SEQ_STATE_ACQUIRING:
+ break;
+ case SEQ_STATE_CONTENDED:
+ contended_term = timestamp - seq->prev_event_time;
+ ls->wait_time_total += contended_term;
+ if (contended_term < ls->wait_time_min)
+ ls->wait_time_min = contended_term;
+ if (ls->wait_time_max < contended_term)
+ ls->wait_time_max = contended_term;
+ break;
+ case SEQ_STATE_RELEASED:
+ case SEQ_STATE_ACQUIRED:
+ case SEQ_STATE_READ_ACQUIRED:
+ /* broken lock sequence, discard it */
+ ls->discard = 1;
+ bad_hist[BROKEN_ACQUIRED]++;
+ list_del(&seq->list);
+ free(seq);
+ goto end;
+ break;
+
+ default:
+ BUG_ON("Unknown state of lock sequence found!\n");
+ break;
+ }
+
+ seq->state = SEQ_STATE_ACQUIRED;
+ ls->nr_acquired++;
+ seq->prev_event_time = timestamp;
+end:
+ return;
+}
+
+static void
+report_lock_contended_event(struct trace_contended_event *contended_event,
+ struct event *__event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct lock_stat *ls;
+ struct thread_stat *ts;
+ struct lock_seq_stat *seq;
+
+ ls = lock_stat_findnew(contended_event->addr, contended_event->name);
+ if (ls->discard)
+ return;
+
+ ts = thread_stat_findnew(thread->pid);
+ seq = get_seq(ts, contended_event->addr);
+
+ switch (seq->state) {
+ case SEQ_STATE_UNINITIALIZED:
+ /* orphan event, do nothing */
+ return;
+ case SEQ_STATE_ACQUIRING:
+ break;
+ case SEQ_STATE_RELEASED:
+ case SEQ_STATE_ACQUIRED:
+ case SEQ_STATE_READ_ACQUIRED:
+ case SEQ_STATE_CONTENDED:
+ /* broken lock sequence, discard it */
+ ls->discard = 1;
+ bad_hist[BROKEN_CONTENDED]++;
+ list_del(&seq->list);
+ free(seq);
+ goto end;
+ break;
+ default:
+ BUG_ON("Unknown state of lock sequence found!\n");
+ break;
+ }
+
+ seq->state = SEQ_STATE_CONTENDED;
+ ls->nr_contended++;
+ seq->prev_event_time = timestamp;
+end:
+ return;
+}
+
+static void
+report_lock_release_event(struct trace_release_event *release_event,
+ struct event *__event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct lock_stat *ls;
+ struct thread_stat *ts;
+ struct lock_seq_stat *seq;
+
+ ls = lock_stat_findnew(release_event->addr, release_event->name);
+ if (ls->discard)
+ return;
+
+ ts = thread_stat_findnew(thread->pid);
+ seq = get_seq(ts, release_event->addr);
+
+ switch (seq->state) {
+ case SEQ_STATE_UNINITIALIZED:
+ goto end;
+ break;
+ case SEQ_STATE_ACQUIRED:
+ break;
+ case SEQ_STATE_READ_ACQUIRED:
+ seq->read_count--;
+ BUG_ON(seq->read_count < 0);
+ if (!seq->read_count) {
+ ls->nr_release++;
+ goto end;
+ }
+ break;
+ case SEQ_STATE_ACQUIRING:
+ case SEQ_STATE_CONTENDED:
+ case SEQ_STATE_RELEASED:
+ /* broken lock sequence, discard it */
+ ls->discard = 1;
+ bad_hist[BROKEN_RELEASE]++;
+ goto free_seq;
+ break;
+ default:
+ BUG_ON("Unknown state of lock sequence found!\n");
+ break;
+ }
+
+ ls->nr_release++;
+free_seq:
+ list_del(&seq->list);
+ free(seq);
+end:
+ return;
+}
+
+/* lock oriented handlers */
+/* TODO: handlers for CPU oriented, thread oriented */
+static struct trace_lock_handler report_lock_ops = {
+ .acquire_event = report_lock_acquire_event,
+ .acquired_event = report_lock_acquired_event,
+ .contended_event = report_lock_contended_event,
+ .release_event = report_lock_release_event,
+};
+
+static struct trace_lock_handler *trace_handler;
+
+static void
+process_lock_acquire_event(void *data,
+ struct event *event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_acquire_event acquire_event;
+ u64 tmp; /* this is required for casting... */
+
+ tmp = raw_field_value(event, "lockdep_addr", data);
+ memcpy(&acquire_event.addr, &tmp, sizeof(void *));
+ acquire_event.name = (char *)raw_field_ptr(event, "name", data);
+ acquire_event.flag = (int)raw_field_value(event, "flag", data);
+
+ if (trace_handler->acquire_event)
+ trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread);
+}
+
+static void
+process_lock_acquired_event(void *data,
+ struct event *event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_acquired_event acquired_event;
+ u64 tmp; /* this is required for casting... */
+
+ tmp = raw_field_value(event, "lockdep_addr", data);
+ memcpy(&acquired_event.addr, &tmp, sizeof(void *));
+ acquired_event.name = (char *)raw_field_ptr(event, "name", data);
+
+ if (trace_handler->acquire_event)
+ trace_handler->acquired_event(&acquired_event, event, cpu, timestamp, thread);
+}
+
+static void
+process_lock_contended_event(void *data,
+ struct event *event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_contended_event contended_event;
+ u64 tmp; /* this is required for casting... */
+
+ tmp = raw_field_value(event, "lockdep_addr", data);
+ memcpy(&contended_event.addr, &tmp, sizeof(void *));
+ contended_event.name = (char *)raw_field_ptr(event, "name", data);
+
+ if (trace_handler->acquire_event)
+ trace_handler->contended_event(&contended_event, event, cpu, timestamp, thread);
+}
+
+static void
+process_lock_release_event(void *data,
+ struct event *event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_release_event release_event;
+ u64 tmp; /* this is required for casting... */
+
+ tmp = raw_field_value(event, "lockdep_addr", data);
+ memcpy(&release_event.addr, &tmp, sizeof(void *));
+ release_event.name = (char *)raw_field_ptr(event, "name", data);
+
+ if (trace_handler->acquire_event)
+ trace_handler->release_event(&release_event, event, cpu, timestamp, thread);
+}
+
+static void
+process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)
+{
+ struct event *event;
+ int type;
+
+ type = trace_parse_common_type(data);
+ event = trace_find_event(type);
+
+ if (!strcmp(event->name, "lock_acquire"))
+ process_lock_acquire_event(data, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "lock_acquired"))
+ process_lock_acquired_event(data, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "lock_contended"))
+ process_lock_contended_event(data, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "lock_release"))
+ process_lock_release_event(data, event, cpu, timestamp, thread);
+}
+
+static void print_bad_events(int bad, int total)
+{
+ /* Output for debug, this have to be removed */
+ int i;
+ const char *name[4] =
+ { "acquire", "acquired", "contended", "release" };
+
+ pr_info("\n=== output for debug===\n\n");
+ pr_info("bad: %d, total: %d\n", bad, total);
+ pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100);
+ pr_info("histogram of events caused bad sequence\n");
+ for (i = 0; i < BROKEN_MAX; i++)
+ pr_info(" %10s: %d\n", name[i], bad_hist[i]);
+}
+
+/* TODO: various way to print, coloring, nano or milli sec */
+static void print_result(void)
+{
+ struct lock_stat *st;
+ char cut_name[20];
+ int bad, total;
+
+ pr_info("%20s ", "Name");
+ pr_info("%10s ", "acquired");
+ pr_info("%10s ", "contended");
+
+ pr_info("%15s ", "total wait (ns)");
+ pr_info("%15s ", "max wait (ns)");
+ pr_info("%15s ", "min wait (ns)");
+
+ pr_info("\n\n");
+
+ bad = total = 0;
+ while ((st = pop_from_result())) {
+ total++;
+ if (st->discard) {
+ bad++;
+ continue;
+ }
+ bzero(cut_name, 20);
+
+ if (strlen(st->name) < 16) {
+ /* output raw name */
+ pr_info("%20s ", st->name);
+ } else {
+ strncpy(cut_name, st->name, 16);
+ cut_name[16] = '.';
+ cut_name[17] = '.';
+ cut_name[18] = '.';
+ cut_name[19] = '\0';
+ /* cut off name for saving output style */
+ pr_info("%20s ", cut_name);
+ }
+
+ pr_info("%10u ", st->nr_acquired);
+ pr_info("%10u ", st->nr_contended);
+
+ pr_info("%15" PRIu64 " ", st->wait_time_total);
+ pr_info("%15" PRIu64 " ", st->wait_time_max);
+ pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ?
+ 0 : st->wait_time_min);
+ pr_info("\n");
+ }
+
+ print_bad_events(bad, total);
+}
+
+static bool info_threads, info_map;
+
+static void dump_threads(void)
+{
+ struct thread_stat *st;
+ struct rb_node *node;
+ struct thread *t;
+
+ pr_info("%10s: comm\n", "Thread ID");
+
+ node = rb_first(&thread_stats);
+ while (node) {
+ st = container_of(node, struct thread_stat, rb);
+ t = perf_session__findnew(session, st->tid);
+ pr_info("%10d: %s\n", st->tid, t->comm);
+ node = rb_next(node);
+ };
+}
+
+static void dump_map(void)
+{
+ unsigned int i;
+ struct lock_stat *st;
+
+ pr_info("Address of instance: name of class\n");
+ for (i = 0; i < LOCKHASH_SIZE; i++) {
+ list_for_each_entry(st, &lockhash_table[i], hash_entry) {
+ pr_info(" %p: %s\n", st->addr, st->name);
+ }
+ }
+}
+
+static void dump_info(void)
+{
+ if (info_threads)
+ dump_threads();
+ else if (info_map)
+ dump_map();
+ else
+ die("Unknown type of information\n");
+}
+
+static int process_sample_event(event_t *self, struct sample_data *sample,
+ struct perf_session *s)
+{
+ struct thread *thread = perf_session__findnew(s, sample->tid);
+
+ if (thread == NULL) {
+ pr_debug("problem processing %d event, skipping it.\n",
+ self->header.type);
+ return -1;
+ }
+
+ process_raw_event(sample->raw_data, sample->cpu, sample->time, thread);
+
+ return 0;
+}
+
+static struct perf_event_ops eops = {
+ .sample = process_sample_event,
+ .comm = event__process_comm,
+ .ordered_samples = true,
+};
+
+static int read_events(void)
+{
+ session = perf_session__new(input_name, O_RDONLY, 0, false, &eops);
+ if (!session)
+ die("Initializing perf session failed\n");
+
+ return perf_session__process_events(session, &eops);
+}
+
+static void sort_result(void)
+{
+ unsigned int i;
+ struct lock_stat *st;
+
+ for (i = 0; i < LOCKHASH_SIZE; i++) {
+ list_for_each_entry(st, &lockhash_table[i], hash_entry) {
+ insert_to_result(st, compare);
+ }
+ }
+}
+
+static void __cmd_report(void)
+{
+ setup_pager();
+ select_key();
+ read_events();
+ sort_result();
+ print_result();
+}
+
+static const char * const report_usage[] = {
+ "perf lock report [<options>]",
+ NULL
+};
+
+static const struct option report_options[] = {
+ OPT_STRING('k', "key", &sort_key, "acquired",
+ "key for sorting"),
+ /* TODO: type */
+ OPT_END()
+};
+
+static const char * const info_usage[] = {
+ "perf lock info [<options>]",
+ NULL
+};
+
+static const struct option info_options[] = {
+ OPT_BOOLEAN('t', "threads", &info_threads,
+ "dump thread list in perf.data"),
+ OPT_BOOLEAN('m', "map", &info_map,
+ "map of lock instances (name:address table)"),
+ OPT_END()
+};
+
+static const char * const lock_usage[] = {
+ "perf lock [<options>] {record|trace|report}",
+ NULL
+};
+
+static const struct option lock_options[] = {
+ OPT_STRING('i', "input", &input_name, "file", "input file name"),
+ OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),
+ OPT_END()
+};
+
+static const char *record_args[] = {
+ "record",
+ "-R",
+ "-f",
+ "-m", "1024",
+ "-c", "1",
+ "-e", "lock:lock_acquire:r",
+ "-e", "lock:lock_acquired:r",
+ "-e", "lock:lock_contended:r",
+ "-e", "lock:lock_release:r",
+};
+
+static int __cmd_record(int argc, const char **argv)
+{
+ unsigned int rec_argc, i, j;
+ const char **rec_argv;
+
+ rec_argc = ARRAY_SIZE(record_args) + argc - 1;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+
+ if (rec_argv == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(record_args); i++)
+ rec_argv[i] = strdup(record_args[i]);
+
+ for (j = 1; j < (unsigned int)argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ BUG_ON(i != rec_argc);
+
+ return cmd_record(i, rec_argv, NULL);
+}
+
+int cmd_lock(int argc, const char **argv, const char *prefix __used)
+{
+ unsigned int i;
+
+ symbol__init();
+ for (i = 0; i < LOCKHASH_SIZE; i++)
+ INIT_LIST_HEAD(lockhash_table + i);
+
+ argc = parse_options(argc, argv, lock_options, lock_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (!argc)
+ usage_with_options(lock_usage, lock_options);
+
+ if (!strncmp(argv[0], "rec", 3)) {
+ return __cmd_record(argc, argv);
+ } else if (!strncmp(argv[0], "report", 6)) {
+ trace_handler = &report_lock_ops;
+ if (argc) {
+ argc = parse_options(argc, argv,
+ report_options, report_usage, 0);
+ if (argc)
+ usage_with_options(report_usage, report_options);
+ }
+ __cmd_report();
+ } else if (!strcmp(argv[0], "script")) {
+ /* Aliased to 'perf script' */
+ return cmd_script(argc, argv, prefix);
+ } else if (!strcmp(argv[0], "info")) {
+ if (argc) {
+ argc = parse_options(argc, argv,
+ info_options, info_usage, 0);
+ if (argc)
+ usage_with_options(info_usage, info_options);
+ }
+ /* recycling report_lock_ops */
+ trace_handler = &report_lock_ops;
+ setup_pager();
+ read_events();
+ dump_info();
+ } else {
+ usage_with_options(lock_usage, lock_options);
+ }
+
+ return 0;
+}
diff --git a/smartt-perf/builtin-probe.c b/smartt-perf/builtin-probe.c
new file mode 100644
index 0000000..add163c
--- /dev/null
+++ b/smartt-perf/builtin-probe.c
@@ -0,0 +1,330 @@
+/*
+ * builtin-probe.c
+ *
+ * Builtin probe command: Set up probe events by C expression
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#define _GNU_SOURCE
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#undef _GNU_SOURCE
+#include "perf.h"
+#include "builtin.h"
+#include "util/util.h"
+#include "util/strlist.h"
+#include "util/symbol.h"
+#include "util/debug.h"
+#include "util/debugfs.h"
+#include "util/parse-options.h"
+#include "util/probe-finder.h"
+#include "util/probe-event.h"
+
+#define MAX_PATH_LEN 256
+
+/* Session management structure */
+static struct {
+ bool list_events;
+ bool force_add;
+ bool show_lines;
+ bool show_vars;
+ bool show_ext_vars;
+ bool mod_events;
+ int nevents;
+ struct perf_probe_event events[MAX_PROBES];
+ struct strlist *dellist;
+ struct line_range line_range;
+ const char *target_module;
+ int max_probe_points;
+} params;
+
+/* Parse an event definition. Note that any error must die. */
+static int parse_probe_event(const char *str)
+{
+ struct perf_probe_event *pev = &params.events[params.nevents];
+ int ret;
+
+ pr_debug("probe-definition(%d): %s\n", params.nevents, str);
+ if (++params.nevents == MAX_PROBES) {
+ pr_err("Too many probes (> %d) were specified.", MAX_PROBES);
+ return -1;
+ }
+
+ /* Parse a perf-probe command into event */
+ ret = parse_perf_probe_command(str, pev);
+ pr_debug("%d arguments\n", pev->nargs);
+
+ return ret;
+}
+
+static int parse_probe_event_argv(int argc, const char **argv)
+{
+ int i, len, ret;
+ char *buf;
+
+ /* Bind up rest arguments */
+ len = 0;
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+ buf = zalloc(len + 1);
+ if (buf == NULL)
+ return -ENOMEM;
+ len = 0;
+ for (i = 0; i < argc; i++)
+ len += sprintf(&buf[len], "%s ", argv[i]);
+ params.mod_events = true;
+ ret = parse_probe_event(buf);
+ free(buf);
+ return ret;
+}
+
+static int opt_add_probe_event(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ if (str) {
+ params.mod_events = true;
+ return parse_probe_event(str);
+ } else
+ return 0;
+}
+
+static int opt_del_probe_event(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ if (str) {
+ params.mod_events = true;
+ if (!params.dellist)
+ params.dellist = strlist__new(true, NULL);
+ strlist__add(params.dellist, str);
+ }
+ return 0;
+}
+
+#ifdef DWARF_SUPPORT
+static int opt_show_lines(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ int ret = 0;
+
+ if (str)
+ ret = parse_line_range_desc(str, &params.line_range);
+ INIT_LIST_HEAD(&params.line_range.line_list);
+ params.show_lines = true;
+
+ return ret;
+}
+
+static int opt_show_vars(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ struct perf_probe_event *pev = &params.events[params.nevents];
+ int ret;
+
+ if (!str)
+ return 0;
+
+ ret = parse_probe_event(str);
+ if (!ret && pev->nargs != 0) {
+ pr_err(" Error: '--vars' doesn't accept arguments.\n");
+ return -EINVAL;
+ }
+ params.show_vars = true;
+
+ return ret;
+}
+#endif
+
+static const char * const probe_usage[] = {
+ "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
+ "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
+ "perf probe [<options>] --del '[GROUP:]EVENT' ...",
+ "perf probe --list",
+#ifdef DWARF_SUPPORT
+ "perf probe [<options>] --line 'LINEDESC'",
+ "perf probe [<options>] --vars 'PROBEPOINT'",
+#endif
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show parsed arguments, etc)"),
+ OPT_BOOLEAN('l', "list", &params.list_events,
+ "list up current probe events"),
+ OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
+ opt_del_probe_event),
+ OPT_CALLBACK('a', "add", NULL,
+#ifdef DWARF_SUPPORT
+ "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
+ " [[NAME=]ARG ...]",
+#else
+ "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]",
+#endif
+ "probe point definition, where\n"
+ "\t\tGROUP:\tGroup name (optional)\n"
+ "\t\tEVENT:\tEvent name\n"
+ "\t\tFUNC:\tFunction name\n"
+ "\t\tOFF:\tOffset from function entry (in byte)\n"
+ "\t\t%return:\tPut the probe at function return\n"
+#ifdef DWARF_SUPPORT
+ "\t\tSRC:\tSource code path\n"
+ "\t\tRL:\tRelative line number from function entry.\n"
+ "\t\tAL:\tAbsolute line number in file.\n"
+ "\t\tPT:\tLazy expression of line code.\n"
+ "\t\tARG:\tProbe argument (local variable name or\n"
+ "\t\t\tkprobe-tracer argument format.)\n",
+#else
+ "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n",
+#endif
+ opt_add_probe_event),
+ OPT_BOOLEAN('f', "force", &params.force_add, "forcibly add events"
+ " with existing name"),
+#ifdef DWARF_SUPPORT
+ OPT_CALLBACK('L', "line", NULL,
+ "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
+ "Show source code lines.", opt_show_lines),
+ OPT_CALLBACK('V', "vars", NULL,
+ "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
+ "Show accessible variables on PROBEDEF", opt_show_vars),
+ OPT_BOOLEAN('\0', "externs", &params.show_ext_vars,
+ "Show external variables too (with --vars only)"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
+ OPT_STRING('s', "source", &symbol_conf.source_prefix,
+ "directory", "path to kernel source"),
+ OPT_STRING('m', "module", &params.target_module,
+ "modname", "target module name"),
+#endif
+ OPT__DRY_RUN(&probe_event_dry_run),
+ OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
+ "Set how many probe points can be found for a probe."),
+ OPT_END()
+};
+
+int cmd_probe(int argc, const char **argv, const char *prefix __used)
+{
+ int ret;
+
+ argc = parse_options(argc, argv, options, probe_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (argc > 0) {
+ if (strcmp(argv[0], "-") == 0) {
+ pr_warning(" Error: '-' is not supported.\n");
+ usage_with_options(probe_usage, options);
+ }
+ ret = parse_probe_event_argv(argc, argv);
+ if (ret < 0) {
+ pr_err(" Error: Parse Error. (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ if (params.max_probe_points == 0)
+ params.max_probe_points = MAX_PROBES;
+
+ if ((!params.nevents && !params.dellist && !params.list_events &&
+ !params.show_lines))
+ usage_with_options(probe_usage, options);
+
+ /*
+ * Only consider the user's kernel image path if given.
+ */
+ symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
+
+ if (params.list_events) {
+ if (params.mod_events) {
+ pr_err(" Error: Don't use --list with --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ if (params.show_lines) {
+ pr_err(" Error: Don't use --list with --line.\n");
+ usage_with_options(probe_usage, options);
+ }
+ if (params.show_vars) {
+ pr_err(" Error: Don't use --list with --vars.\n");
+ usage_with_options(probe_usage, options);
+ }
+ ret = show_perf_probe_events();
+ if (ret < 0)
+ pr_err(" Error: Failed to show event list. (%d)\n",
+ ret);
+ return ret;
+ }
+
+#ifdef DWARF_SUPPORT
+ if (params.show_lines) {
+ if (params.mod_events) {
+ pr_err(" Error: Don't use --line with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ if (params.show_vars) {
+ pr_err(" Error: Don't use --line with --vars.\n");
+ usage_with_options(probe_usage, options);
+ }
+
+ ret = show_line_range(&params.line_range, params.target_module);
+ if (ret < 0)
+ pr_err(" Error: Failed to show lines. (%d)\n", ret);
+ return ret;
+ }
+ if (params.show_vars) {
+ if (params.mod_events) {
+ pr_err(" Error: Don't use --vars with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ ret = show_available_vars(params.events, params.nevents,
+ params.max_probe_points,
+ params.target_module,
+ params.show_ext_vars);
+ if (ret < 0)
+ pr_err(" Error: Failed to show vars. (%d)\n", ret);
+ return ret;
+ }
+#endif
+
+ if (params.dellist) {
+ ret = del_perf_probe_events(params.dellist);
+ strlist__delete(params.dellist);
+ if (ret < 0) {
+ pr_err(" Error: Failed to delete events. (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ if (params.nevents) {
+ ret = add_perf_probe_events(params.events, params.nevents,
+ params.max_probe_points,
+ params.target_module,
+ params.force_add);
+ if (ret < 0) {
+ pr_err(" Error: Failed to add events. (%d)\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
diff --git a/smartt-perf/builtin-record.c b/smartt-perf/builtin-record.c
new file mode 100644
index 0000000..60cac6f
--- /dev/null
+++ b/smartt-perf/builtin-record.c
@@ -0,0 +1,975 @@
+/*
+ * builtin-record.c
+ *
+ * Builtin record command: Record the profile of a workload
+ * (or a CPU, or a PID) into the perf.data output file - for
+ * later analysis via perf report.
+ */
+#define _FILE_OFFSET_BITS 64
+
+#include "builtin.h"
+
+#include "perf.h"
+
+#include "util/build-id.h"
+#include "util/util.h"
+#include "util/parse-options.h"
+#include "util/parse-events.h"
+
+#include "util/header.h"
+#include "util/event.h"
+#include "util/evsel.h"
+#include "util/debug.h"
+#include "util/session.h"
+#include "util/symbol.h"
+#include "util/cpumap.h"
+
+#include <unistd.h>
+#include <sched.h>
+#include <sys/mman.h>
+
+#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
+
+enum write_mode_t {
+ WRITE_FORCE,
+ WRITE_APPEND
+};
+
+static u64 user_interval = ULLONG_MAX;
+static u64 default_interval = 0;
+static u64 sample_type;
+
+static struct cpu_map *cpus;
+static unsigned int page_size;
+static unsigned int mmap_pages = 128;
+static unsigned int user_freq = UINT_MAX;
+static int freq = 1000;
+static int output;
+static int pipe_output = 0;
+static const char *output_name = "perf.data";
+static int group = 0;
+static int realtime_prio = 0;
+static bool nodelay = false;
+static bool raw_samples = false;
+static bool sample_id_all_avail = true;
+static bool system_wide = false;
+static pid_t target_pid = -1;
+static pid_t target_tid = -1;
+static struct thread_map *threads;
+static pid_t child_pid = -1;
+static bool no_inherit = false;
+static enum write_mode_t write_mode = WRITE_FORCE;
+static bool call_graph = false;
+static bool inherit_stat = false;
+static bool no_samples = false;
+static bool sample_address = false;
+static bool sample_time = false;
+static bool no_buildid = false;
+static bool no_buildid_cache = false;
+
+static long samples = 0;
+static u64 bytes_written = 0;
+
+static struct pollfd *event_array;
+
+static int nr_poll = 0;
+static int nr_cpu = 0;
+
+static int file_new = 1;
+static off_t post_processing_offset;
+
+static struct perf_session *session;
+static const char *cpu_list;
+
+struct mmap_data {
+ void *base;
+ unsigned int mask;
+ unsigned int prev;
+};
+
+static struct mmap_data mmap_array[MAX_NR_CPUS];
+
+static unsigned long mmap_read_head(struct mmap_data *md)
+{
+ struct perf_event_mmap_page *pc = md->base;
+ long head;
+
+ head = pc->data_head;
+ rmb();
+
+ return head;
+}
+
+static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
+{
+ struct perf_event_mmap_page *pc = md->base;
+
+ /*
+ * ensure all reads are done before we write the tail out.
+ */
+ /* mb(); */
+ pc->data_tail = tail;
+}
+
+static void advance_output(size_t size)
+{
+ bytes_written += size;
+}
+
+static void write_output(void *buf, size_t size)
+{
+ while (size) {
+ int ret = write(output, buf, size);
+
+ if (ret < 0)
+ die("failed to write");
+
+ size -= ret;
+ buf += ret;
+
+ bytes_written += ret;
+ }
+}
+
+static int process_synthesized_event(event_t *event,
+ struct sample_data *sample __used,
+ struct perf_session *self __used)
+{
+ write_output(event, event->header.size);
+ return 0;
+}
+
+static void mmap_read(struct mmap_data *md)
+{
+ unsigned int head = mmap_read_head(md);
+ unsigned int old = md->prev;
+ unsigned char *data = md->base + page_size;
+ unsigned long size;
+ void *buf;
+ int diff;
+
+ /*
+ * If we're further behind than half the buffer, there's a chance
+ * the writer will bite our tail and mess up the samples under us.
+ *
+ * If we somehow ended up ahead of the head, we got messed up.
+ *
+ * In either case, truncate and restart at head.
+ */
+ diff = head - old;
+ if (diff < 0) {
+ fprintf(stderr, "WARNING: failed to keep up with mmap data\n");
+ /*
+ * head points to a known good entry, start there.
+ */
+ old = head;
+ }
+
+ if (old != head)
+ samples++;
+
+ size = head - old;
+
+ if ((old & md->mask) + size != (head & md->mask)) {
+ buf = &data[old & md->mask];
+ size = md->mask + 1 - (old & md->mask);
+ old += size;
+
+ write_output(buf, size);
+ }
+
+ buf = &data[old & md->mask];
+ size = head - old;
+ old += size;
+
+ write_output(buf, size);
+
+ md->prev = old;
+ mmap_write_tail(md, old);
+}
+
+static volatile int done = 0;
+static volatile int signr = -1;
+
+static void sig_handler(int sig)
+{
+ done = 1;
+ signr = sig;
+}
+
+static void sig_atexit(void)
+{
+ if (child_pid > 0)
+ kill(child_pid, SIGTERM);
+
+ if (signr == -1 || signr == SIGUSR1)
+ return;
+
+ signal(signr, SIG_DFL);
+ kill(getpid(), signr);
+}
+
+static int group_fd;
+
+static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
+{
+ struct perf_header_attr *h_attr;
+
+ if (nr < session->header.attrs) {
+ h_attr = session->header.attr[nr];
+ } else {
+ h_attr = perf_header_attr__new(a);
+ if (h_attr != NULL)
+ if (perf_header__add_attr(&session->header, h_attr) < 0) {
+ perf_header_attr__delete(h_attr);
+ h_attr = NULL;
+ }
+ }
+
+ return h_attr;
+}
+
+static void create_counter(struct perf_evsel *evsel, int cpu)
+{
+ char *filter = evsel->filter;
+ struct perf_event_attr *attr = &evsel->attr;
+ struct perf_header_attr *h_attr;
+ int track = !evsel->idx; /* only the first counter needs these */
+ int thread_index;
+ int ret;
+ struct {
+ u64 count;
+ u64 time_enabled;
+ u64 time_running;
+ u64 id;
+ } read_data;
+ /*
+ * Check if parse_single_tracepoint_event has already asked for
+ * PERF_SAMPLE_TIME.
+ *
+ * XXX this is kludgy but short term fix for problems introduced by
+ * eac23d1c that broke 'perf script' by having different sample_types
+ * when using multiple tracepoint events when we use a perf binary
+ * that tries to use sample_id_all on an older kernel.
+ *
+ * We need to move counter creation to perf_session, support
+ * different sample_types, etc.
+ */
+ bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
+
+ attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
+ PERF_FORMAT_TOTAL_TIME_RUNNING |
+ PERF_FORMAT_ID;
+
+ attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
+
+ if (nr_counters > 1)
+ attr->sample_type |= PERF_SAMPLE_ID;
+
+ /*
+ * We default some events to a 1 default interval. But keep
+ * it a weak assumption overridable by the user.
+ */
+ if (!attr->sample_period || (user_freq != UINT_MAX &&
+ user_interval != ULLONG_MAX)) {
+ if (freq) {
+ attr->sample_type |= PERF_SAMPLE_PERIOD;
+ attr->freq = 1;
+ attr->sample_freq = freq;
+ } else {
+ attr->sample_period = default_interval;
+ }
+ }
+
+ if (no_samples)
+ attr->sample_freq = 0;
+
+ if (inherit_stat)
+ attr->inherit_stat = 1;
+
+ if (sample_address) {
+ attr->sample_type |= PERF_SAMPLE_ADDR;
+ attr->mmap_data = track;
+ }
+
+ if (call_graph)
+ attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
+
+ if (system_wide)
+ attr->sample_type |= PERF_SAMPLE_CPU;
+
+ if (sample_id_all_avail &&
+ (sample_time || system_wide || !no_inherit || cpu_list))
+ attr->sample_type |= PERF_SAMPLE_TIME;
+
+ if (raw_samples) {
+ attr->sample_type |= PERF_SAMPLE_TIME;
+ attr->sample_type |= PERF_SAMPLE_RAW;
+ attr->sample_type |= PERF_SAMPLE_CPU;
+ }
+
+ if (nodelay) {
+ attr->watermark = 0;
+ attr->wakeup_events = 1;
+ }
+
+ attr->mmap = track;
+ attr->comm = track;
+ attr->inherit = !no_inherit;
+ if (target_pid == -1 && target_tid == -1 && !system_wide) {
+ attr->disabled = 1;
+ attr->enable_on_exec = 1;
+ }
+retry_sample_id:
+ attr->sample_id_all = sample_id_all_avail ? 1 : 0;
+
+ for (thread_index = 0; thread_index < threads->nr; thread_index++) {
+try_again:
+ FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0);
+
+ if (FD(evsel, nr_cpu, thread_index) < 0) {
+ int err = errno;
+
+ if (err == EPERM || err == EACCES)
+ die("Permission error - are you root?\n"
+ "\t Consider tweaking"
+ " /proc/sys/kernel/perf_event_paranoid.\n");
+ else if (err == ENODEV && cpu_list) {
+ die("No such device - did you specify"
+ " an out-of-range profile CPU?\n");
+ } else if (err == EINVAL && sample_id_all_avail) {
+ /*
+ * Old kernel, no attr->sample_id_type_all field
+ */
+ sample_id_all_avail = false;
+ if (!sample_time && !raw_samples && !time_needed)
+ attr->sample_type &= ~PERF_SAMPLE_TIME;
+
+ goto retry_sample_id;
+ }
+
+ /*
+ * If it's cycles then fall back to hrtimer
+ * based cpu-clock-tick sw counter, which
+ * is always available even if no PMU support:
+ */
+ if (attr->type == PERF_TYPE_HARDWARE
+ && attr->config == PERF_COUNT_HW_CPU_CYCLES) {
+
+ if (verbose)
+ warning(" ... trying to fall back to cpu-clock-ticks\n");
+ attr->type = PERF_TYPE_SOFTWARE;
+ attr->config = PERF_COUNT_SW_CPU_CLOCK;
+ goto try_again;
+ }
+ printf("\n");
+ error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
+ FD(evsel, nr_cpu, thread_index), strerror(err));
+
+#if defined(__i386__) || defined(__x86_64__)
+ if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
+ die("No hardware sampling interrupt available."
+ " No APIC? If so then you can boot the kernel"
+ " with the \"lapic\" boot parameter to"
+ " force-enable it.\n");
+#endif
+
+ die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+ exit(-1);
+ }
+
+ h_attr = get_header_attr(attr, evsel->idx);
+ if (h_attr == NULL)
+ die("nomem\n");
+
+ if (!file_new) {
+ if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
+ fprintf(stderr, "incompatible append\n");
+ exit(-1);
+ }
+ }
+
+ if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) {
+ perror("Unable to read perf file descriptor");
+ exit(-1);
+ }
+
+ if (perf_header_attr__add_id(h_attr, read_data.id) < 0) {
+ pr_warning("Not enough memory to add id\n");
+ exit(-1);
+ }
+
+ assert(FD(evsel, nr_cpu, thread_index) >= 0);
+ fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK);
+
+ /*
+ * First counter acts as the group leader:
+ */
+ if (group && group_fd == -1)
+ group_fd = FD(evsel, nr_cpu, thread_index);
+
+ if (evsel->idx || thread_index) {
+ struct perf_evsel *first;
+ first = list_entry(evsel_list.next, struct perf_evsel, node);
+ ret = ioctl(FD(evsel, nr_cpu, thread_index),
+ PERF_EVENT_IOC_SET_OUTPUT,
+ FD(first, nr_cpu, 0));
+ if (ret) {
+ error("failed to set output: %d (%s)\n", errno,
+ strerror(errno));
+ exit(-1);
+ }
+ } else {
+ mmap_array[nr_cpu].prev = 0;
+ mmap_array[nr_cpu].mask = mmap_pages*page_size - 1;
+ mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0);
+ if (mmap_array[nr_cpu].base == MAP_FAILED) {
+ error("failed to mmap with %d (%s)\n", errno, strerror(errno));
+ exit(-1);
+ }
+
+ event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index);
+ event_array[nr_poll].events = POLLIN;
+ nr_poll++;
+ }
+
+ if (filter != NULL) {
+ ret = ioctl(FD(evsel, nr_cpu, thread_index),
+ PERF_EVENT_IOC_SET_FILTER, filter);
+ if (ret) {
+ error("failed to set filter with %d (%s)\n", errno,
+ strerror(errno));
+ exit(-1);
+ }
+ }
+ }
+
+ if (!sample_type)
+ sample_type = attr->sample_type;
+}
+
+static void open_counters(int cpu)
+{
+ struct perf_evsel *pos;
+
+ group_fd = -1;
+
+ list_for_each_entry(pos, &evsel_list, node)
+ create_counter(pos, cpu);
+
+ nr_cpu++;
+}
+
+static int process_buildids(void)
+{
+ u64 size = lseek(output, 0, SEEK_CUR);
+
+ if (size == 0)
+ return 0;
+
+ session->fd = output;
+ return __perf_session__process_events(session, post_processing_offset,
+ size - post_processing_offset,
+ size, &build_id__mark_dso_hit_ops);
+}
+
+static void atexit_header(void)
+{
+ if (!pipe_output) {
+ session->header.data_size += bytes_written;
+
+ if (!no_buildid)
+ process_buildids();
+ perf_header__write(&session->header, output, true);
+ perf_session__delete(session);
+ perf_evsel_list__delete();
+ symbol__exit();
+ }
+}
+
+static void event__synthesize_guest_os(struct machine *machine, void *data)
+{
+ int err;
+ struct perf_session *psession = data;
+
+ if (machine__is_host(machine))
+ return;
+
+ /*
+ *As for guest kernel when processing subcommand record&report,
+ *we arrange module mmap prior to guest kernel mmap and trigger
+ *a preload dso because default guest module symbols are loaded
+ *from guest kallsyms instead of /lib/modules/XXX/XXX. This
+ *method is used to avoid symbol missing when the first addr is
+ *in module instead of in guest kernel.
+ */
+ err = event__synthesize_modules(process_synthesized_event,
+ psession, machine);
+ if (err < 0)
+ pr_err("Couldn't record guest kernel [%d]'s reference"
+ " relocation symbol.\n", machine->pid);
+
+ /*
+ * We use _stext for guest kernel because guest kernel's /proc/kallsyms
+ * have no _text sometimes.
+ */
+ err = event__synthesize_kernel_mmap(process_synthesized_event,
+ psession, machine, "_text");
+ if (err < 0)
+ err = event__synthesize_kernel_mmap(process_synthesized_event,
+ psession, machine, "_stext");
+ if (err < 0)
+ pr_err("Couldn't record guest kernel [%d]'s reference"
+ " relocation symbol.\n", machine->pid);
+}
+
+static struct perf_event_header finished_round_event = {
+ .size = sizeof(struct perf_event_header),
+ .type = PERF_RECORD_FINISHED_ROUND,
+};
+
+static void mmap_read_all(void)
+{
+ int i;
+
+ for (i = 0; i < nr_cpu; i++) {
+ if (mmap_array[i].base)
+ mmap_read(&mmap_array[i]);
+ }
+
+ if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
+ write_output(&finished_round_event, sizeof(finished_round_event));
+}
+
+static int __cmd_record(int argc, const char **argv)
+{
+ int i;
+ struct stat st;
+ int flags;
+ int err;
+ unsigned long waking = 0;
+ int child_ready_pipe[2], go_pipe[2];
+ const bool forks = argc > 0;
+ char buf;
+ struct machine *machine;
+
+ page_size = sysconf(_SC_PAGE_SIZE);
+
+ atexit(sig_atexit);
+ signal(SIGCHLD, sig_handler);
+ signal(SIGINT, sig_handler);
+ signal(SIGUSR1, sig_handler);
+
+ if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
+ perror("failed to create pipes");
+ exit(-1);
+ }
+
+ if (!strcmp(output_name, "-"))
+ pipe_output = 1;
+ else if (!stat(output_name, &st) && st.st_size) {
+ if (write_mode == WRITE_FORCE) {
+ char oldname[PATH_MAX];
+ snprintf(oldname, sizeof(oldname), "%s.old",
+ output_name);
+ unlink(oldname);
+ rename(output_name, oldname);
+ }
+ } else if (write_mode == WRITE_APPEND) {
+ write_mode = WRITE_FORCE;
+ }
+
+ flags = O_CREAT|O_RDWR;
+ if (write_mode == WRITE_APPEND)
+ file_new = 0;
+ else
+ flags |= O_TRUNC;
+
+ if (pipe_output)
+ output = STDOUT_FILENO;
+ else
+ output = open(output_name, flags, S_IRUSR | S_IWUSR);
+ if (output < 0) {
+ perror("failed to create output file");
+ exit(-1);
+ }
+
+ session = perf_session__new(output_name, O_WRONLY,
+ write_mode == WRITE_FORCE, false, NULL);
+ if (session == NULL) {
+ pr_err("Not enough memory for reading perf file header\n");
+ return -1;
+ }
+
+ if (!no_buildid)
+ perf_header__set_feat(&session->header, HEADER_BUILD_ID);
+
+ if (!file_new) {
+ err = perf_header__read(session, output);
+ if (err < 0)
+ goto out_delete_session;
+ }
+
+ if (have_tracepoints(&evsel_list))
+ perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
+
+ /*
+ * perf_session__delete(session) will be called at atexit_header()
+ */
+ atexit(atexit_header);
+
+ if (forks) {
+ child_pid = fork();
+ if (child_pid < 0) {
+ perror("failed to fork");
+ exit(-1);
+ }
+
+ if (!child_pid) {
+ if (pipe_output)
+ dup2(2, 1);
+ close(child_ready_pipe[0]);
+ close(go_pipe[1]);
+ fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
+
+ /*
+ * Do a dummy execvp to get the PLT entry resolved,
+ * so we avoid the resolver overhead on the real
+ * execvp call.
+ */
+ execvp("", (char **)argv);
+
+ /*
+ * Tell the parent we're ready to go
+ */
+ close(child_ready_pipe[1]);
+
+ /*
+ * Wait until the parent tells us to go.
+ */
+ if (read(go_pipe[0], &buf, 1) == -1)
+ perror("unable to read pipe");
+
+ execvp(argv[0], (char **)argv);
+
+ perror(argv[0]);
+ kill(getppid(), SIGUSR1);
+ exit(-1);
+ }
+
+ if (!system_wide && target_tid == -1 && target_pid == -1)
+ threads->map[0] = child_pid;
+
+ close(child_ready_pipe[1]);
+ close(go_pipe[0]);
+ /*
+ * wait for child to settle
+ */
+ if (read(child_ready_pipe[0], &buf, 1) == -1) {
+ perror("unable to read pipe");
+ exit(-1);
+ }
+ close(child_ready_pipe[0]);
+ }
+
+ if (!system_wide && no_inherit && !cpu_list) {
+ open_counters(-1);
+ } else {
+ for (i = 0; i < cpus->nr; i++)
+ open_counters(cpus->map[i]);
+ }
+
+ perf_session__set_sample_type(session, sample_type);
+
+ if (pipe_output) {
+ err = perf_header__write_pipe(output);
+ if (err < 0)
+ return err;
+ } else if (file_new) {
+ err = perf_header__write(&session->header, output, false);
+ if (err < 0)
+ return err;
+ }
+
+ post_processing_offset = lseek(output, 0, SEEK_CUR);
+
+ perf_session__set_sample_id_all(session, sample_id_all_avail);
+
+ if (pipe_output) {
+ err = event__synthesize_attrs(&session->header,
+ process_synthesized_event,
+ session);
+ if (err < 0) {
+ pr_err("Couldn't synthesize attrs.\n");
+ return err;
+ }
+
+ err = event__synthesize_event_types(process_synthesized_event,
+ session);
+ if (err < 0) {
+ pr_err("Couldn't synthesize event_types.\n");
+ return err;
+ }
+
+ if (have_tracepoints(&evsel_list)) {
+ /*
+ * FIXME err <= 0 here actually means that
+ * there were no tracepoints so its not really
+ * an error, just that we don't need to
+ * synthesize anything. We really have to
+ * return this more properly and also
+ * propagate errors that now are calling die()
+ */
+ err = event__synthesize_tracing_data(output, &evsel_list,
+ process_synthesized_event,
+ session);
+ if (err <= 0) {
+ pr_err("Couldn't record tracing data.\n");
+ return err;
+ }
+ advance_output(err);
+ }
+ }
+
+ machine = perf_session__find_host_machine(session);
+ if (!machine) {
+ pr_err("Couldn't find native kernel information.\n");
+ return -1;
+ }
+
+ err = event__synthesize_kernel_mmap(process_synthesized_event,
+ session, machine, "_text");
+ if (err < 0)
+ err = event__synthesize_kernel_mmap(process_synthesized_event,
+ session, machine, "_stext");
+ if (err < 0)
+ pr_err("Couldn't record kernel reference relocation symbol\n"
+ "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
+ "Check /proc/kallsyms permission or run as root.\n");
+
+ err = event__synthesize_modules(process_synthesized_event,
+ session, machine);
+ if (err < 0)
+ pr_err("Couldn't record kernel module information.\n"
+ "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
+ "Check /proc/modules permission or run as root.\n");
+
+ if (perf_guest)
+ perf_session__process_machines(session, event__synthesize_guest_os);
+
+ if (!system_wide)
+ event__synthesize_thread_map(threads, process_synthesized_event,
+ session);
+ else
+ event__synthesize_threads(process_synthesized_event, session);
+
+ if (realtime_prio) {
+ struct sched_param param;
+
+ param.sched_priority = realtime_prio;
+ if (sched_setscheduler(0, SCHED_FIFO, &param)) {
+ pr_err("Could not set realtime priority.\n");
+ exit(-1);
+ }
+ }
+
+ /*
+ * Let the child rip
+ */
+ if (forks)
+ close(go_pipe[1]);
+
+ for (;;) {
+ int hits = samples;
+ int thread;
+
+ mmap_read_all();
+
+ if (hits == samples) {
+ if (done)
+ break;
+ err = poll(event_array, nr_poll, -1);
+ waking++;
+ }
+
+ if (done) {
+ for (i = 0; i < nr_cpu; i++) {
+ struct perf_evsel *pos;
+
+ list_for_each_entry(pos, &evsel_list, node) {
+ for (thread = 0;
+ thread < threads->nr;
+ thread++)
+ ioctl(FD(pos, i, thread),
+ PERF_EVENT_IOC_DISABLE);
+ }
+ }
+ }
+ }
+
+ if (quiet || signr == SIGUSR1)
+ return 0;
+
+ fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
+
+ /*
+ * Approximate RIP event size: 24 bytes.
+ */
+ fprintf(stderr,
+ "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
+ (double)bytes_written / 1024.0 / 1024.0,
+ output_name,
+ bytes_written / 24);
+
+ return 0;
+
+out_delete_session:
+ perf_session__delete(session);
+ return err;
+}
+
+static const char * const record_usage[] = {
+ "perf record [<options>] [<command>]",
+ "perf record [<options>] -- <command> [<options>]",
+ NULL
+};
+
+static bool force, append_file;
+
+const struct option record_options[] = {
+ OPT_CALLBACK('e', "event", NULL, "event",
+ "event selector. use 'perf list' to list available events",
+ parse_events),
+ OPT_CALLBACK(0, "filter", NULL, "filter",
+ "event filter", parse_filter),
+ OPT_INTEGER('p', "pid", &target_pid,
+ "record events on existing process id"),
+ OPT_INTEGER('t', "tid", &target_tid,
+ "record events on existing thread id"),
+ OPT_INTEGER('r', "realtime", &realtime_prio,
+ "collect data with this RT SCHED_FIFO priority"),
+ OPT_BOOLEAN('D', "no-delay", &nodelay,
+ "collect data without buffering"),
+ OPT_BOOLEAN('R', "raw-samples", &raw_samples,
+ "collect raw sample records from all opened counters"),
+ OPT_BOOLEAN('a', "all-cpus", &system_wide,
+ "system-wide collection from all CPUs"),
+ OPT_BOOLEAN('A', "append", &append_file,
+ "append to the output file to do incremental profiling"),
+ OPT_STRING('C', "cpu", &cpu_list, "cpu",
+ "list of cpus to monitor"),
+ OPT_BOOLEAN('f', "force", &force,
+ "overwrite existing data file (deprecated)"),
+ OPT_U64('c', "count", &user_interval, "event period to sample"),
+ OPT_STRING('o', "output", &output_name, "file",
+ "output file name"),
+ OPT_BOOLEAN('i', "no-inherit", &no_inherit,
+ "child tasks do not inherit counters"),
+ OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"),
+ OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"),
+ OPT_BOOLEAN('g', "call-graph", &call_graph,
+ "do call-graph (stack chain/backtrace) recording"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show counter open errors, etc)"),
+ OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
+ OPT_BOOLEAN('s', "stat", &inherit_stat,
+ "per thread counts"),
+ OPT_BOOLEAN('d', "data", &sample_address,
+ "Sample addresses"),
+ OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"),
+ OPT_BOOLEAN('n', "no-samples", &no_samples,
+ "don't sample"),
+ OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache,
+ "do not update the buildid cache"),
+ OPT_BOOLEAN('B', "no-buildid", &no_buildid,
+ "do not collect buildids in perf.data"),
+ OPT_END()
+};
+
+int cmd_record(int argc, const char **argv, const char *prefix __used)
+{
+ int err = -ENOMEM;
+ struct perf_evsel *pos;
+
+ argc = parse_options(argc, argv, record_options, record_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (!argc && target_pid == -1 && target_tid == -1 &&
+ !system_wide && !cpu_list)
+ usage_with_options(record_usage, record_options);
+
+ if (force && append_file) {
+ fprintf(stderr, "Can't overwrite and append at the same time."
+ " You need to choose between -f and -A");
+ usage_with_options(record_usage, record_options);
+ } else if (append_file) {
+ write_mode = WRITE_APPEND;
+ } else {
+ write_mode = WRITE_FORCE;
+ }
+
+ symbol__init();
+
+ if (no_buildid_cache || no_buildid)
+ disable_buildid_cache();
+
+ if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) {
+ pr_err("Not enough memory for event selector list\n");
+ goto out_symbol_exit;
+ }
+
+ if (target_pid != -1)
+ target_tid = target_pid;
+
+ threads = thread_map__new(target_pid, target_tid);
+ if (threads == NULL) {
+ pr_err("Problems finding threads of monitor\n");
+ usage_with_options(record_usage, record_options);
+ }
+
+ cpus = cpu_map__new(cpu_list);
+ if (cpus == NULL) {
+ perror("failed to parse CPUs map");
+ return -1;
+ }
+
+ list_for_each_entry(pos, &evsel_list, node) {
+ if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
+ goto out_free_fd;
+ if (perf_header__push_event(pos->attr.config, event_name(pos)))
+ goto out_free_fd;
+ }
+ event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS *
+ MAX_COUNTERS * threads->nr));
+ if (!event_array)
+ goto out_free_fd;
+
+ if (user_interval != ULLONG_MAX)
+ default_interval = user_interval;
+ if (user_freq != UINT_MAX)
+ freq = user_freq;
+
+ /*
+ * User specified count overrides default frequency.
+ */
+ if (default_interval)
+ freq = 0;
+ else if (freq) {
+ default_interval = freq;
+ } else {
+ fprintf(stderr, "frequency and count are zero, aborting\n");
+ err = -EINVAL;
+ goto out_free_event_array;
+ }
+
+ err = __cmd_record(argc, argv);
+
+out_free_event_array:
+ free(event_array);
+out_free_fd:
+ thread_map__delete(threads);
+ threads = NULL;
+out_symbol_exit:
+ symbol__exit();
+ return err;
+}
diff --git a/smartt-perf/builtin-report.c b/smartt-perf/builtin-report.c
new file mode 100644
index 0000000..c27e31f
--- /dev/null
+++ b/smartt-perf/builtin-report.c
@@ -0,0 +1,551 @@
+/*
+ * builtin-report.c
+ *
+ * Builtin report command: Analyze the perf.data input file,
+ * look up and read DSOs and symbol information and display
+ * a histogram of results, along various sorting keys.
+ */
+#include "builtin.h"
+
+#include "util/util.h"
+
+#include "util/color.h"
+#include <linux/list.h>
+#include "util/cache.h"
+#include <linux/rbtree.h>
+#include "util/symbol.h"
+#include "util/callchain.h"
+#include "util/strlist.h"
+#include "util/values.h"
+
+#include "perf.h"
+#include "util/debug.h"
+#include "util/header.h"
+#include "util/session.h"
+
+#include "util/parse-options.h"
+#include "util/parse-events.h"
+
+#include "util/thread.h"
+#include "util/sort.h"
+#include "util/hist.h"
+
+static char const *input_name = "perf.data";
+
+static bool force, use_tui, use_stdio;
+static bool hide_unresolved;
+static bool dont_use_callchains;
+
+static bool show_threads;
+static struct perf_read_values show_threads_values;
+
+static const char default_pretty_printing_style[] = "normal";
+static const char *pretty_printing_style = default_pretty_printing_style;
+
+static char callchain_default_opt[] = "fractal,0.5";
+
+static struct hists *perf_session__hists_findnew(struct perf_session *self,
+ u64 event_stream, u32 type,
+ u64 config)
+{
+ struct rb_node **p = &self->hists_tree.rb_node;
+ struct rb_node *parent = NULL;
+ struct hists *iter, *new;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hists, rb_node);
+ if (iter->config == config)
+ return iter;
+
+
+ if (config > iter->config)
+ p = &(*p)->rb_right;
+ else
+ p = &(*p)->rb_left;
+ }
+
+ new = malloc(sizeof(struct hists));
+ if (new == NULL)
+ return NULL;
+ memset(new, 0, sizeof(struct hists));
+ new->event_stream = event_stream;
+ new->config = config;
+ new->type = type;
+ rb_link_node(&new->rb_node, parent, p);
+ rb_insert_color(&new->rb_node, &self->hists_tree);
+ return new;
+}
+
+static int perf_session__add_hist_entry(struct perf_session *self,
+ struct addr_location *al,
+ struct sample_data *data)
+{
+ struct map_symbol *syms = NULL;
+ struct symbol *parent = NULL;
+ int err = -ENOMEM;
+ struct hist_entry *he;
+ struct hists *hists;
+ struct perf_event_attr *attr;
+
+ if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) {
+ syms = perf_session__resolve_callchain(self, al->thread,
+ data->callchain, &parent);
+ if (syms == NULL)
+ return -ENOMEM;
+ }
+
+ attr = perf_header__find_attr(data->id, &self->header);
+ if (attr)
+ hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config);
+ else
+ hists = perf_session__hists_findnew(self, data->id, 0, 0);
+ if (hists == NULL)
+ goto out_free_syms;
+ he = __hists__add_entry(hists, al, parent, data->period);
+ if (he == NULL)
+ goto out_free_syms;
+ err = 0;
+ if (symbol_conf.use_callchain) {
+ err = callchain_append(he->callchain, data->callchain, syms,
+ data->period);
+ if (err)
+ goto out_free_syms;
+ }
+ /*
+ * Only in the newt browser we are doing integrated annotation,
+ * so we don't allocated the extra space needed because the stdio
+ * code will not use it.
+ */
+ if (use_browser > 0)
+ err = hist_entry__inc_addr_samples(he, al->addr);
+out_free_syms:
+ free(syms);
+ return err;
+}
+
+static int add_event_total(struct perf_session *session,
+ struct sample_data *data,
+ struct perf_event_attr *attr)
+{
+ struct hists *hists;
+
+ if (attr)
+ hists = perf_session__hists_findnew(session, data->id,
+ attr->type, attr->config);
+ else
+ hists = perf_session__hists_findnew(session, data->id, 0, 0);
+
+ if (!hists)
+ return -ENOMEM;
+
+ hists->stats.total_period += data->period;
+ /*
+ * FIXME: add_event_total should be moved from here to
+ * perf_session__process_event so that the proper hist is passed to
+ * the event_op methods.
+ */
+ hists__inc_nr_events(hists, PERF_RECORD_SAMPLE);
+ session->hists.stats.total_period += data->period;
+ return 0;
+}
+
+static int process_sample_event(event_t *event, struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct addr_location al;
+ struct perf_event_attr *attr;
+
+ if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
+ fprintf(stderr, "problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ if (al.filtered || (hide_unresolved && al.sym == NULL))
+ return 0;
+
+ if (perf_session__add_hist_entry(session, &al, sample)) {
+ pr_debug("problem incrementing symbol period, skipping event\n");
+ return -1;
+ }
+
+ attr = perf_header__find_attr(sample->id, &session->header);
+
+ if (add_event_total(session, sample, attr)) {
+ pr_debug("problem adding event period\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_read_event(event_t *event, struct sample_data *sample __used,
+ struct perf_session *session __used)
+{
+ struct perf_event_attr *attr;
+
+ attr = perf_header__find_attr(event->read.id, &session->header);
+
+ if (show_threads) {
+ const char *name = attr ? __event_name(attr->type, attr->config)
+ : "unknown";
+ perf_read_values_add_value(&show_threads_values,
+ event->read.pid, event->read.tid,
+ event->read.id,
+ name,
+ event->read.value);
+ }
+
+ dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
+ attr ? __event_name(attr->type, attr->config) : "FAIL",
+ event->read.value);
+
+ return 0;
+}
+
+static int perf_session__setup_sample_type(struct perf_session *self)
+{
+ if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
+ if (sort__has_parent) {
+ fprintf(stderr, "selected --sort parent, but no"
+ " callchain data. Did you call"
+ " perf record without -g?\n");
+ return -EINVAL;
+ }
+ if (symbol_conf.use_callchain) {
+ fprintf(stderr, "selected -g but no callchain data."
+ " Did you call perf record without"
+ " -g?\n");
+ return -1;
+ }
+ } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
+ !symbol_conf.use_callchain) {
+ symbol_conf.use_callchain = true;
+ if (register_callchain_param(&callchain_param) < 0) {
+ fprintf(stderr, "Can't register callchain"
+ " params\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct perf_event_ops event_ops = {
+ .sample = process_sample_event,
+ .mmap = event__process_mmap,
+ .comm = event__process_comm,
+ .exit = event__process_task,
+ .fork = event__process_task,
+ .lost = event__process_lost,
+ .read = process_read_event,
+ .attr = event__process_attr,
+ .event_type = event__process_event_type,
+ .tracing_data = event__process_tracing_data,
+ .build_id = event__process_build_id,
+ .ordered_samples = true,
+ .ordering_requires_timestamps = true,
+};
+
+extern volatile int session_done;
+
+static void sig_handler(int sig __used)
+{
+ session_done = 1;
+}
+
+static size_t hists__fprintf_nr_sample_events(struct hists *self,
+ const char *evname, FILE *fp)
+{
+ size_t ret;
+ char unit;
+ unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
+
+ nr_events = convert_unit(nr_events, &unit);
+ ret = fprintf(fp, "# Events: %lu%c", nr_events, unit);
+ if (evname != NULL)
+ ret += fprintf(fp, " %s", evname);
+ return ret + fprintf(fp, "\n#\n");
+}
+
+static int hists__tty_browse_tree(struct rb_root *tree, const char *help)
+{
+ struct rb_node *next = rb_first(tree);
+
+ while (next) {
+ struct hists *hists = rb_entry(next, struct hists, rb_node);
+ const char *evname = NULL;
+
+ if (rb_first(&hists->entries) != rb_last(&hists->entries))
+ evname = __event_name(hists->type, hists->config);
+
+ hists__fprintf_nr_sample_events(hists, evname, stdout);
+ hists__fprintf(hists, NULL, false, stdout);
+ fprintf(stdout, "\n\n");
+ next = rb_next(&hists->rb_node);
+ }
+
+ if (sort_order == default_sort_order &&
+ parent_pattern == default_parent_pattern) {
+ fprintf(stdout, "#\n# (%s)\n#\n", help);
+
+ if (show_threads) {
+ bool style = !strcmp(pretty_printing_style, "raw");
+ perf_read_values_display(stdout, &show_threads_values,
+ style);
+ perf_read_values_destroy(&show_threads_values);
+ }
+ }
+
+ return 0;
+}
+
+static int __cmd_report(void)
+{
+ int ret = -EINVAL;
+ struct perf_session *session;
+ struct rb_node *next;
+ const char *help = "For a higher level overview, try: perf report --sort comm,dso";
+
+ signal(SIGINT, sig_handler);
+
+ session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
+ if (session == NULL)
+ return -ENOMEM;
+
+ if (show_threads)
+ perf_read_values_init(&show_threads_values);
+
+ ret = perf_session__setup_sample_type(session);
+ if (ret)
+ goto out_delete;
+
+ ret = perf_session__process_events(session, &event_ops);
+ if (ret)
+ goto out_delete;
+
+ if (dump_trace) {
+ perf_session__fprintf_nr_events(session, stdout);
+ goto out_delete;
+ }
+
+ if (verbose > 3)
+ perf_session__fprintf(session, stdout);
+
+ if (verbose > 2)
+ perf_session__fprintf_dsos(session, stdout);
+
+ next = rb_first(&session->hists_tree);
+ while (next) {
+ struct hists *hists;
+
+ hists = rb_entry(next, struct hists, rb_node);
+ hists__collapse_resort(hists);
+ hists__output_resort(hists);
+ next = rb_next(&hists->rb_node);
+ }
+
+ if (use_browser > 0)
+ hists__tui_browse_tree(&session->hists_tree, help);
+ else
+ hists__tty_browse_tree(&session->hists_tree, help);
+
+out_delete:
+ /*
+ * Speed up the exit process, for large files this can
+ * take quite a while.
+ *
+ * XXX Enable this when using valgrind or if we ever
+ * librarize this command.
+ *
+ * Also experiment with obstacks to see how much speed
+ * up we'll get here.
+ *
+ * perf_session__delete(session);
+ */
+ return ret;
+}
+
+static int
+parse_callchain_opt(const struct option *opt __used, const char *arg,
+ int unset)
+{
+ char *tok, *tok2;
+ char *endptr;
+
+ /*
+ * --no-call-graph
+ */
+ if (unset) {
+ dont_use_callchains = true;
+ return 0;
+ }
+
+ symbol_conf.use_callchain = true;
+
+ if (!arg)
+ return 0;
+
+ tok = strtok((char *)arg, ",");
+ if (!tok)
+ return -1;
+
+ /* get the output mode */
+ if (!strncmp(tok, "graph", strlen(arg)))
+ callchain_param.mode = CHAIN_GRAPH_ABS;
+
+ else if (!strncmp(tok, "flat", strlen(arg)))
+ callchain_param.mode = CHAIN_FLAT;
+
+ else if (!strncmp(tok, "fractal", strlen(arg)))
+ callchain_param.mode = CHAIN_GRAPH_REL;
+
+ else if (!strncmp(tok, "none", strlen(arg))) {
+ callchain_param.mode = CHAIN_NONE;
+ symbol_conf.use_callchain = false;
+
+ return 0;
+ }
+
+ else
+ return -1;
+
+ /* get the min percentage */
+ tok = strtok(NULL, ",");
+ if (!tok)
+ goto setup;
+
+ tok2 = strtok(NULL, ",");
+ callchain_param.min_percent = strtod(tok, &endptr);
+ if (tok == endptr)
+ return -1;
+
+ if (tok2)
+ callchain_param.print_limit = strtod(tok2, &endptr);
+setup:
+ if (register_callchain_param(&callchain_param) < 0) {
+ fprintf(stderr, "Can't register callchain params\n");
+ return -1;
+ }
+ return 0;
+}
+
+static const char * const report_usage[] = {
+ "perf report [<options>] <command>",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
+ OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
+ "file", "kallsyms pathname"),
+ OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
+ OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
+ "load module symbols - WARNING: use only with -k and LIVE kernel"),
+ OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
+ "Show a column with the number of samples"),
+ OPT_BOOLEAN('T', "threads", &show_threads,
+ "Show per-thread event counters"),
+ OPT_STRING(0, "pretty", &pretty_printing_style, "key",
+ "pretty printing style key: normal raw"),
+ OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
+ OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
+ OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
+ "sort by key(s): pid, comm, dso, symbol, parent"),
+ OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
+ "Show sample percentage for different cpu modes"),
+ OPT_STRING('p', "parent", &parent_pattern, "regex",
+ "regex filter to identify parent, see: '--sort parent'"),
+ OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
+ "Only display entries with parent-match"),
+ OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
+ "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. "
+ "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
+ OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
+ "only consider symbols in these dsos"),
+ OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
+ "only consider symbols in these comms"),
+ OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
+ "only consider these symbols"),
+ OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
+ "width[,width...]",
+ "don't try to adjust column width, use these fixed values"),
+ OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
+ "separator for columns, no spaces will be added between "
+ "columns '.' is reserved."),
+ OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
+ "Only display entries resolved to a symbol"),
+ OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+ "Look for files with symbols relative to this directory"),
+ OPT_END()
+};
+
+int cmd_report(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, options, report_usage, 0);
+
+ if (use_stdio)
+ use_browser = 0;
+ else if (use_tui)
+ use_browser = 1;
+
+ if (strcmp(input_name, "-") != 0)
+ setup_browser();
+ else
+ use_browser = 0;
+ /*
+ * Only in the newt browser we are doing integrated annotation,
+ * so don't allocate extra space that won't be used in the stdio
+ * implementation.
+ */
+ if (use_browser > 0) {
+ symbol_conf.priv_size = sizeof(struct sym_priv);
+ /*
+ * For searching by name on the "Browse map details".
+ * providing it only in verbose mode not to bloat too
+ * much struct symbol.
+ */
+ if (verbose) {
+ /*
+ * XXX: Need to provide a less kludgy way to ask for
+ * more space per symbol, the u32 is for the index on
+ * the ui browser.
+ * See symbol__browser_index.
+ */
+ symbol_conf.priv_size += sizeof(u32);
+ symbol_conf.sort_by_name = true;
+ }
+ }
+
+ if (symbol__init() < 0)
+ return -1;
+
+ setup_sorting(report_usage, options);
+
+ if (parent_pattern != default_parent_pattern) {
+ if (sort_dimension__add("parent") < 0)
+ return -1;
+ sort_parent.elide = 1;
+ } else
+ symbol_conf.exclude_other = false;
+
+ /*
+ * Any (unrecognized) arguments left?
+ */
+ if (argc)
+ usage_with_options(report_usage, options);
+
+ sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
+ sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
+ sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
+
+ return __cmd_report();
+}
diff --git a/smartt-perf/builtin-sched.c b/smartt-perf/builtin-sched.c
new file mode 100644
index 0000000..29acb89
--- /dev/null
+++ b/smartt-perf/builtin-sched.c
@@ -0,0 +1,1921 @@
+#include "builtin.h"
+#include "perf.h"
+
+#include "util/util.h"
+#include "util/cache.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/header.h"
+#include "util/session.h"
+
+#include "util/parse-options.h"
+#include "util/trace-event.h"
+
+#include "util/debug.h"
+
+#include <sys/prctl.h>
+
+#include <semaphore.h>
+#include <pthread.h>
+#include <math.h>
+
+static char const *input_name = "perf.data";
+
+static char default_sort_order[] = "avg, max, switch, runtime";
+static const char *sort_order = default_sort_order;
+
+static int profile_cpu = -1;
+
+#define PR_SET_NAME 15 /* Set process name */
+#define MAX_CPUS 4096
+
+static u64 run_measurement_overhead;
+static u64 sleep_measurement_overhead;
+
+#define COMM_LEN 20
+#define SYM_LEN 129
+
+#define MAX_PID 65536
+
+static unsigned long nr_tasks;
+
+struct sched_atom;
+
+struct task_desc {
+ unsigned long nr;
+ unsigned long pid;
+ char comm[COMM_LEN];
+
+ unsigned long nr_events;
+ unsigned long curr_event;
+ struct sched_atom **atoms;
+
+ pthread_t thread;
+ sem_t sleep_sem;
+
+ sem_t ready_for_work;
+ sem_t work_done_sem;
+
+ u64 cpu_usage;
+};
+
+enum sched_event_type {
+ SCHED_EVENT_RUN,
+ SCHED_EVENT_SLEEP,
+ SCHED_EVENT_WAKEUP,
+ SCHED_EVENT_MIGRATION,
+};
+
+struct sched_atom {
+ enum sched_event_type type;
+ int specific_wait;
+ u64 timestamp;
+ u64 duration;
+ unsigned long nr;
+ sem_t *wait_sem;
+ struct task_desc *wakee;
+};
+
+static struct task_desc *pid_to_task[MAX_PID];
+
+static struct task_desc **tasks;
+
+static pthread_mutex_t start_work_mutex = PTHREAD_MUTEX_INITIALIZER;
+static u64 start_time;
+
+static pthread_mutex_t work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static unsigned long nr_run_events;
+static unsigned long nr_sleep_events;
+static unsigned long nr_wakeup_events;
+
+static unsigned long nr_sleep_corrections;
+static unsigned long nr_run_events_optimized;
+
+static unsigned long targetless_wakeups;
+static unsigned long multitarget_wakeups;
+
+static u64 cpu_usage;
+static u64 runavg_cpu_usage;
+static u64 parent_cpu_usage;
+static u64 runavg_parent_cpu_usage;
+
+static unsigned long nr_runs;
+static u64 sum_runtime;
+static u64 sum_fluct;
+static u64 run_avg;
+
+static unsigned int replay_repeat = 10;
+static unsigned long nr_timestamps;
+static unsigned long nr_unordered_timestamps;
+static unsigned long nr_state_machine_bugs;
+static unsigned long nr_context_switch_bugs;
+static unsigned long nr_events;
+static unsigned long nr_lost_chunks;
+static unsigned long nr_lost_events;
+
+#define TASK_STATE_TO_CHAR_STR "RSDTtZX"
+
+enum thread_state {
+ THREAD_SLEEPING = 0,
+ THREAD_WAIT_CPU,
+ THREAD_SCHED_IN,
+ THREAD_IGNORE
+};
+
+struct work_atom {
+ struct list_head list;
+ enum thread_state state;
+ u64 sched_out_time;
+ u64 wake_up_time;
+ u64 sched_in_time;
+ u64 runtime;
+};
+
+struct work_atoms {
+ struct list_head work_list;
+ struct thread *thread;
+ struct rb_node node;
+ u64 max_lat;
+ u64 max_lat_at;
+ u64 total_lat;
+ u64 nb_atoms;
+ u64 total_runtime;
+};
+
+typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);
+
+static struct rb_root atom_root, sorted_atom_root;
+
+static u64 all_runtime;
+static u64 all_count;
+
+
+static u64 get_nsecs(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+static void burn_nsecs(u64 nsecs)
+{
+ u64 T0 = get_nsecs(), T1;
+
+ do {
+ T1 = get_nsecs();
+ } while (T1 + run_measurement_overhead < T0 + nsecs);
+}
+
+static void sleep_nsecs(u64 nsecs)
+{
+ struct timespec ts;
+
+ ts.tv_nsec = nsecs % 999999999;
+ ts.tv_sec = nsecs / 999999999;
+
+ nanosleep(&ts, NULL);
+}
+
+static void calibrate_run_measurement_overhead(void)
+{
+ u64 T0, T1, delta, min_delta = 1000000000ULL;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ T0 = get_nsecs();
+ burn_nsecs(0);
+ T1 = get_nsecs();
+ delta = T1-T0;
+ min_delta = min(min_delta, delta);
+ }
+ run_measurement_overhead = min_delta;
+
+ printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta);
+}
+
+static void calibrate_sleep_measurement_overhead(void)
+{
+ u64 T0, T1, delta, min_delta = 1000000000ULL;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ T0 = get_nsecs();
+ sleep_nsecs(10000);
+ T1 = get_nsecs();
+ delta = T1-T0;
+ min_delta = min(min_delta, delta);
+ }
+ min_delta -= 10000;
+ sleep_measurement_overhead = min_delta;
+
+ printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta);
+}
+
+static struct sched_atom *
+get_new_event(struct task_desc *task, u64 timestamp)
+{
+ struct sched_atom *event = zalloc(sizeof(*event));
+ unsigned long idx = task->nr_events;
+ size_t size;
+
+ event->timestamp = timestamp;
+ event->nr = idx;
+
+ task->nr_events++;
+ size = sizeof(struct sched_atom *) * task->nr_events;
+ task->atoms = realloc(task->atoms, size);
+ BUG_ON(!task->atoms);
+
+ task->atoms[idx] = event;
+
+ return event;
+}
+
+static struct sched_atom *last_event(struct task_desc *task)
+{
+ if (!task->nr_events)
+ return NULL;
+
+ return task->atoms[task->nr_events - 1];
+}
+
+static void
+add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration)
+{
+ struct sched_atom *event, *curr_event = last_event(task);
+
+ /*
+ * optimize an existing RUN event by merging this one
+ * to it:
+ */
+ if (curr_event && curr_event->type == SCHED_EVENT_RUN) {
+ nr_run_events_optimized++;
+ curr_event->duration += duration;
+ return;
+ }
+
+ event = get_new_event(task, timestamp);
+
+ event->type = SCHED_EVENT_RUN;
+ event->duration = duration;
+
+ nr_run_events++;
+}
+
+static void
+add_sched_event_wakeup(struct task_desc *task, u64 timestamp,
+ struct task_desc *wakee)
+{
+ struct sched_atom *event, *wakee_event;
+
+ event = get_new_event(task, timestamp);
+ event->type = SCHED_EVENT_WAKEUP;
+ event->wakee = wakee;
+
+ wakee_event = last_event(wakee);
+ if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) {
+ targetless_wakeups++;
+ return;
+ }
+ if (wakee_event->wait_sem) {
+ multitarget_wakeups++;
+ return;
+ }
+
+ wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem));
+ sem_init(wakee_event->wait_sem, 0, 0);
+ wakee_event->specific_wait = 1;
+ event->wait_sem = wakee_event->wait_sem;
+
+ nr_wakeup_events++;
+}
+
+static void
+add_sched_event_sleep(struct task_desc *task, u64 timestamp,
+ u64 task_state __used)
+{
+ struct sched_atom *event = get_new_event(task, timestamp);
+
+ event->type = SCHED_EVENT_SLEEP;
+
+ nr_sleep_events++;
+}
+
+static struct task_desc *register_pid(unsigned long pid, const char *comm)
+{
+ struct task_desc *task;
+
+ BUG_ON(pid >= MAX_PID);
+
+ task = pid_to_task[pid];
+
+ if (task)
+ return task;
+
+ task = zalloc(sizeof(*task));
+ task->pid = pid;
+ task->nr = nr_tasks;
+ strcpy(task->comm, comm);
+ /*
+ * every task starts in sleeping state - this gets ignored
+ * if there's no wakeup pointing to this sleep state:
+ */
+ add_sched_event_sleep(task, 0, 0);
+
+ pid_to_task[pid] = task;
+ nr_tasks++;
+ tasks = realloc(tasks, nr_tasks*sizeof(struct task_task *));
+ BUG_ON(!tasks);
+ tasks[task->nr] = task;
+
+ if (verbose)
+ printf("registered task #%ld, PID %ld (%s)\n", nr_tasks, pid, comm);
+
+ return task;
+}
+
+
+static void print_task_traces(void)
+{
+ struct task_desc *task;
+ unsigned long i;
+
+ for (i = 0; i < nr_tasks; i++) {
+ task = tasks[i];
+ printf("task %6ld (%20s:%10ld), nr_events: %ld\n",
+ task->nr, task->comm, task->pid, task->nr_events);
+ }
+}
+
+static void add_cross_task_wakeups(void)
+{
+ struct task_desc *task1, *task2;
+ unsigned long i, j;
+
+ for (i = 0; i < nr_tasks; i++) {
+ task1 = tasks[i];
+ j = i + 1;
+ if (j == nr_tasks)
+ j = 0;
+ task2 = tasks[j];
+ add_sched_event_wakeup(task1, 0, task2);
+ }
+}
+
+static void
+process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom)
+{
+ int ret = 0;
+ u64 now;
+ long long delta;
+
+ now = get_nsecs();
+ delta = start_time + atom->timestamp - now;
+
+ switch (atom->type) {
+ case SCHED_EVENT_RUN:
+ burn_nsecs(atom->duration);
+ break;
+ case SCHED_EVENT_SLEEP:
+ if (atom->wait_sem)
+ ret = sem_wait(atom->wait_sem);
+ BUG_ON(ret);
+ break;
+ case SCHED_EVENT_WAKEUP:
+ if (atom->wait_sem)
+ ret = sem_post(atom->wait_sem);
+ BUG_ON(ret);
+ break;
+ case SCHED_EVENT_MIGRATION:
+ break;
+ default:
+ BUG_ON(1);
+ }
+}
+
+static u64 get_cpu_usage_nsec_parent(void)
+{
+ struct rusage ru;
+ u64 sum;
+ int err;
+
+ err = getrusage(RUSAGE_SELF, &ru);
+ BUG_ON(err);
+
+ sum = ru.ru_utime.tv_sec*1e9 + ru.ru_utime.tv_usec*1e3;
+ sum += ru.ru_stime.tv_sec*1e9 + ru.ru_stime.tv_usec*1e3;
+
+ return sum;
+}
+
+static int self_open_counters(void)
+{
+ struct perf_event_attr attr;
+ int fd;
+
+ memset(&attr, 0, sizeof(attr));
+
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.config = PERF_COUNT_SW_TASK_CLOCK;
+
+ fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+
+ if (fd < 0)
+ die("Error: sys_perf_event_open() syscall returned"
+ "with %d (%s)\n", fd, strerror(errno));
+ return fd;
+}
+
+static u64 get_cpu_usage_nsec_self(int fd)
+{
+ u64 runtime;
+ int ret;
+
+ ret = read(fd, &runtime, sizeof(runtime));
+ BUG_ON(ret != sizeof(runtime));
+
+ return runtime;
+}
+
+static void *thread_func(void *ctx)
+{
+ struct task_desc *this_task = ctx;
+ u64 cpu_usage_0, cpu_usage_1;
+ unsigned long i, ret;
+ char comm2[22];
+ int fd;
+
+ sprintf(comm2, ":%s", this_task->comm);
+ prctl(PR_SET_NAME, comm2);
+ fd = self_open_counters();
+
+again:
+ ret = sem_post(&this_task->ready_for_work);
+ BUG_ON(ret);
+ ret = pthread_mutex_lock(&start_work_mutex);
+ BUG_ON(ret);
+ ret = pthread_mutex_unlock(&start_work_mutex);
+ BUG_ON(ret);
+
+ cpu_usage_0 = get_cpu_usage_nsec_self(fd);
+
+ for (i = 0; i < this_task->nr_events; i++) {
+ this_task->curr_event = i;
+ process_sched_event(this_task, this_task->atoms[i]);
+ }
+
+ cpu_usage_1 = get_cpu_usage_nsec_self(fd);
+ this_task->cpu_usage = cpu_usage_1 - cpu_usage_0;
+ ret = sem_post(&this_task->work_done_sem);
+ BUG_ON(ret);
+
+ ret = pthread_mutex_lock(&work_done_wait_mutex);
+ BUG_ON(ret);
+ ret = pthread_mutex_unlock(&work_done_wait_mutex);
+ BUG_ON(ret);
+
+ goto again;
+}
+
+static void create_tasks(void)
+{
+ struct task_desc *task;
+ pthread_attr_t attr;
+ unsigned long i;
+ int err;
+
+ err = pthread_attr_init(&attr);
+ BUG_ON(err);
+ err = pthread_attr_setstacksize(&attr,
+ (size_t) max(16 * 1024, PTHREAD_STACK_MIN));
+ BUG_ON(err);
+ err = pthread_mutex_lock(&start_work_mutex);
+ BUG_ON(err);
+ err = pthread_mutex_lock(&work_done_wait_mutex);
+ BUG_ON(err);
+ for (i = 0; i < nr_tasks; i++) {
+ task = tasks[i];
+ sem_init(&task->sleep_sem, 0, 0);
+ sem_init(&task->ready_for_work, 0, 0);
+ sem_init(&task->work_done_sem, 0, 0);
+ task->curr_event = 0;
+ err = pthread_create(&task->thread, &attr, thread_func, task);
+ BUG_ON(err);
+ }
+}
+
+static void wait_for_tasks(void)
+{
+ u64 cpu_usage_0, cpu_usage_1;
+ struct task_desc *task;
+ unsigned long i, ret;
+
+ start_time = get_nsecs();
+ cpu_usage = 0;
+ pthread_mutex_unlock(&work_done_wait_mutex);
+
+ for (i = 0; i < nr_tasks; i++) {
+ task = tasks[i];
+ ret = sem_wait(&task->ready_for_work);
+ BUG_ON(ret);
+ sem_init(&task->ready_for_work, 0, 0);
+ }
+ ret = pthread_mutex_lock(&work_done_wait_mutex);
+ BUG_ON(ret);
+
+ cpu_usage_0 = get_cpu_usage_nsec_parent();
+
+ pthread_mutex_unlock(&start_work_mutex);
+
+ for (i = 0; i < nr_tasks; i++) {
+ task = tasks[i];
+ ret = sem_wait(&task->work_done_sem);
+ BUG_ON(ret);
+ sem_init(&task->work_done_sem, 0, 0);
+ cpu_usage += task->cpu_usage;
+ task->cpu_usage = 0;
+ }
+
+ cpu_usage_1 = get_cpu_usage_nsec_parent();
+ if (!runavg_cpu_usage)
+ runavg_cpu_usage = cpu_usage;
+ runavg_cpu_usage = (runavg_cpu_usage*9 + cpu_usage)/10;
+
+ parent_cpu_usage = cpu_usage_1 - cpu_usage_0;
+ if (!runavg_parent_cpu_usage)
+ runavg_parent_cpu_usage = parent_cpu_usage;
+ runavg_parent_cpu_usage = (runavg_parent_cpu_usage*9 +
+ parent_cpu_usage)/10;
+
+ ret = pthread_mutex_lock(&start_work_mutex);
+ BUG_ON(ret);
+
+ for (i = 0; i < nr_tasks; i++) {
+ task = tasks[i];
+ sem_init(&task->sleep_sem, 0, 0);
+ task->curr_event = 0;
+ }
+}
+
+static void run_one_test(void)
+{
+ u64 T0, T1, delta, avg_delta, fluct, std_dev;
+
+ T0 = get_nsecs();
+ wait_for_tasks();
+ T1 = get_nsecs();
+
+ delta = T1 - T0;
+ sum_runtime += delta;
+ nr_runs++;
+
+ avg_delta = sum_runtime / nr_runs;
+ if (delta < avg_delta)
+ fluct = avg_delta - delta;
+ else
+ fluct = delta - avg_delta;
+ sum_fluct += fluct;
+ std_dev = sum_fluct / nr_runs / sqrt(nr_runs);
+ if (!run_avg)
+ run_avg = delta;
+ run_avg = (run_avg*9 + delta)/10;
+
+ printf("#%-3ld: %0.3f, ",
+ nr_runs, (double)delta/1000000.0);
+
+ printf("ravg: %0.2f, ",
+ (double)run_avg/1e6);
+
+ printf("cpu: %0.2f / %0.2f",
+ (double)cpu_usage/1e6, (double)runavg_cpu_usage/1e6);
+
+#if 0
+ /*
+ * rusage statistics done by the parent, these are less
+ * accurate than the sum_exec_runtime based statistics:
+ */
+ printf(" [%0.2f / %0.2f]",
+ (double)parent_cpu_usage/1e6,
+ (double)runavg_parent_cpu_usage/1e6);
+#endif
+
+ printf("\n");
+
+ if (nr_sleep_corrections)
+ printf(" (%ld sleep corrections)\n", nr_sleep_corrections);
+ nr_sleep_corrections = 0;
+}
+
+static void test_calibrations(void)
+{
+ u64 T0, T1;
+
+ T0 = get_nsecs();
+ burn_nsecs(1e6);
+ T1 = get_nsecs();
+
+ printf("the run test took %" PRIu64 " nsecs\n", T1 - T0);
+
+ T0 = get_nsecs();
+ sleep_nsecs(1e6);
+ T1 = get_nsecs();
+
+ printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0);
+}
+
+#define FILL_FIELD(ptr, field, event, data) \
+ ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data)
+
+#define FILL_ARRAY(ptr, array, event, data) \
+do { \
+ void *__array = raw_field_ptr(event, #array, data); \
+ memcpy(ptr.array, __array, sizeof(ptr.array)); \
+} while(0)
+
+#define FILL_COMMON_FIELDS(ptr, event, data) \
+do { \
+ FILL_FIELD(ptr, common_type, event, data); \
+ FILL_FIELD(ptr, common_flags, event, data); \
+ FILL_FIELD(ptr, common_preempt_count, event, data); \
+ FILL_FIELD(ptr, common_pid, event, data); \
+ FILL_FIELD(ptr, common_tgid, event, data); \
+} while (0)
+
+
+
+struct trace_switch_event {
+ u32 size;
+
+ u16 common_type;
+ u8 common_flags;
+ u8 common_preempt_count;
+ u32 common_pid;
+ u32 common_tgid;
+
+ char prev_comm[16];
+ u32 prev_pid;
+ u32 prev_prio;
+ u64 prev_state;
+ char next_comm[16];
+ u32 next_pid;
+ u32 next_prio;
+};
+
+struct trace_runtime_event {
+ u32 size;
+
+ u16 common_type;
+ u8 common_flags;
+ u8 common_preempt_count;
+ u32 common_pid;
+ u32 common_tgid;
+
+ char comm[16];
+ u32 pid;
+ u64 runtime;
+ u64 vruntime;
+};
+
+struct trace_wakeup_event {
+ u32 size;
+
+ u16 common_type;
+ u8 common_flags;
+ u8 common_preempt_count;
+ u32 common_pid;
+ u32 common_tgid;
+
+ char comm[16];
+ u32 pid;
+
+ u32 prio;
+ u32 success;
+ u32 cpu;
+};
+
+struct trace_fork_event {
+ u32 size;
+
+ u16 common_type;
+ u8 common_flags;
+ u8 common_preempt_count;
+ u32 common_pid;
+ u32 common_tgid;
+
+ char parent_comm[16];
+ u32 parent_pid;
+ char child_comm[16];
+ u32 child_pid;
+};
+
+struct trace_migrate_task_event {
+ u32 size;
+
+ u16 common_type;
+ u8 common_flags;
+ u8 common_preempt_count;
+ u32 common_pid;
+ u32 common_tgid;
+
+ char comm[16];
+ u32 pid;
+
+ u32 prio;
+ u32 cpu;
+};
+
+struct trace_sched_handler {
+ void (*switch_event)(struct trace_switch_event *,
+ struct perf_session *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+
+ void (*runtime_event)(struct trace_runtime_event *,
+ struct perf_session *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+
+ void (*wakeup_event)(struct trace_wakeup_event *,
+ struct perf_session *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+
+ void (*fork_event)(struct trace_fork_event *,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+
+ void (*migrate_task_event)(struct trace_migrate_task_event *,
+ struct perf_session *session,
+ struct event *,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread);
+};
+
+
+static void
+replay_wakeup_event(struct trace_wakeup_event *wakeup_event,
+ struct perf_session *session __used,
+ struct event *event,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct task_desc *waker, *wakee;
+
+ if (verbose) {
+ printf("sched_wakeup event %p\n", event);
+
+ printf(" ... pid %d woke up %s/%d\n",
+ wakeup_event->common_pid,
+ wakeup_event->comm,
+ wakeup_event->pid);
+ }
+
+ waker = register_pid(wakeup_event->common_pid, "<unknown>");
+ wakee = register_pid(wakeup_event->pid, wakeup_event->comm);
+
+ add_sched_event_wakeup(waker, timestamp, wakee);
+}
+
+static u64 cpu_last_switched[MAX_CPUS];
+
+static void
+replay_switch_event(struct trace_switch_event *switch_event,
+ struct perf_session *session __used,
+ struct event *event,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread __used)
+{
+ struct task_desc *prev, *next;
+ u64 timestamp0;
+ s64 delta;
+
+ if (verbose)
+ printf("sched_switch event %p\n", event);
+
+ if (cpu >= MAX_CPUS || cpu < 0)
+ return;
+
+ timestamp0 = cpu_last_switched[cpu];
+ if (timestamp0)
+ delta = timestamp - timestamp0;
+ else
+ delta = 0;
+
+ if (delta < 0)
+ die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
+
+ if (verbose) {
+ printf(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n",
+ switch_event->prev_comm, switch_event->prev_pid,
+ switch_event->next_comm, switch_event->next_pid,
+ delta);
+ }
+
+ prev = register_pid(switch_event->prev_pid, switch_event->prev_comm);
+ next = register_pid(switch_event->next_pid, switch_event->next_comm);
+
+ cpu_last_switched[cpu] = timestamp;
+
+ add_sched_event_run(prev, timestamp, delta);
+ add_sched_event_sleep(prev, timestamp, switch_event->prev_state);
+}
+
+
+static void
+replay_fork_event(struct trace_fork_event *fork_event,
+ struct event *event,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ if (verbose) {
+ printf("sched_fork event %p\n", event);
+ printf("... parent: %s/%d\n", fork_event->parent_comm, fork_event->parent_pid);
+ printf("... child: %s/%d\n", fork_event->child_comm, fork_event->child_pid);
+ }
+ register_pid(fork_event->parent_pid, fork_event->parent_comm);
+ register_pid(fork_event->child_pid, fork_event->child_comm);
+}
+
+static struct trace_sched_handler replay_ops = {
+ .wakeup_event = replay_wakeup_event,
+ .switch_event = replay_switch_event,
+ .fork_event = replay_fork_event,
+};
+
+struct sort_dimension {
+ const char *name;
+ sort_fn_t cmp;
+ struct list_head list;
+};
+
+static LIST_HEAD(cmp_pid);
+
+static int
+thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)
+{
+ struct sort_dimension *sort;
+ int ret = 0;
+
+ BUG_ON(list_empty(list));
+
+ list_for_each_entry(sort, list, list) {
+ ret = sort->cmp(l, r);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct work_atoms *
+thread_atoms_search(struct rb_root *root, struct thread *thread,
+ struct list_head *sort_list)
+{
+ struct rb_node *node = root->rb_node;
+ struct work_atoms key = { .thread = thread };
+
+ while (node) {
+ struct work_atoms *atoms;
+ int cmp;
+
+ atoms = container_of(node, struct work_atoms, node);
+
+ cmp = thread_lat_cmp(sort_list, &key, atoms);
+ if (cmp > 0)
+ node = node->rb_left;
+ else if (cmp < 0)
+ node = node->rb_right;
+ else {
+ BUG_ON(thread != atoms->thread);
+ return atoms;
+ }
+ }
+ return NULL;
+}
+
+static void
+__thread_latency_insert(struct rb_root *root, struct work_atoms *data,
+ struct list_head *sort_list)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+ while (*new) {
+ struct work_atoms *this;
+ int cmp;
+
+ this = container_of(*new, struct work_atoms, node);
+ parent = *new;
+
+ cmp = thread_lat_cmp(sort_list, data, this);
+
+ if (cmp > 0)
+ new = &((*new)->rb_left);
+ else
+ new = &((*new)->rb_right);
+ }
+
+ rb_link_node(&data->node, parent, new);
+ rb_insert_color(&data->node, root);
+}
+
+static void thread_atoms_insert(struct thread *thread)
+{
+ struct work_atoms *atoms = zalloc(sizeof(*atoms));
+ if (!atoms)
+ die("No memory");
+
+ atoms->thread = thread;
+ INIT_LIST_HEAD(&atoms->work_list);
+ __thread_latency_insert(&atom_root, atoms, &cmp_pid);
+}
+
+static void
+latency_fork_event(struct trace_fork_event *fork_event __used,
+ struct event *event __used,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ /* should insert the newcomer */
+}
+
+__used
+static char sched_out_state(struct trace_switch_event *switch_event)
+{
+ const char *str = TASK_STATE_TO_CHAR_STR;
+
+ return str[switch_event->prev_state];
+}
+
+static void
+add_sched_out_event(struct work_atoms *atoms,
+ char run_state,
+ u64 timestamp)
+{
+ struct work_atom *atom = zalloc(sizeof(*atom));
+ if (!atom)
+ die("Non memory");
+
+ atom->sched_out_time = timestamp;
+
+ if (run_state == 'R') {
+ atom->state = THREAD_WAIT_CPU;
+ atom->wake_up_time = atom->sched_out_time;
+ }
+
+ list_add_tail(&atom->list, &atoms->work_list);
+}
+
+static void
+add_runtime_event(struct work_atoms *atoms, u64 delta, u64 timestamp __used)
+{
+ struct work_atom *atom;
+
+ BUG_ON(list_empty(&atoms->work_list));
+
+ atom = list_entry(atoms->work_list.prev, struct work_atom, list);
+
+ atom->runtime += delta;
+ atoms->total_runtime += delta;
+}
+
+static void
+add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
+{
+ struct work_atom *atom;
+ u64 delta;
+
+ if (list_empty(&atoms->work_list))
+ return;
+
+ atom = list_entry(atoms->work_list.prev, struct work_atom, list);
+
+ if (atom->state != THREAD_WAIT_CPU)
+ return;
+
+ if (timestamp < atom->wake_up_time) {
+ atom->state = THREAD_IGNORE;
+ return;
+ }
+
+ atom->state = THREAD_SCHED_IN;
+ atom->sched_in_time = timestamp;
+
+ delta = atom->sched_in_time - atom->wake_up_time;
+ atoms->total_lat += delta;
+ if (delta > atoms->max_lat) {
+ atoms->max_lat = delta;
+ atoms->max_lat_at = timestamp;
+ }
+ atoms->nb_atoms++;
+}
+
+static void
+latency_switch_event(struct trace_switch_event *switch_event,
+ struct perf_session *session,
+ struct event *event __used,
+ int cpu,
+ u64 timestamp,
+ struct thread *thread __used)
+{
+ struct work_atoms *out_events, *in_events;
+ struct thread *sched_out, *sched_in;
+ u64 timestamp0;
+ s64 delta;
+
+ BUG_ON(cpu >= MAX_CPUS || cpu < 0);
+
+ timestamp0 = cpu_last_switched[cpu];
+ cpu_last_switched[cpu] = timestamp;
+ if (timestamp0)
+ delta = timestamp - timestamp0;
+ else
+ delta = 0;
+
+ if (delta < 0)
+ die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
+
+
+ sched_out = perf_session__findnew(session, switch_event->prev_pid);
+ sched_in = perf_session__findnew(session, switch_event->next_pid);
+
+ out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
+ if (!out_events) {
+ thread_atoms_insert(sched_out);
+ out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
+ if (!out_events)
+ die("out-event: Internal tree error");
+ }
+ add_sched_out_event(out_events, sched_out_state(switch_event), timestamp);
+
+ in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid);
+ if (!in_events) {
+ thread_atoms_insert(sched_in);
+ in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid);
+ if (!in_events)
+ die("in-event: Internal tree error");
+ /*
+ * Take came in we have not heard about yet,
+ * add in an initial atom in runnable state:
+ */
+ add_sched_out_event(in_events, 'R', timestamp);
+ }
+ add_sched_in_event(in_events, timestamp);
+}
+
+static void
+latency_runtime_event(struct trace_runtime_event *runtime_event,
+ struct perf_session *session,
+ struct event *event __used,
+ int cpu,
+ u64 timestamp,
+ struct thread *this_thread __used)
+{
+ struct thread *thread = perf_session__findnew(session, runtime_event->pid);
+ struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
+
+ BUG_ON(cpu >= MAX_CPUS || cpu < 0);
+ if (!atoms) {
+ thread_atoms_insert(thread);
+ atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
+ if (!atoms)
+ die("in-event: Internal tree error");
+ add_sched_out_event(atoms, 'R', timestamp);
+ }
+
+ add_runtime_event(atoms, runtime_event->runtime, timestamp);
+}
+
+static void
+latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
+ struct perf_session *session,
+ struct event *__event __used,
+ int cpu __used,
+ u64 timestamp,
+ struct thread *thread __used)
+{
+ struct work_atoms *atoms;
+ struct work_atom *atom;
+ struct thread *wakee;
+
+ /* Note for later, it may be interesting to observe the failing cases */
+ if (!wakeup_event->success)
+ return;
+
+ wakee = perf_session__findnew(session, wakeup_event->pid);
+ atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
+ if (!atoms) {
+ thread_atoms_insert(wakee);
+ atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
+ if (!atoms)
+ die("wakeup-event: Internal tree error");
+ add_sched_out_event(atoms, 'S', timestamp);
+ }
+
+ BUG_ON(list_empty(&atoms->work_list));
+
+ atom = list_entry(atoms->work_list.prev, struct work_atom, list);
+
+ /*
+ * You WILL be missing events if you've recorded only
+ * one CPU, or are only looking at only one, so don't
+ * make useless noise.
+ */
+ if (profile_cpu == -1 && atom->state != THREAD_SLEEPING)
+ nr_state_machine_bugs++;
+
+ nr_timestamps++;
+ if (atom->sched_out_time > timestamp) {
+ nr_unordered_timestamps++;
+ return;
+ }
+
+ atom->state = THREAD_WAIT_CPU;
+ atom->wake_up_time = timestamp;
+}
+
+static void
+latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
+ struct perf_session *session,
+ struct event *__event __used,
+ int cpu __used,
+ u64 timestamp,
+ struct thread *thread __used)
+{
+ struct work_atoms *atoms;
+ struct work_atom *atom;
+ struct thread *migrant;
+
+ /*
+ * Only need to worry about migration when profiling one CPU.
+ */
+ if (profile_cpu == -1)
+ return;
+
+ migrant = perf_session__findnew(session, migrate_task_event->pid);
+ atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
+ if (!atoms) {
+ thread_atoms_insert(migrant);
+ register_pid(migrant->pid, migrant->comm);
+ atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
+ if (!atoms)
+ die("migration-event: Internal tree error");
+ add_sched_out_event(atoms, 'R', timestamp);
+ }
+
+ BUG_ON(list_empty(&atoms->work_list));
+
+ atom = list_entry(atoms->work_list.prev, struct work_atom, list);
+ atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp;
+
+ nr_timestamps++;
+
+ if (atom->sched_out_time > timestamp)
+ nr_unordered_timestamps++;
+}
+
+static struct trace_sched_handler lat_ops = {
+ .wakeup_event = latency_wakeup_event,
+ .switch_event = latency_switch_event,
+ .runtime_event = latency_runtime_event,
+ .fork_event = latency_fork_event,
+ .migrate_task_event = latency_migrate_task_event,
+};
+
+static void output_lat_thread(struct work_atoms *work_list)
+{
+ int i;
+ int ret;
+ u64 avg;
+
+ if (!work_list->nb_atoms)
+ return;
+ /*
+ * Ignore idle threads:
+ */
+ if (!strcmp(work_list->thread->comm, "swapper"))
+ return;
+
+ all_runtime += work_list->total_runtime;
+ all_count += work_list->nb_atoms;
+
+ ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid);
+
+ for (i = 0; i < 24 - ret; i++)
+ printf(" ");
+
+ avg = work_list->total_lat / work_list->nb_atoms;
+
+ printf("|%11.3f ms |%9" PRIu64 " | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n",
+ (double)work_list->total_runtime / 1e6,
+ work_list->nb_atoms, (double)avg / 1e6,
+ (double)work_list->max_lat / 1e6,
+ (double)work_list->max_lat_at / 1e9);
+}
+
+static int pid_cmp(struct work_atoms *l, struct work_atoms *r)
+{
+ if (l->thread->pid < r->thread->pid)
+ return -1;
+ if (l->thread->pid > r->thread->pid)
+ return 1;
+
+ return 0;
+}
+
+static struct sort_dimension pid_sort_dimension = {
+ .name = "pid",
+ .cmp = pid_cmp,
+};
+
+static int avg_cmp(struct work_atoms *l, struct work_atoms *r)
+{
+ u64 avgl, avgr;
+
+ if (!l->nb_atoms)
+ return -1;
+
+ if (!r->nb_atoms)
+ return 1;
+
+ avgl = l->total_lat / l->nb_atoms;
+ avgr = r->total_lat / r->nb_atoms;
+
+ if (avgl < avgr)
+ return -1;
+ if (avgl > avgr)
+ return 1;
+
+ return 0;
+}
+
+static struct sort_dimension avg_sort_dimension = {
+ .name = "avg",
+ .cmp = avg_cmp,
+};
+
+static int max_cmp(struct work_atoms *l, struct work_atoms *r)
+{
+ if (l->max_lat < r->max_lat)
+ return -1;
+ if (l->max_lat > r->max_lat)
+ return 1;
+
+ return 0;
+}
+
+static struct sort_dimension max_sort_dimension = {
+ .name = "max",
+ .cmp = max_cmp,
+};
+
+static int switch_cmp(struct work_atoms *l, struct work_atoms *r)
+{
+ if (l->nb_atoms < r->nb_atoms)
+ return -1;
+ if (l->nb_atoms > r->nb_atoms)
+ return 1;
+
+ return 0;
+}
+
+static struct sort_dimension switch_sort_dimension = {
+ .name = "switch",
+ .cmp = switch_cmp,
+};
+
+static int runtime_cmp(struct work_atoms *l, struct work_atoms *r)
+{
+ if (l->total_runtime < r->total_runtime)
+ return -1;
+ if (l->total_runtime > r->total_runtime)
+ return 1;
+
+ return 0;
+}
+
+static struct sort_dimension runtime_sort_dimension = {
+ .name = "runtime",
+ .cmp = runtime_cmp,
+};
+
+static struct sort_dimension *available_sorts[] = {
+ &pid_sort_dimension,
+ &avg_sort_dimension,
+ &max_sort_dimension,
+ &switch_sort_dimension,
+ &runtime_sort_dimension,
+};
+
+#define NB_AVAILABLE_SORTS (int)(sizeof(available_sorts) / sizeof(struct sort_dimension *))
+
+static LIST_HEAD(sort_list);
+
+static int sort_dimension__add(const char *tok, struct list_head *list)
+{
+ int i;
+
+ for (i = 0; i < NB_AVAILABLE_SORTS; i++) {
+ if (!strcmp(available_sorts[i]->name, tok)) {
+ list_add_tail(&available_sorts[i]->list, list);
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static void setup_sorting(void);
+
+static void sort_lat(void)
+{
+ struct rb_node *node;
+
+ for (;;) {
+ struct work_atoms *data;
+ node = rb_first(&atom_root);
+ if (!node)
+ break;
+
+ rb_erase(node, &atom_root);
+ data = rb_entry(node, struct work_atoms, node);
+ __thread_latency_insert(&sorted_atom_root, data, &sort_list);
+ }
+}
+
+static struct trace_sched_handler *trace_handler;
+
+static void
+process_sched_wakeup_event(void *data, struct perf_session *session,
+ struct event *event,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_wakeup_event wakeup_event;
+
+ FILL_COMMON_FIELDS(wakeup_event, event, data);
+
+ FILL_ARRAY(wakeup_event, comm, event, data);
+ FILL_FIELD(wakeup_event, pid, event, data);
+ FILL_FIELD(wakeup_event, prio, event, data);
+ FILL_FIELD(wakeup_event, success, event, data);
+ FILL_FIELD(wakeup_event, cpu, event, data);
+
+ if (trace_handler->wakeup_event)
+ trace_handler->wakeup_event(&wakeup_event, session, event,
+ cpu, timestamp, thread);
+}
+
+/*
+ * Track the current task - that way we can know whether there's any
+ * weird events, such as a task being switched away that is not current.
+ */
+static int max_cpu;
+
+static u32 curr_pid[MAX_CPUS] = { [0 ... MAX_CPUS-1] = -1 };
+
+static struct thread *curr_thread[MAX_CPUS];
+
+static char next_shortname1 = 'A';
+static char next_shortname2 = '0';
+
+static void
+map_switch_event(struct trace_switch_event *switch_event,
+ struct perf_session *session,
+ struct event *event __used,
+ int this_cpu,
+ u64 timestamp,
+ struct thread *thread __used)
+{
+ struct thread *sched_out, *sched_in;
+ int new_shortname;
+ u64 timestamp0;
+ s64 delta;
+ int cpu;
+
+ BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0);
+
+ if (this_cpu > max_cpu)
+ max_cpu = this_cpu;
+
+ timestamp0 = cpu_last_switched[this_cpu];
+ cpu_last_switched[this_cpu] = timestamp;
+ if (timestamp0)
+ delta = timestamp - timestamp0;
+ else
+ delta = 0;
+
+ if (delta < 0)
+ die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
+
+
+ sched_out = perf_session__findnew(session, switch_event->prev_pid);
+ sched_in = perf_session__findnew(session, switch_event->next_pid);
+
+ curr_thread[this_cpu] = sched_in;
+
+ printf(" ");
+
+ new_shortname = 0;
+ if (!sched_in->shortname[0]) {
+ sched_in->shortname[0] = next_shortname1;
+ sched_in->shortname[1] = next_shortname2;
+
+ if (next_shortname1 < 'Z') {
+ next_shortname1++;
+ } else {
+ next_shortname1='A';
+ if (next_shortname2 < '9') {
+ next_shortname2++;
+ } else {
+ next_shortname2='0';
+ }
+ }
+ new_shortname = 1;
+ }
+
+ for (cpu = 0; cpu <= max_cpu; cpu++) {
+ if (cpu != this_cpu)
+ printf(" ");
+ else
+ printf("*");
+
+ if (curr_thread[cpu]) {
+ if (curr_thread[cpu]->pid)
+ printf("%2s ", curr_thread[cpu]->shortname);
+ else
+ printf(". ");
+ } else
+ printf(" ");
+ }
+
+ printf(" %12.6f secs ", (double)timestamp/1e9);
+ if (new_shortname) {
+ printf("%s => %s:%d\n",
+ sched_in->shortname, sched_in->comm, sched_in->pid);
+ } else {
+ printf("\n");
+ }
+}
+
+
+static void
+process_sched_switch_event(void *data, struct perf_session *session,
+ struct event *event,
+ int this_cpu,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_switch_event switch_event;
+
+ FILL_COMMON_FIELDS(switch_event, event, data);
+
+ FILL_ARRAY(switch_event, prev_comm, event, data);
+ FILL_FIELD(switch_event, prev_pid, event, data);
+ FILL_FIELD(switch_event, prev_prio, event, data);
+ FILL_FIELD(switch_event, prev_state, event, data);
+ FILL_ARRAY(switch_event, next_comm, event, data);
+ FILL_FIELD(switch_event, next_pid, event, data);
+ FILL_FIELD(switch_event, next_prio, event, data);
+
+ if (curr_pid[this_cpu] != (u32)-1) {
+ /*
+ * Are we trying to switch away a PID that is
+ * not current?
+ */
+ if (curr_pid[this_cpu] != switch_event.prev_pid)
+ nr_context_switch_bugs++;
+ }
+ if (trace_handler->switch_event)
+ trace_handler->switch_event(&switch_event, session, event,
+ this_cpu, timestamp, thread);
+
+ curr_pid[this_cpu] = switch_event.next_pid;
+}
+
+static void
+process_sched_runtime_event(void *data, struct perf_session *session,
+ struct event *event,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_runtime_event runtime_event;
+
+ FILL_ARRAY(runtime_event, comm, event, data);
+ FILL_FIELD(runtime_event, pid, event, data);
+ FILL_FIELD(runtime_event, runtime, event, data);
+ FILL_FIELD(runtime_event, vruntime, event, data);
+
+ if (trace_handler->runtime_event)
+ trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread);
+}
+
+static void
+process_sched_fork_event(void *data,
+ struct event *event,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_fork_event fork_event;
+
+ FILL_COMMON_FIELDS(fork_event, event, data);
+
+ FILL_ARRAY(fork_event, parent_comm, event, data);
+ FILL_FIELD(fork_event, parent_pid, event, data);
+ FILL_ARRAY(fork_event, child_comm, event, data);
+ FILL_FIELD(fork_event, child_pid, event, data);
+
+ if (trace_handler->fork_event)
+ trace_handler->fork_event(&fork_event, event,
+ cpu, timestamp, thread);
+}
+
+static void
+process_sched_exit_event(struct event *event,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ if (verbose)
+ printf("sched_exit event %p\n", event);
+}
+
+static void
+process_sched_migrate_task_event(void *data, struct perf_session *session,
+ struct event *event,
+ int cpu __used,
+ u64 timestamp __used,
+ struct thread *thread __used)
+{
+ struct trace_migrate_task_event migrate_task_event;
+
+ FILL_COMMON_FIELDS(migrate_task_event, event, data);
+
+ FILL_ARRAY(migrate_task_event, comm, event, data);
+ FILL_FIELD(migrate_task_event, pid, event, data);
+ FILL_FIELD(migrate_task_event, prio, event, data);
+ FILL_FIELD(migrate_task_event, cpu, event, data);
+
+ if (trace_handler->migrate_task_event)
+ trace_handler->migrate_task_event(&migrate_task_event, session,
+ event, cpu, timestamp, thread);
+}
+
+static void
+process_raw_event(event_t *raw_event __used, struct perf_session *session,
+ void *data, int cpu, u64 timestamp, struct thread *thread)
+{
+ struct event *event;
+ int type;
+
+
+ type = trace_parse_common_type(data);
+ event = trace_find_event(type);
+
+ if (!strcmp(event->name, "sched_switch"))
+ process_sched_switch_event(data, session, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "sched_stat_runtime"))
+ process_sched_runtime_event(data, session, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "sched_wakeup"))
+ process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "sched_wakeup_new"))
+ process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "sched_process_fork"))
+ process_sched_fork_event(data, event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "sched_process_exit"))
+ process_sched_exit_event(event, cpu, timestamp, thread);
+ if (!strcmp(event->name, "sched_migrate_task"))
+ process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread);
+}
+
+static int process_sample_event(event_t *event, struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct thread *thread;
+
+ if (!(session->sample_type & PERF_SAMPLE_RAW))
+ return 0;
+
+ thread = perf_session__findnew(session, sample->pid);
+ if (thread == NULL) {
+ pr_debug("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+
+ if (profile_cpu != -1 && profile_cpu != (int)sample->cpu)
+ return 0;
+
+ process_raw_event(event, session, sample->raw_data, sample->cpu,
+ sample->time, thread);
+
+ return 0;
+}
+
+static struct perf_event_ops event_ops = {
+ .sample = process_sample_event,
+ .comm = event__process_comm,
+ .lost = event__process_lost,
+ .fork = event__process_task,
+ .ordered_samples = true,
+};
+
+static int read_events(void)
+{
+ int err = -EINVAL;
+ struct perf_session *session = perf_session__new(input_name, O_RDONLY,
+ 0, false, &event_ops);
+ if (session == NULL)
+ return -ENOMEM;
+
+ if (perf_session__has_traces(session, "record -R")) {
+ err = perf_session__process_events(session, &event_ops);
+ nr_events = session->hists.stats.nr_events[0];
+ nr_lost_events = session->hists.stats.total_lost;
+ nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST];
+ }
+
+ perf_session__delete(session);
+ return err;
+}
+
+static void print_bad_events(void)
+{
+ if (nr_unordered_timestamps && nr_timestamps) {
+ printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n",
+ (double)nr_unordered_timestamps/(double)nr_timestamps*100.0,
+ nr_unordered_timestamps, nr_timestamps);
+ }
+ if (nr_lost_events && nr_events) {
+ printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n",
+ (double)nr_lost_events/(double)nr_events*100.0,
+ nr_lost_events, nr_events, nr_lost_chunks);
+ }
+ if (nr_state_machine_bugs && nr_timestamps) {
+ printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)",
+ (double)nr_state_machine_bugs/(double)nr_timestamps*100.0,
+ nr_state_machine_bugs, nr_timestamps);
+ if (nr_lost_events)
+ printf(" (due to lost events?)");
+ printf("\n");
+ }
+ if (nr_context_switch_bugs && nr_timestamps) {
+ printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)",
+ (double)nr_context_switch_bugs/(double)nr_timestamps*100.0,
+ nr_context_switch_bugs, nr_timestamps);
+ if (nr_lost_events)
+ printf(" (due to lost events?)");
+ printf("\n");
+ }
+}
+
+static void __cmd_lat(void)
+{
+ struct rb_node *next;
+
+ setup_pager();
+ read_events();
+ sort_lat();
+
+ printf("\n ---------------------------------------------------------------------------------------------------------------\n");
+ printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n");
+ printf(" ---------------------------------------------------------------------------------------------------------------\n");
+
+ next = rb_first(&sorted_atom_root);
+
+ while (next) {
+ struct work_atoms *work_list;
+
+ work_list = rb_entry(next, struct work_atoms, node);
+ output_lat_thread(work_list);
+ next = rb_next(next);
+ }
+
+ printf(" -----------------------------------------------------------------------------------------\n");
+ printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n",
+ (double)all_runtime/1e6, all_count);
+
+ printf(" ---------------------------------------------------\n");
+
+ print_bad_events();
+ printf("\n");
+
+}
+
+static struct trace_sched_handler map_ops = {
+ .wakeup_event = NULL,
+ .switch_event = map_switch_event,
+ .runtime_event = NULL,
+ .fork_event = NULL,
+};
+
+static void __cmd_map(void)
+{
+ max_cpu = sysconf(_SC_NPROCESSORS_CONF);
+
+ setup_pager();
+ read_events();
+ print_bad_events();
+}
+
+static void __cmd_replay(void)
+{
+ unsigned long i;
+
+ calibrate_run_measurement_overhead();
+ calibrate_sleep_measurement_overhead();
+
+ test_calibrations();
+
+ read_events();
+
+ printf("nr_run_events: %ld\n", nr_run_events);
+ printf("nr_sleep_events: %ld\n", nr_sleep_events);
+ printf("nr_wakeup_events: %ld\n", nr_wakeup_events);
+
+ if (targetless_wakeups)
+ printf("target-less wakeups: %ld\n", targetless_wakeups);
+ if (multitarget_wakeups)
+ printf("multi-target wakeups: %ld\n", multitarget_wakeups);
+ if (nr_run_events_optimized)
+ printf("run atoms optimized: %ld\n",
+ nr_run_events_optimized);
+
+ print_task_traces();
+ add_cross_task_wakeups();
+
+ create_tasks();
+ printf("------------------------------------------------------------\n");
+ for (i = 0; i < replay_repeat; i++)
+ run_one_test();
+}
+
+
+static const char * const sched_usage[] = {
+ "perf sched [<options>] {record|latency|map|replay|trace}",
+ NULL
+};
+
+static const struct option sched_options[] = {
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_END()
+};
+
+static const char * const latency_usage[] = {
+ "perf sched latency [<options>]",
+ NULL
+};
+
+static const struct option latency_options[] = {
+ OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
+ "sort by key(s): runtime, switch, avg, max"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_INTEGER('C', "CPU", &profile_cpu,
+ "CPU to profile on"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_END()
+};
+
+static const char * const replay_usage[] = {
+ "perf sched replay [<options>]",
+ NULL
+};
+
+static const struct option replay_options[] = {
+ OPT_UINTEGER('r', "repeat", &replay_repeat,
+ "repeat the workload replay N times (-1: infinite)"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_END()
+};
+
+static void setup_sorting(void)
+{
+ char *tmp, *tok, *str = strdup(sort_order);
+
+ for (tok = strtok_r(str, ", ", &tmp);
+ tok; tok = strtok_r(NULL, ", ", &tmp)) {
+ if (sort_dimension__add(tok, &sort_list) < 0) {
+ error("Unknown --sort key: `%s'", tok);
+ usage_with_options(latency_usage, latency_options);
+ }
+ }
+
+ free(str);
+
+ sort_dimension__add("pid", &cmp_pid);
+}
+
+static const char *record_args[] = {
+ "record",
+ "-a",
+ "-R",
+ "-f",
+ "-m", "1024",
+ "-c", "1",
+ "-e", "sched:sched_switch",
+ "-e", "sched:sched_stat_wait",
+ "-e", "sched:sched_stat_sleep",
+ "-e", "sched:sched_stat_iowait",
+ "-e", "sched:sched_stat_runtime",
+ "-e", "sched:sched_process_exit",
+ "-e", "sched:sched_process_fork",
+ "-e", "sched:sched_wakeup",
+ "-e", "sched:sched_migrate_task",
+};
+
+static int __cmd_record(int argc, const char **argv)
+{
+ unsigned int rec_argc, i, j;
+ const char **rec_argv;
+
+ rec_argc = ARRAY_SIZE(record_args) + argc - 1;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+
+ if (rec_argv == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(record_args); i++)
+ rec_argv[i] = strdup(record_args[i]);
+
+ for (j = 1; j < (unsigned int)argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ BUG_ON(i != rec_argc);
+
+ return cmd_record(i, rec_argv, NULL);
+}
+
+int cmd_sched(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, sched_options, sched_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (!argc)
+ usage_with_options(sched_usage, sched_options);
+
+ /*
+ * Aliased to 'perf script' for now:
+ */
+ if (!strcmp(argv[0], "script"))
+ return cmd_script(argc, argv, prefix);
+
+ symbol__init();
+ if (!strncmp(argv[0], "rec", 3)) {
+ return __cmd_record(argc, argv);
+ } else if (!strncmp(argv[0], "lat", 3)) {
+ trace_handler = &lat_ops;
+ if (argc > 1) {
+ argc = parse_options(argc, argv, latency_options, latency_usage, 0);
+ if (argc)
+ usage_with_options(latency_usage, latency_options);
+ }
+ setup_sorting();
+ __cmd_lat();
+ } else if (!strcmp(argv[0], "map")) {
+ trace_handler = &map_ops;
+ setup_sorting();
+ __cmd_map();
+ } else if (!strncmp(argv[0], "rep", 3)) {
+ trace_handler = &replay_ops;
+ if (argc) {
+ argc = parse_options(argc, argv, replay_options, replay_usage, 0);
+ if (argc)
+ usage_with_options(replay_usage, replay_options);
+ }
+ __cmd_replay();
+ } else {
+ usage_with_options(sched_usage, sched_options);
+ }
+
+ return 0;
+}
diff --git a/smartt-perf/builtin-script.c b/smartt-perf/builtin-script.c
new file mode 100644
index 0000000..b766c2a
--- /dev/null
+++ b/smartt-perf/builtin-script.c
@@ -0,0 +1,821 @@
+#include "builtin.h"
+
+#include "perf.h"
+#include "util/cache.h"
+#include "util/debug.h"
+#include "util/exec_cmd.h"
+#include "util/header.h"
+#include "util/parse-options.h"
+#include "util/session.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/trace-event.h"
+#include "util/parse-options.h"
+#include "util/util.h"
+
+static char const *script_name;
+static char const *generate_script_lang;
+static bool debug_mode;
+static u64 last_timestamp;
+static u64 nr_unordered;
+extern const struct option record_options[];
+
+static int default_start_script(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
+{
+ return 0;
+}
+
+static int default_stop_script(void)
+{
+ return 0;
+}
+
+static int default_generate_script(const char *outfile __unused)
+{
+ return 0;
+}
+
+static struct scripting_ops default_scripting_ops = {
+ .start_script = default_start_script,
+ .stop_script = default_stop_script,
+ .process_event = print_event,
+ .generate_script = default_generate_script,
+};
+
+static struct scripting_ops *scripting_ops;
+
+static void setup_scripting(void)
+{
+ setup_perl_scripting();
+ setup_python_scripting();
+
+ scripting_ops = &default_scripting_ops;
+}
+
+static int cleanup_scripting(void)
+{
+ pr_debug("\nperf script stopped\n");
+
+ return scripting_ops->stop_script();
+}
+
+static char const *input_name = "perf.data";
+
+static int process_sample_event(event_t *event, struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct thread *thread = perf_session__findnew(session, event->ip.pid);
+
+ if (thread == NULL) {
+ pr_debug("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ if (session->sample_type & PERF_SAMPLE_RAW) {
+ if (debug_mode) {
+ if (sample->time < last_timestamp) {
+ pr_err("Samples misordered, previous: %" PRIu64
+ " this: %" PRIu64 "\n", last_timestamp,
+ sample->time);
+ nr_unordered++;
+ }
+ last_timestamp = sample->time;
+ return 0;
+ }
+ /*
+ * FIXME: better resolve from pid from the struct trace_entry
+ * field, although it should be the same than this perf
+ * event pid
+ */
+ scripting_ops->process_event(sample->cpu, sample->raw_data,
+ sample->raw_size,
+ sample->time, thread->comm);
+ }
+
+ session->hists.stats.total_period += sample->period;
+ return 0;
+}
+
+static struct perf_event_ops event_ops = {
+ .sample = process_sample_event,
+ .comm = event__process_comm,
+ .attr = event__process_attr,
+ .event_type = event__process_event_type,
+ .tracing_data = event__process_tracing_data,
+ .build_id = event__process_build_id,
+ .ordering_requires_timestamps = true,
+ .ordered_samples = true,
+};
+
+extern volatile int session_done;
+
+static void sig_handler(int sig __unused)
+{
+ session_done = 1;
+}
+
+static int __cmd_script(struct perf_session *session)
+{
+ int ret;
+
+ signal(SIGINT, sig_handler);
+
+ ret = perf_session__process_events(session, &event_ops);
+
+ if (debug_mode)
+ pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered);
+
+ return ret;
+}
+
+struct script_spec {
+ struct list_head node;
+ struct scripting_ops *ops;
+ char spec[0];
+};
+
+static LIST_HEAD(script_specs);
+
+static struct script_spec *script_spec__new(const char *spec,
+ struct scripting_ops *ops)
+{
+ struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
+
+ if (s != NULL) {
+ strcpy(s->spec, spec);
+ s->ops = ops;
+ }
+
+ return s;
+}
+
+static void script_spec__delete(struct script_spec *s)
+{
+ free(s->spec);
+ free(s);
+}
+
+static void script_spec__add(struct script_spec *s)
+{
+ list_add_tail(&s->node, &script_specs);
+}
+
+static struct script_spec *script_spec__find(const char *spec)
+{
+ struct script_spec *s;
+
+ list_for_each_entry(s, &script_specs, node)
+ if (strcasecmp(s->spec, spec) == 0)
+ return s;
+ return NULL;
+}
+
+static struct script_spec *script_spec__findnew(const char *spec,
+ struct scripting_ops *ops)
+{
+ struct script_spec *s = script_spec__find(spec);
+
+ if (s)
+ return s;
+
+ s = script_spec__new(spec, ops);
+ if (!s)
+ goto out_delete_spec;
+
+ script_spec__add(s);
+
+ return s;
+
+out_delete_spec:
+ script_spec__delete(s);
+
+ return NULL;
+}
+
+int script_spec_register(const char *spec, struct scripting_ops *ops)
+{
+ struct script_spec *s;
+
+ s = script_spec__find(spec);
+ if (s)
+ return -1;
+
+ s = script_spec__findnew(spec, ops);
+ if (!s)
+ return -1;
+
+ return 0;
+}
+
+static struct scripting_ops *script_spec__lookup(const char *spec)
+{
+ struct script_spec *s = script_spec__find(spec);
+ if (!s)
+ return NULL;
+
+ return s->ops;
+}
+
+static void list_available_languages(void)
+{
+ struct script_spec *s;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Scripting language extensions (used in "
+ "perf script -s [spec:]script.[spec]):\n\n");
+
+ list_for_each_entry(s, &script_specs, node)
+ fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name);
+
+ fprintf(stderr, "\n");
+}
+
+static int parse_scriptname(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ char spec[PATH_MAX];
+ const char *script, *ext;
+ int len;
+
+ if (strcmp(str, "lang") == 0) {
+ list_available_languages();
+ exit(0);
+ }
+
+ script = strchr(str, ':');
+ if (script) {
+ len = script - str;
+ if (len >= PATH_MAX) {
+ fprintf(stderr, "invalid language specifier");
+ return -1;
+ }
+ strncpy(spec, str, len);
+ spec[len] = '\0';
+ scripting_ops = script_spec__lookup(spec);
+ if (!scripting_ops) {
+ fprintf(stderr, "invalid language specifier");
+ return -1;
+ }
+ script++;
+ } else {
+ script = str;
+ ext = strrchr(script, '.');
+ if (!ext) {
+ fprintf(stderr, "invalid script extension");
+ return -1;
+ }
+ scripting_ops = script_spec__lookup(++ext);
+ if (!scripting_ops) {
+ fprintf(stderr, "invalid script extension");
+ return -1;
+ }
+ }
+
+ script_name = strdup(script);
+
+ return 0;
+}
+
+/* Helper function for filesystems that return a dent->d_type DT_UNKNOWN */
+static int is_directory(const char *base_path, const struct dirent *dent)
+{
+ char path[PATH_MAX];
+ struct stat st;
+
+ sprintf(path, "%s/%s", base_path, dent->d_name);
+ if (stat(path, &st))
+ return 0;
+
+ return S_ISDIR(st.st_mode);
+}
+
+#define for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next)\
+ while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \
+ lang_next) \
+ if ((lang_dirent.d_type == DT_DIR || \
+ (lang_dirent.d_type == DT_UNKNOWN && \
+ is_directory(scripts_path, &lang_dirent))) && \
+ (strcmp(lang_dirent.d_name, ".")) && \
+ (strcmp(lang_dirent.d_name, "..")))
+
+#define for_each_script(lang_path, lang_dir, script_dirent, script_next)\
+ while (!readdir_r(lang_dir, &script_dirent, &script_next) && \
+ script_next) \
+ if (script_dirent.d_type != DT_DIR && \
+ (script_dirent.d_type != DT_UNKNOWN || \
+ !is_directory(lang_path, &script_dirent)))
+
+
+#define RECORD_SUFFIX "-record"
+#define REPORT_SUFFIX "-report"
+
+struct script_desc {
+ struct list_head node;
+ char *name;
+ char *half_liner;
+ char *args;
+};
+
+static LIST_HEAD(script_descs);
+
+static struct script_desc *script_desc__new(const char *name)
+{
+ struct script_desc *s = zalloc(sizeof(*s));
+
+ if (s != NULL && name)
+ s->name = strdup(name);
+
+ return s;
+}
+
+static void script_desc__delete(struct script_desc *s)
+{
+ free(s->name);
+ free(s->half_liner);
+ free(s->args);
+ free(s);
+}
+
+static void script_desc__add(struct script_desc *s)
+{
+ list_add_tail(&s->node, &script_descs);
+}
+
+static struct script_desc *script_desc__find(const char *name)
+{
+ struct script_desc *s;
+
+ list_for_each_entry(s, &script_descs, node)
+ if (strcasecmp(s->name, name) == 0)
+ return s;
+ return NULL;
+}
+
+static struct script_desc *script_desc__findnew(const char *name)
+{
+ struct script_desc *s = script_desc__find(name);
+
+ if (s)
+ return s;
+
+ s = script_desc__new(name);
+ if (!s)
+ goto out_delete_desc;
+
+ script_desc__add(s);
+
+ return s;
+
+out_delete_desc:
+ script_desc__delete(s);
+
+ return NULL;
+}
+
+static const char *ends_with(const char *str, const char *suffix)
+{
+ size_t suffix_len = strlen(suffix);
+ const char *p = str;
+
+ if (strlen(str) > suffix_len) {
+ p = str + strlen(str) - suffix_len;
+ if (!strncmp(p, suffix, suffix_len))
+ return p;
+ }
+
+ return NULL;
+}
+
+static char *ltrim(char *str)
+{
+ int len = strlen(str);
+
+ while (len && isspace(*str)) {
+ len--;
+ str++;
+ }
+
+ return str;
+}
+
+static int read_script_info(struct script_desc *desc, const char *filename)
+{
+ char line[BUFSIZ], *p;
+ FILE *fp;
+
+ fp = fopen(filename, "r");
+ if (!fp)
+ return -1;
+
+ while (fgets(line, sizeof(line), fp)) {
+ p = ltrim(line);
+ if (strlen(p) == 0)
+ continue;
+ if (*p != '#')
+ continue;
+ p++;
+ if (strlen(p) && *p == '!')
+ continue;
+
+ p = ltrim(p);
+ if (strlen(p) && p[strlen(p) - 1] == '\n')
+ p[strlen(p) - 1] = '\0';
+
+ if (!strncmp(p, "description:", strlen("description:"))) {
+ p += strlen("description:");
+ desc->half_liner = strdup(ltrim(p));
+ continue;
+ }
+
+ if (!strncmp(p, "args:", strlen("args:"))) {
+ p += strlen("args:");
+ desc->args = strdup(ltrim(p));
+ continue;
+ }
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+static int list_available_scripts(const struct option *opt __used,
+ const char *s __used, int unset __used)
+{
+ struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ char scripts_path[MAXPATHLEN];
+ DIR *scripts_dir, *lang_dir;
+ char script_path[MAXPATHLEN];
+ char lang_path[MAXPATHLEN];
+ struct script_desc *desc;
+ char first_half[BUFSIZ];
+ char *script_root;
+ char *str;
+
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+ scripts_dir = opendir(scripts_path);
+ if (!scripts_dir)
+ return -1;
+
+ for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
+ snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
+ lang_dirent.d_name);
+ lang_dir = opendir(lang_path);
+ if (!lang_dir)
+ continue;
+
+ for_each_script(lang_path, lang_dir, script_dirent, script_next) {
+ script_root = strdup(script_dirent.d_name);
+ str = (char *)ends_with(script_root, REPORT_SUFFIX);
+ if (str) {
+ *str = '\0';
+ desc = script_desc__findnew(script_root);
+ snprintf(script_path, MAXPATHLEN, "%s/%s",
+ lang_path, script_dirent.d_name);
+ read_script_info(desc, script_path);
+ }
+ free(script_root);
+ }
+ }
+
+ fprintf(stdout, "List of available trace scripts:\n");
+ list_for_each_entry(desc, &script_descs, node) {
+ sprintf(first_half, "%s %s", desc->name,
+ desc->args ? desc->args : "");
+ fprintf(stdout, " %-36s %s\n", first_half,
+ desc->half_liner ? desc->half_liner : "");
+ }
+
+ exit(0);
+}
+
+static char *get_script_path(const char *script_root, const char *suffix)
+{
+ struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ char scripts_path[MAXPATHLEN];
+ char script_path[MAXPATHLEN];
+ DIR *scripts_dir, *lang_dir;
+ char lang_path[MAXPATHLEN];
+ char *str, *__script_root;
+ char *path = NULL;
+
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+ scripts_dir = opendir(scripts_path);
+ if (!scripts_dir)
+ return NULL;
+
+ for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
+ snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
+ lang_dirent.d_name);
+ lang_dir = opendir(lang_path);
+ if (!lang_dir)
+ continue;
+
+ for_each_script(lang_path, lang_dir, script_dirent, script_next) {
+ __script_root = strdup(script_dirent.d_name);
+ str = (char *)ends_with(__script_root, suffix);
+ if (str) {
+ *str = '\0';
+ if (strcmp(__script_root, script_root))
+ continue;
+ snprintf(script_path, MAXPATHLEN, "%s/%s",
+ lang_path, script_dirent.d_name);
+ path = strdup(script_path);
+ free(__script_root);
+ break;
+ }
+ free(__script_root);
+ }
+ }
+
+ return path;
+}
+
+static bool is_top_script(const char *script_path)
+{
+ return ends_with(script_path, "top") == NULL ? false : true;
+}
+
+static int has_required_arg(char *script_path)
+{
+ struct script_desc *desc;
+ int n_args = 0;
+ char *p;
+
+ desc = script_desc__new(NULL);
+
+ if (read_script_info(desc, script_path))
+ goto out;
+
+ if (!desc->args)
+ goto out;
+
+ for (p = desc->args; *p; p++)
+ if (*p == '<')
+ n_args++;
+out:
+ script_desc__delete(desc);
+
+ return n_args;
+}
+
+static const char * const script_usage[] = {
+ "perf script [<options>]",
+ "perf script [<options>] record <script> [<record-options>] <command>",
+ "perf script [<options>] report <script> [script-args]",
+ "perf script [<options>] <script> [<record-options>] <command>",
+ "perf script [<options>] <top-script> [script-args]",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('L', "Latency", &latency_format,
+ "show latency attributes (irqs/preemption disabled, etc)"),
+ OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
+ list_available_scripts),
+ OPT_CALLBACK('s', "script", NULL, "name",
+ "script file name (lang:script name, script name, or *)",
+ parse_scriptname),
+ OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
+ "generate perf-script.xx script in specified language"),
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_BOOLEAN('d', "debug-mode", &debug_mode,
+ "do various checks like samples ordering and lost events"),
+
+ OPT_END()
+};
+
+static bool have_cmd(int argc, const char **argv)
+{
+ char **__argv = malloc(sizeof(const char *) * argc);
+
+ if (!__argv)
+ die("malloc");
+ memcpy(__argv, argv, sizeof(const char *) * argc);
+ argc = parse_options(argc, (const char **)__argv, record_options,
+ NULL, PARSE_OPT_STOP_AT_NON_OPTION);
+ free(__argv);
+
+ return argc != 0;
+}
+
+int cmd_script(int argc, const char **argv, const char *prefix __used)
+{
+ char *rec_script_path = NULL;
+ char *rep_script_path = NULL;
+ struct perf_session *session;
+ char *script_path = NULL;
+ const char **__argv;
+ bool system_wide;
+ int i, j, err;
+
+ setup_scripting();
+
+ argc = parse_options(argc, argv, options, script_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
+ rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
+ if (!rec_script_path)
+ return cmd_record(argc, argv, NULL);
+ }
+
+ if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) {
+ rep_script_path = get_script_path(argv[1], REPORT_SUFFIX);
+ if (!rep_script_path) {
+ fprintf(stderr,
+ "Please specify a valid report script"
+ "(see 'perf script -l' for listing)\n");
+ return -1;
+ }
+ }
+
+ /* make sure PERF_EXEC_PATH is set for scripts */
+ perf_set_argv_exec_path(perf_exec_path());
+
+ if (argc && !script_name && !rec_script_path && !rep_script_path) {
+ int live_pipe[2];
+ int rep_args;
+ pid_t pid;
+
+ rec_script_path = get_script_path(argv[0], RECORD_SUFFIX);
+ rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
+
+ if (!rec_script_path && !rep_script_path) {
+ fprintf(stderr, " Couldn't find script %s\n\n See perf"
+ " script -l for available scripts.\n", argv[0]);
+ usage_with_options(script_usage, options);
+ }
+
+ if (is_top_script(argv[0])) {
+ rep_args = argc - 1;
+ } else {
+ int rec_args;
+
+ rep_args = has_required_arg(rep_script_path);
+ rec_args = (argc - 1) - rep_args;
+ if (rec_args < 0) {
+ fprintf(stderr, " %s script requires options."
+ "\n\n See perf script -l for available "
+ "scripts and options.\n", argv[0]);
+ usage_with_options(script_usage, options);
+ }
+ }
+
+ if (pipe(live_pipe) < 0) {
+ perror("failed to create pipe");
+ exit(-1);
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ perror("failed to fork");
+ exit(-1);
+ }
+
+ if (!pid) {
+ system_wide = true;
+ j = 0;
+
+ dup2(live_pipe[1], 1);
+ close(live_pipe[0]);
+
+ if (!is_top_script(argv[0]))
+ system_wide = !have_cmd(argc - rep_args,
+ &argv[rep_args]);
+
+ __argv = malloc((argc + 6) * sizeof(const char *));
+ if (!__argv)
+ die("malloc");
+
+ __argv[j++] = "/bin/sh";
+ __argv[j++] = rec_script_path;
+ if (system_wide)
+ __argv[j++] = "-a";
+ __argv[j++] = "-q";
+ __argv[j++] = "-o";
+ __argv[j++] = "-";
+ for (i = rep_args + 1; i < argc; i++)
+ __argv[j++] = argv[i];
+ __argv[j++] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ free(__argv);
+ exit(-1);
+ }
+
+ dup2(live_pipe[0], 0);
+ close(live_pipe[1]);
+
+ __argv = malloc((argc + 4) * sizeof(const char *));
+ if (!__argv)
+ die("malloc");
+ j = 0;
+ __argv[j++] = "/bin/sh";
+ __argv[j++] = rep_script_path;
+ for (i = 1; i < rep_args + 1; i++)
+ __argv[j++] = argv[i];
+ __argv[j++] = "-i";
+ __argv[j++] = "-";
+ __argv[j++] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ free(__argv);
+ exit(-1);
+ }
+
+ if (rec_script_path)
+ script_path = rec_script_path;
+ if (rep_script_path)
+ script_path = rep_script_path;
+
+ if (script_path) {
+ system_wide = false;
+ j = 0;
+
+ if (rec_script_path)
+ system_wide = !have_cmd(argc - 1, &argv[1]);
+
+ __argv = malloc((argc + 2) * sizeof(const char *));
+ if (!__argv)
+ die("malloc");
+ __argv[j++] = "/bin/sh";
+ __argv[j++] = script_path;
+ if (system_wide)
+ __argv[j++] = "-a";
+ for (i = 2; i < argc; i++)
+ __argv[j++] = argv[i];
+ __argv[j++] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ free(__argv);
+ exit(-1);
+ }
+
+ if (symbol__init() < 0)
+ return -1;
+ if (!script_name)
+ setup_pager();
+
+ session = perf_session__new(input_name, O_RDONLY, 0, false, &event_ops);
+ if (session == NULL)
+ return -ENOMEM;
+
+ if (strcmp(input_name, "-") &&
+ !perf_session__has_traces(session, "record -R"))
+ return -EINVAL;
+
+ if (generate_script_lang) {
+ struct stat perf_stat;
+
+ int input = open(input_name, O_RDONLY);
+ if (input < 0) {
+ perror("failed to open file");
+ exit(-1);
+ }
+
+ err = fstat(input, &perf_stat);
+ if (err < 0) {
+ perror("failed to stat file");
+ exit(-1);
+ }
+
+ if (!perf_stat.st_size) {
+ fprintf(stderr, "zero-sized file, nothing to do!\n");
+ exit(0);
+ }
+
+ scripting_ops = script_spec__lookup(generate_script_lang);
+ if (!scripting_ops) {
+ fprintf(stderr, "invalid language specifier");
+ return -1;
+ }
+
+ err = scripting_ops->generate_script("perf-script");
+ goto out;
+ }
+
+ if (script_name) {
+ err = scripting_ops->start_script(script_name, argc, argv);
+ if (err)
+ goto out;
+ pr_debug("perf script started with script %s\n\n", script_name);
+ }
+
+ err = __cmd_script(session);
+
+ perf_session__delete(session);
+ cleanup_scripting();
+out:
+ return err;
+}
diff --git a/smartt-perf/builtin-stat.c b/smartt-perf/builtin-stat.c
new file mode 100644
index 0000000..859f146
--- /dev/null
+++ b/smartt-perf/builtin-stat.c
@@ -0,0 +1,758 @@
+/*
+ * builtin-stat.c
+ *
+ * Builtin stat command: Give a precise performance counters summary
+ * overview about any workload, CPU or specific PID.
+ *
+ * Sample output:
+
+ $ perf stat ~/hackbench 10
+ Time: 0.104
+
+ Performance counter stats for '/home/mingo/hackbench':
+
+ 1255.538611 task clock ticks # 10.143 CPU utilization factor
+ 54011 context switches # 0.043 M/sec
+ 385 CPU migrations # 0.000 M/sec
+ 17755 pagefaults # 0.014 M/sec
+ 3808323185 CPU cycles # 3033.219 M/sec
+ 1575111190 instructions # 1254.530 M/sec
+ 17367895 cache references # 13.833 M/sec
+ 7674421 cache misses # 6.112 M/sec
+
+ Wall-clock time elapsed: 123.786620 msecs
+
+ *
+ * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
+ *
+ * Improvements and fixes by:
+ *
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Yanmin Zhang <yanmin.zhang@intel.com>
+ * Wu Fengguang <fengguang.wu@intel.com>
+ * Mike Galbraith <efault@gmx.de>
+ * Paul Mackerras <paulus@samba.org>
+ * Jaswinder Singh Rajput <jaswinder@kernel.org>
+ *
+ * Released under the GPL v2. (and only v2, not any later version)
+ */
+
+#include "perf.h"
+#include "builtin.h"
+#include "util/util.h"
+#include "util/parse-options.h"
+#include "util/parse-events.h"
+#include "util/event.h"
+#include "util/evsel.h"
+#include "util/debug.h"
+#include "util/header.h"
+#include "util/cpumap.h"
+#include "util/thread.h"
+#include "util/connect.h"
+
+#include <sys/prctl.h>
+#include <math.h>
+#include <locale.h>
+
+#define DEFAULT_SEPARATOR " "
+
+static struct perf_event_attr default_attrs[] = {
+
+ { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK },
+ { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES },
+ { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS },
+ { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS },
+
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES },
+
+};
+
+static bool system_wide = false;
+static struct cpu_map *cpus;
+static int run_idx = 0;
+
+static int run_count = 1;
+static bool no_inherit = false;
+static bool scale = true;
+static bool no_aggr = false;
+static pid_t target_pid = -1;
+static pid_t target_tid = -1;
+static struct thread_map *threads;
+static pid_t child_pid = -1;
+static bool null_run = false;
+static bool big_num = true;
+static int big_num_opt = -1;
+static const char *cpu_list;
+static const char *csv_sep = NULL;
+static bool csv_output = false;
+
+static volatile int done = 0;
+
+struct stats
+{
+ double n, mean, M2;
+};
+
+struct perf_stat {
+ struct stats res_stats[3];
+};
+
+static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
+{
+ evsel->priv = zalloc(sizeof(struct perf_stat));
+ return evsel->priv == NULL ? -ENOMEM : 0;
+}
+
+static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
+{
+ free(evsel->priv);
+ evsel->priv = NULL;
+}
+
+static void update_stats(struct stats *stats, u64 val)
+{
+ double delta;
+
+ stats->n++;
+ delta = val - stats->mean;
+ stats->mean += delta / stats->n;
+ stats->M2 += delta*(val - stats->mean);
+}
+
+static double avg_stats(struct stats *stats)
+{
+ return stats->mean;
+}
+
+/*
+ * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+ *
+ * (\Sum n_i^2) - ((\Sum n_i)^2)/n
+ * s^2 = -------------------------------
+ * n - 1
+ *
+ * http://en.wikipedia.org/wiki/Stddev
+ *
+ * The std dev of the mean is related to the std dev by:
+ *
+ * s
+ * s_mean = -------
+ * sqrt(n)
+ *
+ */
+static double stddev_stats(struct stats *stats)
+{
+ double variance = stats->M2 / (stats->n - 1);
+ double variance_mean = variance / stats->n;
+
+ return sqrt(variance_mean);
+}
+
+struct stats runtime_nsecs_stats[MAX_NR_CPUS];
+struct stats runtime_cycles_stats[MAX_NR_CPUS];
+struct stats runtime_branches_stats[MAX_NR_CPUS];
+struct stats walltime_nsecs_stats;
+
+static int create_perf_stat_counter(struct perf_evsel *evsel)
+{
+ struct perf_event_attr *attr = &evsel->attr;
+
+ if (scale)
+ attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
+ PERF_FORMAT_TOTAL_TIME_RUNNING;
+
+ if (system_wide)
+ return perf_evsel__open_per_cpu(evsel, cpus);
+
+ attr->inherit = !no_inherit;
+ if (target_pid == -1 && target_tid == -1) {
+ attr->disabled = 1;
+ attr->enable_on_exec = 1;
+ }
+
+ return perf_evsel__open_per_thread(evsel, threads);
+}
+
+/*
+ * Does the counter have nsecs as a unit?
+ */
+static inline int nsec_counter(struct perf_evsel *evsel)
+{
+ if (perf_evsel__match(evsel, SOFTWARE, SW_CPU_CLOCK) ||
+ perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Read out the results of a single counter:
+ * aggregate counts across CPUs in system-wide mode
+ */
+static int read_counter_aggr(struct perf_evsel *counter)
+{
+ struct perf_stat *ps = counter->priv;
+ u64 *count = counter->counts->aggr.values;
+ int i;
+
+ if (__perf_evsel__read(counter, cpus->nr, threads->nr, scale) < 0)
+ return -1;
+
+ for (i = 0; i < 3; i++)
+ update_stats(&ps->res_stats[i], count[i]);
+
+ if (verbose) {
+ fprintf(stderr, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ event_name(counter), count[0], count[1], count[2]);
+ }
+
+ /*
+ * Save the full runtime - to allow normalization during printout:
+ */
+ if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK))
+ update_stats(&runtime_nsecs_stats[0], count[0]);
+ if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
+ update_stats(&runtime_cycles_stats[0], count[0]);
+ if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
+ update_stats(&runtime_branches_stats[0], count[0]);
+
+ return 0;
+}
+
+/*
+ * Read out the results of a single counter:
+ * do not aggregate counts across CPUs in system-wide mode
+ */
+static int read_counter(struct perf_evsel *counter)
+{
+ u64 *count;
+ int cpu;
+
+ for (cpu = 0; cpu < cpus->nr; cpu++) {
+ if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0)
+ return -1;
+
+ count = counter->counts->cpu[cpu].values;
+
+ if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK))
+ update_stats(&runtime_nsecs_stats[cpu], count[0]);
+ if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
+ update_stats(&runtime_cycles_stats[cpu], count[0]);
+ if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
+ update_stats(&runtime_branches_stats[cpu], count[0]);
+ }
+
+ return 0;
+}
+
+static int run_perf_stat(int argc __used, const char **argv)
+{
+ unsigned long long t0, t1;
+ struct perf_evsel *counter;
+ int status = 0;
+ int child_ready_pipe[2], go_pipe[2];
+ const bool forks = (argc > 0);
+ char buf;
+
+ if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
+ perror("failed to create pipes");
+ exit(1);
+ }
+
+ if (forks) {
+ if ((child_pid = fork()) < 0)
+ perror("failed to fork");
+
+ if (!child_pid) {
+ close(child_ready_pipe[0]);
+ close(go_pipe[1]);
+ fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
+
+ /*
+ * Do a dummy execvp to get the PLT entry resolved,
+ * so we avoid the resolver overhead on the real
+ * execvp call.
+ */
+ execvp("", (char **)argv);
+
+ /*
+ * Tell the parent we're ready to go
+ */
+ close(child_ready_pipe[1]);
+
+ /*
+ * Wait until the parent tells us to go.
+ */
+ if (read(go_pipe[0], &buf, 1) == -1)
+ perror("unable to read pipe");
+
+ execvp(argv[0], (char **)argv);
+
+ perror(argv[0]);
+ exit(-1);
+ }
+
+ if (target_tid == -1 && target_pid == -1 && !system_wide)
+ threads->map[0] = child_pid;
+
+ /*
+ * Wait for the child to be ready to exec.
+ */
+ close(child_ready_pipe[1]);
+ close(go_pipe[0]);
+ if (read(child_ready_pipe[0], &buf, 1) == -1)
+ perror("unable to read pipe");
+ close(child_ready_pipe[0]);
+ }
+
+ list_for_each_entry(counter, &evsel_list, node) {
+ if (create_perf_stat_counter(counter) < 0) {
+ if (errno == -EPERM || errno == -EACCES) {
+ error("You may not have permission to collect %sstats.\n"
+ "\t Consider tweaking"
+ " /proc/sys/kernel/perf_event_paranoid or running as root.",
+ system_wide ? "system-wide " : "");
+ } else if (errno == ENOENT) {
+ error("%s event is not supported. ", event_name(counter));
+ } else {
+ error("open_counter returned with %d (%s). "
+ "/bin/dmesg may provide additional information.\n",
+ errno, strerror(errno));
+ }
+ if (child_pid != -1)
+ kill(child_pid, SIGTERM);
+ die("Not all events could be opened.\n");
+ return -1;
+ }
+ }
+
+ /*
+ * Enable counters and exec the command:
+ */
+ t0 = rdclock();
+
+ if (forks) {
+ close(go_pipe[1]);
+ wait(&status);
+ } else {
+ while(!done) sleep(1);
+ }
+
+ t1 = rdclock();
+
+ update_stats(&walltime_nsecs_stats, t1 - t0);
+
+ if (no_aggr) {
+ list_for_each_entry(counter, &evsel_list, node) {
+ read_counter(counter);
+ perf_evsel__close_fd(counter, cpus->nr, 1);
+ }
+ } else {
+ list_for_each_entry(counter, &evsel_list, node) {
+ read_counter_aggr(counter);
+ perf_evsel__close_fd(counter, cpus->nr, threads->nr);
+ }
+ }
+
+ return WEXITSTATUS(status);
+}
+
+static void print_noise(struct perf_evsel *evsel, double avg)
+{
+ struct perf_stat *ps;
+
+ if (run_count == 1)
+ return;
+
+ ps = evsel->priv;
+ fprintf(stderr, " ( +- %7.3f%% )",
+ 100 * stddev_stats(&ps->res_stats[0]) / avg);
+}
+
+static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
+{
+ double msecs = avg / 1e6;
+ char cpustr[16] = { '\0', };
+ const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-24s";
+
+ if (no_aggr)
+ sprintf(cpustr, "CPU%*d%s",
+ csv_output ? 0 : -4,
+ cpus->map[cpu], csv_sep);
+
+ fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel));
+
+ if (csv_output)
+ return;
+
+ if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK))
+ fprintf(stderr, " # %10.3f CPUs ",
+ avg / avg_stats(&walltime_nsecs_stats));
+}
+
+static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
+{
+ double total, ratio = 0.0;
+ char cpustr[16] = { '\0', };
+ const char *fmt;
+
+ if (csv_output)
+ fmt = "%s%.0f%s%s";
+ else if (big_num)
+ fmt = "%s%'18.0f%s%-24s";
+ else
+ fmt = "%s%18.0f%s%-24s";
+
+ if (no_aggr)
+ sprintf(cpustr, "CPU%*d%s",
+ csv_output ? 0 : -4,
+ cpus->map[cpu], csv_sep);
+ else
+ cpu = 0;
+
+// fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(evsel));
+ SMARTTperf_StatMetrics(&avg, event_name(evsel));
+
+ if (csv_output)
+ return;
+
+ if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
+ total = avg_stats(&runtime_cycles_stats[cpu]);
+
+ if (total)
+ ratio = avg / total;
+
+// fprintf(stderr, " # %10.3f IPC ", ratio);
+ } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) &&
+ runtime_branches_stats[cpu].n != 0) {
+ total = avg_stats(&runtime_branches_stats[cpu]);
+
+ if (total)
+ ratio = avg * 100 / total;
+
+// fprintf(stderr, " # %10.3f %% ", ratio);
+
+ } else if (runtime_nsecs_stats[cpu].n != 0) {
+ total = avg_stats(&runtime_nsecs_stats[cpu]);
+
+ if (total)
+ ratio = 1000.0 * avg / total;
+
+// fprintf(stderr, " # %10.3f M/sec", ratio);
+ }
+}
+
+/*
+ * Print out the results of a single counter:
+ * aggregated counts in system-wide mode
+ */
+static void print_counter_aggr(struct perf_evsel *counter)
+{
+ struct perf_stat *ps = counter->priv;
+ double avg = avg_stats(&ps->res_stats[0]);
+ int scaled = counter->counts->scaled;
+
+ if (scaled == -1) {
+// fprintf(stderr, "%*s%s%-24s\n",
+// csv_output ? 0 : 18,
+// "<not counted>", csv_sep, event_name(counter));
+ return;
+ }
+
+ if (nsec_counter(counter))
+ nsec_printout(-1, counter, avg);
+ else
+ abs_printout(-1, counter, avg);
+
+ if (csv_output) {
+// fputc('\n', stderr);
+ return;
+ }
+
+ print_noise(counter, avg);
+
+ if (scaled) {
+ double avg_enabled, avg_running;
+
+ avg_enabled = avg_stats(&ps->res_stats[1]);
+ avg_running = avg_stats(&ps->res_stats[2]);
+
+// fprintf(stderr, " (scaled from %.2f%%)",
+// 100 * avg_running / avg_enabled);
+ }
+
+// fprintf(stderr, "\n");
+}
+
+/*
+ * Print out the results of a single counter:
+ * does not use aggregated count in system-wide
+ */
+static void print_counter(struct perf_evsel *counter)
+{
+ u64 ena, run, val;
+ int cpu;
+
+ for (cpu = 0; cpu < cpus->nr; cpu++) {
+ val = counter->counts->cpu[cpu].val;
+ ena = counter->counts->cpu[cpu].ena;
+ run = counter->counts->cpu[cpu].run;
+ if (run == 0 || ena == 0) {
+ fprintf(stderr, "CPU%*d%s%*s%s%-24s",
+ csv_output ? 0 : -4,
+ cpus->map[cpu], csv_sep,
+ csv_output ? 0 : 18,
+ "<not counted>", csv_sep,
+ event_name(counter));
+
+// fprintf(stderr, "\n");
+ continue;
+ }
+
+ if (nsec_counter(counter))
+ nsec_printout(cpu, counter, val);
+ else
+ abs_printout(cpu, counter, val);
+
+ if (!csv_output) {
+ print_noise(counter, 1.0);
+
+ if (run != ena) {
+// fprintf(stderr, " (scaled from %.2f%%)",
+// 100.0 * run / ena);
+ }
+ }
+// fprintf(stderr, "\n");
+ }
+}
+
+static void print_stat(int argc, const char **argv)
+{
+ struct perf_evsel *counter;
+ int i;
+
+ fflush(stdout);
+ SMARTTperf_Connect();
+
+ if (!csv_output) {
+// fprintf(stderr, "\n");
+// fprintf(stderr, " Performance counter stats for ");
+ if(target_pid == -1 && target_tid == -1) {
+ fprintf(stderr, "\'%s", argv[0]);
+ for (i = 1; i < argc; i++)
+ fprintf(stderr, " %s", argv[i]);
+ } else if (target_pid != -1)
+ {
+// fprintf(stderr, "process id \'%d", target_pid);
+ SMARTTperf_StatPid(&target_pid);
+ }
+ else
+ fprintf(stderr, "thread id \'%d", target_tid);
+
+// fprintf(stderr, "\'");
+ if (run_count > 1)
+ fprintf(stderr, " (%d runs)", run_count);
+// fprintf(stderr, ":\n\n");
+ }
+
+ if (no_aggr) {
+ list_for_each_entry(counter, &evsel_list, node)
+ print_counter(counter);
+ } else {
+ list_for_each_entry(counter, &evsel_list, node)
+ print_counter_aggr(counter);
+ }
+
+ if (!csv_output) {
+// fprintf(stderr, "\n");
+// fprintf(stderr, " %18.9f seconds time elapsed",
+// avg_stats(&walltime_nsecs_stats)/1e9);
+ if (run_count > 1) {
+ fprintf(stderr, " ( +- %7.3f%% )",
+ 100*stddev_stats(&walltime_nsecs_stats) /
+ avg_stats(&walltime_nsecs_stats));
+ }
+// fprintf(stderr, "\n\n");
+ SMARTTperf_SendData();
+ }
+}
+
+static volatile int signr = -1;
+
+static void skip_signal(int signo)
+{
+ if(child_pid == -1)
+ done = 1;
+
+ signr = signo;
+}
+
+static void sig_atexit(void)
+{
+ if (child_pid != -1)
+ kill(child_pid, SIGTERM);
+
+ if (signr == -1)
+ return;
+
+ signal(signr, SIG_DFL);
+ kill(getpid(), signr);
+}
+
+static const char * const stat_usage[] = {
+ "perf stat [<options>] [<command>]",
+ NULL
+};
+
+static int stat__set_big_num(const struct option *opt __used,
+ const char *s __used, int unset)
+{
+ big_num_opt = unset ? 0 : 1;
+ return 0;
+}
+
+static const struct option options[] = {
+ OPT_CALLBACK('e', "event", NULL, "event",
+ "event selector. use 'perf list' to list available events",
+ parse_events),
+ OPT_BOOLEAN('i', "no-inherit", &no_inherit,
+ "child tasks do not inherit counters"),
+ OPT_INTEGER('p', "pid", &target_pid,
+ "stat events on existing process id"),
+ OPT_INTEGER('t', "tid", &target_tid,
+ "stat events on existing thread id"),
+ OPT_BOOLEAN('a', "all-cpus", &system_wide,
+ "system-wide collection from all CPUs"),
+ OPT_BOOLEAN('c', "scale", &scale,
+ "scale/normalize counters"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show counter open errors, etc)"),
+ OPT_INTEGER('r', "repeat", &run_count,
+ "repeat command and print average + stddev (max: 100)"),
+ OPT_BOOLEAN('n', "null", &null_run,
+ "null run - dont start any counters"),
+ OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL,
+ "print large numbers with thousands\' separators",
+ stat__set_big_num),
+ OPT_STRING('C', "cpu", &cpu_list, "cpu",
+ "list of cpus to monitor in system-wide"),
+ OPT_BOOLEAN('A', "no-aggr", &no_aggr,
+ "disable CPU count aggregation"),
+ OPT_STRING('x', "field-separator", &csv_sep, "separator",
+ "print counts with custom separator"),
+ OPT_END()
+};
+
+int cmd_stat(int argc, const char **argv, const char *prefix __used)
+{
+ struct perf_evsel *pos;
+ int status = -ENOMEM;
+
+ setlocale(LC_ALL, "");
+
+ argc = parse_options(argc, argv, options, stat_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (csv_sep)
+ csv_output = true;
+ else
+ csv_sep = DEFAULT_SEPARATOR;
+
+ /*
+ * let the spreadsheet do the pretty-printing
+ */
+ if (csv_output) {
+ /* User explicitely passed -B? */
+ if (big_num_opt == 1) {
+ fprintf(stderr, "-B option not supported with -x\n");
+ usage_with_options(stat_usage, options);
+ } else /* Nope, so disable big number formatting */
+ big_num = false;
+ } else if (big_num_opt == 0) /* User passed --no-big-num */
+ big_num = false;
+
+ if (!argc && target_pid == -1 && target_tid == -1)
+ usage_with_options(stat_usage, options);
+ if (run_count <= 0)
+ usage_with_options(stat_usage, options);
+
+ /* no_aggr is for system-wide only */
+ if (no_aggr && !system_wide)
+ usage_with_options(stat_usage, options);
+
+ /* Set attrs and nr_counters if no event is selected and !null_run */
+ if (!null_run && !nr_counters) {
+ size_t c;
+
+ nr_counters = ARRAY_SIZE(default_attrs);
+
+ for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) {
+ pos = perf_evsel__new(&default_attrs[c],
+ nr_counters);
+ if (pos == NULL)
+ goto out;
+ list_add(&pos->node, &evsel_list);
+ }
+ }
+
+ if (target_pid != -1)
+ target_tid = target_pid;
+
+ threads = thread_map__new(target_pid, target_tid);
+ if (threads == NULL) {
+ pr_err("Problems finding threads of monitor\n");
+ usage_with_options(stat_usage, options);
+ }
+
+ if (system_wide)
+ cpus = cpu_map__new(cpu_list);
+ else
+ cpus = cpu_map__dummy_new();
+
+ if (cpus == NULL) {
+ perror("failed to parse CPUs map");
+ usage_with_options(stat_usage, options);
+ return -1;
+ }
+
+ list_for_each_entry(pos, &evsel_list, node) {
+ if (perf_evsel__alloc_stat_priv(pos) < 0 ||
+ perf_evsel__alloc_counts(pos, cpus->nr) < 0 ||
+ perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
+ goto out_free_fd;
+ }
+
+ /*
+ * We dont want to block the signals - that would cause
+ * child tasks to inherit that and Ctrl-C would not work.
+ * What we want is for Ctrl-C to work in the exec()-ed
+ * task, but being ignored by perf stat itself:
+ */
+ atexit(sig_atexit);
+ signal(SIGINT, skip_signal);
+ signal(SIGALRM, skip_signal);
+ signal(SIGABRT, skip_signal);
+
+ status = 0;
+ for (run_idx = 0; run_idx < run_count; run_idx++) {
+ if (run_count != 1 && verbose)
+ fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx + 1);
+ status = run_perf_stat(argc, argv);
+ }
+
+ if (status != -1)
+ print_stat(argc, argv);
+out_free_fd:
+ list_for_each_entry(pos, &evsel_list, node)
+ perf_evsel__free_stat_priv(pos);
+ perf_evsel_list__delete();
+out:
+ thread_map__delete(threads);
+ threads = NULL;
+ return status;
+}
diff --git a/smartt-perf/builtin-test.c b/smartt-perf/builtin-test.c
new file mode 100644
index 0000000..5dcdba6
--- /dev/null
+++ b/smartt-perf/builtin-test.c
@@ -0,0 +1,507 @@
+/*
+ * builtin-test.c
+ *
+ * Builtin regression testing command: ever growing number of sanity tests
+ */
+#include "builtin.h"
+
+#include "util/cache.h"
+#include "util/debug.h"
+#include "util/parse-options.h"
+#include "util/session.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+
+static long page_size;
+
+static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym)
+{
+ bool *visited = symbol__priv(sym);
+ *visited = true;
+ return 0;
+}
+
+static int test__vmlinux_matches_kallsyms(void)
+{
+ int err = -1;
+ struct rb_node *nd;
+ struct symbol *sym;
+ struct map *kallsyms_map, *vmlinux_map;
+ struct machine kallsyms, vmlinux;
+ enum map_type type = MAP__FUNCTION;
+ struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
+
+ /*
+ * Step 1:
+ *
+ * Init the machines that will hold kernel, modules obtained from
+ * both vmlinux + .ko files and from /proc/kallsyms split by modules.
+ */
+ machine__init(&kallsyms, "", HOST_KERNEL_ID);
+ machine__init(&vmlinux, "", HOST_KERNEL_ID);
+
+ /*
+ * Step 2:
+ *
+ * Create the kernel maps for kallsyms and the DSO where we will then
+ * load /proc/kallsyms. Also create the modules maps from /proc/modules
+ * and find the .ko files that match them in /lib/modules/`uname -r`/.
+ */
+ if (machine__create_kernel_maps(&kallsyms) < 0) {
+ pr_debug("machine__create_kernel_maps ");
+ return -1;
+ }
+
+ /*
+ * Step 3:
+ *
+ * Load and split /proc/kallsyms into multiple maps, one per module.
+ */
+ if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) {
+ pr_debug("dso__load_kallsyms ");
+ goto out;
+ }
+
+ /*
+ * Step 4:
+ *
+ * kallsyms will be internally on demand sorted by name so that we can
+ * find the reference relocation * symbol, i.e. the symbol we will use
+ * to see if the running kernel was relocated by checking if it has the
+ * same value in the vmlinux file we load.
+ */
+ kallsyms_map = machine__kernel_map(&kallsyms, type);
+
+ sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL);
+ if (sym == NULL) {
+ pr_debug("dso__find_symbol_by_name ");
+ goto out;
+ }
+
+ ref_reloc_sym.addr = sym->start;
+
+ /*
+ * Step 5:
+ *
+ * Now repeat step 2, this time for the vmlinux file we'll auto-locate.
+ */
+ if (machine__create_kernel_maps(&vmlinux) < 0) {
+ pr_debug("machine__create_kernel_maps ");
+ goto out;
+ }
+
+ vmlinux_map = machine__kernel_map(&vmlinux, type);
+ map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym;
+
+ /*
+ * Step 6:
+ *
+ * Locate a vmlinux file in the vmlinux path that has a buildid that
+ * matches the one of the running kernel.
+ *
+ * While doing that look if we find the ref reloc symbol, if we find it
+ * we'll have its ref_reloc_symbol.unrelocated_addr and then
+ * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines
+ * to fixup the symbols.
+ */
+ if (machine__load_vmlinux_path(&vmlinux, type,
+ vmlinux_matches_kallsyms_filter) <= 0) {
+ pr_debug("machine__load_vmlinux_path ");
+ goto out;
+ }
+
+ err = 0;
+ /*
+ * Step 7:
+ *
+ * Now look at the symbols in the vmlinux DSO and check if we find all of them
+ * in the kallsyms dso. For the ones that are in both, check its names and
+ * end addresses too.
+ */
+ for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) {
+ struct symbol *pair, *first_pair;
+ bool backwards = true;
+
+ sym = rb_entry(nd, struct symbol, rb_node);
+
+ if (sym->start == sym->end)
+ continue;
+
+ first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL);
+ pair = first_pair;
+
+ if (pair && pair->start == sym->start) {
+next_pair:
+ if (strcmp(sym->name, pair->name) == 0) {
+ /*
+ * kallsyms don't have the symbol end, so we
+ * set that by using the next symbol start - 1,
+ * in some cases we get this up to a page
+ * wrong, trace_kmalloc when I was developing
+ * this code was one such example, 2106 bytes
+ * off the real size. More than that and we
+ * _really_ have a problem.
+ */
+ s64 skew = sym->end - pair->end;
+ if (llabs(skew) < page_size)
+ continue;
+
+ pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
+ sym->start, sym->name, sym->end, pair->end);
+ } else {
+ struct rb_node *nnd;
+detour:
+ nnd = backwards ? rb_prev(&pair->rb_node) :
+ rb_next(&pair->rb_node);
+ if (nnd) {
+ struct symbol *next = rb_entry(nnd, struct symbol, rb_node);
+
+ if (next->start == sym->start) {
+ pair = next;
+ goto next_pair;
+ }
+ }
+
+ if (backwards) {
+ backwards = false;
+ pair = first_pair;
+ goto detour;
+ }
+
+ pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
+ sym->start, sym->name, pair->name);
+ }
+ } else
+ pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name);
+
+ err = -1;
+ }
+
+ if (!verbose)
+ goto out;
+
+ pr_info("Maps only in vmlinux:\n");
+
+ for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
+ /*
+ * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
+ * the kernel will have the path for the vmlinux file being used,
+ * so use the short name, less descriptive but the same ("[kernel]" in
+ * both cases.
+ */
+ pair = map_groups__find_by_name(&kallsyms.kmaps, type,
+ (pos->dso->kernel ?
+ pos->dso->short_name :
+ pos->dso->name));
+ if (pair)
+ pair->priv = 1;
+ else
+ map__fprintf(pos, stderr);
+ }
+
+ pr_info("Maps in vmlinux with a different name in kallsyms:\n");
+
+ for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
+
+ pair = map_groups__find(&kallsyms.kmaps, type, pos->start);
+ if (pair == NULL || pair->priv)
+ continue;
+
+ if (pair->start == pos->start) {
+ pair->priv = 1;
+ pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
+ pos->start, pos->end, pos->pgoff, pos->dso->name);
+ if (pos->pgoff != pair->pgoff || pos->end != pair->end)
+ pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "",
+ pair->start, pair->end, pair->pgoff);
+ pr_info(" %s\n", pair->dso->name);
+ pair->priv = 1;
+ }
+ }
+
+ pr_info("Maps only in kallsyms:\n");
+
+ for (nd = rb_first(&kallsyms.kmaps.maps[type]);
+ nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+
+ if (!pos->priv)
+ map__fprintf(pos, stderr);
+ }
+out:
+ return err;
+}
+
+#include "util/cpumap.h"
+#include "util/evsel.h"
+#include <sys/types.h>
+
+static int trace_event__id(const char *event_name)
+{
+ char *filename;
+ int err = -1, fd;
+
+ if (asprintf(&filename,
+ "/sys/kernel/debug/tracing/events/syscalls/%s/id",
+ event_name) < 0)
+ return -1;
+
+ fd = open(filename, O_RDONLY);
+ if (fd >= 0) {
+ char id[16];
+ if (read(fd, id, sizeof(id)) > 0)
+ err = atoi(id);
+ close(fd);
+ }
+
+ free(filename);
+ return err;
+}
+
+static int test__open_syscall_event(void)
+{
+ int err = -1, fd;
+ struct thread_map *threads;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr;
+ unsigned int nr_open_calls = 111, i;
+ int id = trace_event__id("sys_enter_open");
+
+ if (id < 0) {
+ pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
+ return -1;
+ }
+
+ threads = thread_map__new(-1, getpid());
+ if (threads == NULL) {
+ pr_debug("thread_map__new\n");
+ return -1;
+ }
+
+ memset(&attr, 0, sizeof(attr));
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.config = id;
+ evsel = perf_evsel__new(&attr, 0);
+ if (evsel == NULL) {
+ pr_debug("perf_evsel__new\n");
+ goto out_thread_map_delete;
+ }
+
+ if (perf_evsel__open_per_thread(evsel, threads) < 0) {
+ pr_debug("failed to open counter: %s, "
+ "tweak /proc/sys/kernel/perf_event_paranoid?\n",
+ strerror(errno));
+ goto out_evsel_delete;
+ }
+
+ for (i = 0; i < nr_open_calls; ++i) {
+ fd = open("/etc/passwd", O_RDONLY);
+ close(fd);
+ }
+
+ if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) {
+ pr_debug("perf_evsel__open_read_on_cpu\n");
+ goto out_close_fd;
+ }
+
+ if (evsel->counts->cpu[0].val != nr_open_calls) {
+ pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
+ nr_open_calls, evsel->counts->cpu[0].val);
+ goto out_close_fd;
+ }
+
+ err = 0;
+out_close_fd:
+ perf_evsel__close_fd(evsel, 1, threads->nr);
+out_evsel_delete:
+ perf_evsel__delete(evsel);
+out_thread_map_delete:
+ thread_map__delete(threads);
+ return err;
+}
+
+#include <sched.h>
+
+static int test__open_syscall_event_on_all_cpus(void)
+{
+ int err = -1, fd, cpu;
+ struct thread_map *threads;
+ struct cpu_map *cpus;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr;
+ unsigned int nr_open_calls = 111, i;
+ cpu_set_t cpu_set;
+ int id = trace_event__id("sys_enter_open");
+
+ if (id < 0) {
+ pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
+ return -1;
+ }
+
+ threads = thread_map__new(-1, getpid());
+ if (threads == NULL) {
+ pr_debug("thread_map__new\n");
+ return -1;
+ }
+
+ cpus = cpu_map__new(NULL);
+ if (threads == NULL) {
+ pr_debug("thread_map__new\n");
+ return -1;
+ }
+
+
+ CPU_ZERO(&cpu_set);
+
+ memset(&attr, 0, sizeof(attr));
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.config = id;
+ evsel = perf_evsel__new(&attr, 0);
+ if (evsel == NULL) {
+ pr_debug("perf_evsel__new\n");
+ goto out_thread_map_delete;
+ }
+
+ if (perf_evsel__open(evsel, cpus, threads) < 0) {
+ pr_debug("failed to open counter: %s, "
+ "tweak /proc/sys/kernel/perf_event_paranoid?\n",
+ strerror(errno));
+ goto out_evsel_delete;
+ }
+
+ for (cpu = 0; cpu < cpus->nr; ++cpu) {
+ unsigned int ncalls = nr_open_calls + cpu;
+ /*
+ * XXX eventually lift this restriction in a way that
+ * keeps perf building on older glibc installations
+ * without CPU_ALLOC. 1024 cpus in 2010 still seems
+ * a reasonable upper limit tho :-)
+ */
+ if (cpus->map[cpu] >= CPU_SETSIZE) {
+ pr_debug("Ignoring CPU %d\n", cpus->map[cpu]);
+ continue;
+ }
+
+ CPU_SET(cpus->map[cpu], &cpu_set);
+ if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
+ pr_debug("sched_setaffinity() failed on CPU %d: %s ",
+ cpus->map[cpu],
+ strerror(errno));
+ goto out_close_fd;
+ }
+ for (i = 0; i < ncalls; ++i) {
+ fd = open("/etc/passwd", O_RDONLY);
+ close(fd);
+ }
+ CPU_CLR(cpus->map[cpu], &cpu_set);
+ }
+
+ /*
+ * Here we need to explicitely preallocate the counts, as if
+ * we use the auto allocation it will allocate just for 1 cpu,
+ * as we start by cpu 0.
+ */
+ if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) {
+ pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
+ goto out_close_fd;
+ }
+
+ for (cpu = 0; cpu < cpus->nr; ++cpu) {
+ unsigned int expected;
+
+ if (cpus->map[cpu] >= CPU_SETSIZE)
+ continue;
+
+ if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
+ pr_debug("perf_evsel__open_read_on_cpu\n");
+ goto out_close_fd;
+ }
+
+ expected = nr_open_calls + cpu;
+ if (evsel->counts->cpu[cpu].val != expected) {
+ pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
+ expected, cpus->map[cpu], evsel->counts->cpu[cpu].val);
+ goto out_close_fd;
+ }
+ }
+
+ err = 0;
+out_close_fd:
+ perf_evsel__close_fd(evsel, 1, threads->nr);
+out_evsel_delete:
+ perf_evsel__delete(evsel);
+out_thread_map_delete:
+ thread_map__delete(threads);
+ return err;
+}
+
+static struct test {
+ const char *desc;
+ int (*func)(void);
+} tests[] = {
+ {
+ .desc = "vmlinux symtab matches kallsyms",
+ .func = test__vmlinux_matches_kallsyms,
+ },
+ {
+ .desc = "detect open syscall event",
+ .func = test__open_syscall_event,
+ },
+ {
+ .desc = "detect open syscall event on all cpus",
+ .func = test__open_syscall_event_on_all_cpus,
+ },
+ {
+ .func = NULL,
+ },
+};
+
+static int __cmd_test(void)
+{
+ int i = 0;
+
+ page_size = sysconf(_SC_PAGE_SIZE);
+
+ while (tests[i].func) {
+ int err;
+ pr_info("%2d: %s:", i + 1, tests[i].desc);
+ pr_debug("\n--- start ---\n");
+ err = tests[i].func();
+ pr_debug("---- end ----\n%s:", tests[i].desc);
+ pr_info(" %s\n", err ? "FAILED!\n" : "Ok");
+ ++i;
+ }
+
+ return 0;
+}
+
+static const char * const test_usage[] = {
+ "perf test [<options>]",
+ NULL,
+};
+
+static const struct option test_options[] = {
+ OPT_INTEGER('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_END()
+};
+
+int cmd_test(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, test_options, test_usage, 0);
+ if (argc)
+ usage_with_options(test_usage, test_options);
+
+ symbol_conf.priv_size = sizeof(int);
+ symbol_conf.sort_by_name = true;
+ symbol_conf.try_vmlinux_path = true;
+
+ if (symbol__init() < 0)
+ return -1;
+
+ setup_pager();
+
+ return __cmd_test();
+}
diff --git a/smartt-perf/builtin-timechart.c b/smartt-perf/builtin-timechart.c
new file mode 100644
index 0000000..0ace786
--- /dev/null
+++ b/smartt-perf/builtin-timechart.c
@@ -0,0 +1,1104 @@
+/*
+ * builtin-timechart.c - make an svg timechart of system activity
+ *
+ * (C) Copyright 2009 Intel Corporation
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include "builtin.h"
+
+#include "util/util.h"
+
+#include "util/color.h"
+#include <linux/list.h>
+#include "util/cache.h"
+#include <linux/rbtree.h>
+#include "util/symbol.h"
+#include "util/callchain.h"
+#include "util/strlist.h"
+
+#include "perf.h"
+#include "util/header.h"
+#include "util/parse-options.h"
+#include "util/parse-events.h"
+#include "util/event.h"
+#include "util/session.h"
+#include "util/svghelper.h"
+
+#define SUPPORT_OLD_POWER_EVENTS 1
+#define PWR_EVENT_EXIT -1
+
+
+static char const *input_name = "perf.data";
+static char const *output_name = "output.svg";
+
+static unsigned int numcpus;
+static u64 min_freq; /* Lowest CPU frequency seen */
+static u64 max_freq; /* Highest CPU frequency seen */
+static u64 turbo_frequency;
+
+static u64 first_time, last_time;
+
+static bool power_only;
+
+
+struct per_pid;
+struct per_pidcomm;
+
+struct cpu_sample;
+struct power_event;
+struct wake_event;
+
+struct sample_wrapper;
+
+/*
+ * Datastructure layout:
+ * We keep an list of "pid"s, matching the kernels notion of a task struct.
+ * Each "pid" entry, has a list of "comm"s.
+ * this is because we want to track different programs different, while
+ * exec will reuse the original pid (by design).
+ * Each comm has a list of samples that will be used to draw
+ * final graph.
+ */
+
+struct per_pid {
+ struct per_pid *next;
+
+ int pid;
+ int ppid;
+
+ u64 start_time;
+ u64 end_time;
+ u64 total_time;
+ int display;
+
+ struct per_pidcomm *all;
+ struct per_pidcomm *current;
+};
+
+
+struct per_pidcomm {
+ struct per_pidcomm *next;
+
+ u64 start_time;
+ u64 end_time;
+ u64 total_time;
+
+ int Y;
+ int display;
+
+ long state;
+ u64 state_since;
+
+ char *comm;
+
+ struct cpu_sample *samples;
+};
+
+struct sample_wrapper {
+ struct sample_wrapper *next;
+
+ u64 timestamp;
+ unsigned char data[0];
+};
+
+#define TYPE_NONE 0
+#define TYPE_RUNNING 1
+#define TYPE_WAITING 2
+#define TYPE_BLOCKED 3
+
+struct cpu_sample {
+ struct cpu_sample *next;
+
+ u64 start_time;
+ u64 end_time;
+ int type;
+ int cpu;
+};
+
+static struct per_pid *all_data;
+
+#define CSTATE 1
+#define PSTATE 2
+
+struct power_event {
+ struct power_event *next;
+ int type;
+ int state;
+ u64 start_time;
+ u64 end_time;
+ int cpu;
+};
+
+struct wake_event {
+ struct wake_event *next;
+ int waker;
+ int wakee;
+ u64 time;
+};
+
+static struct power_event *power_events;
+static struct wake_event *wake_events;
+
+struct process_filter;
+struct process_filter {
+ char *name;
+ int pid;
+ struct process_filter *next;
+};
+
+static struct process_filter *process_filter;
+
+
+static struct per_pid *find_create_pid(int pid)
+{
+ struct per_pid *cursor = all_data;
+
+ while (cursor) {
+ if (cursor->pid == pid)
+ return cursor;
+ cursor = cursor->next;
+ }
+ cursor = malloc(sizeof(struct per_pid));
+ assert(cursor != NULL);
+ memset(cursor, 0, sizeof(struct per_pid));
+ cursor->pid = pid;
+ cursor->next = all_data;
+ all_data = cursor;
+ return cursor;
+}
+
+static void pid_set_comm(int pid, char *comm)
+{
+ struct per_pid *p;
+ struct per_pidcomm *c;
+ p = find_create_pid(pid);
+ c = p->all;
+ while (c) {
+ if (c->comm && strcmp(c->comm, comm) == 0) {
+ p->current = c;
+ return;
+ }
+ if (!c->comm) {
+ c->comm = strdup(comm);
+ p->current = c;
+ return;
+ }
+ c = c->next;
+ }
+ c = malloc(sizeof(struct per_pidcomm));
+ assert(c != NULL);
+ memset(c, 0, sizeof(struct per_pidcomm));
+ c->comm = strdup(comm);
+ p->current = c;
+ c->next = p->all;
+ p->all = c;
+}
+
+static void pid_fork(int pid, int ppid, u64 timestamp)
+{
+ struct per_pid *p, *pp;
+ p = find_create_pid(pid);
+ pp = find_create_pid(ppid);
+ p->ppid = ppid;
+ if (pp->current && pp->current->comm && !p->current)
+ pid_set_comm(pid, pp->current->comm);
+
+ p->start_time = timestamp;
+ if (p->current) {
+ p->current->start_time = timestamp;
+ p->current->state_since = timestamp;
+ }
+}
+
+static void pid_exit(int pid, u64 timestamp)
+{
+ struct per_pid *p;
+ p = find_create_pid(pid);
+ p->end_time = timestamp;
+ if (p->current)
+ p->current->end_time = timestamp;
+}
+
+static void
+pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
+{
+ struct per_pid *p;
+ struct per_pidcomm *c;
+ struct cpu_sample *sample;
+
+ p = find_create_pid(pid);
+ c = p->current;
+ if (!c) {
+ c = malloc(sizeof(struct per_pidcomm));
+ assert(c != NULL);
+ memset(c, 0, sizeof(struct per_pidcomm));
+ p->current = c;
+ c->next = p->all;
+ p->all = c;
+ }
+
+ sample = malloc(sizeof(struct cpu_sample));
+ assert(sample != NULL);
+ memset(sample, 0, sizeof(struct cpu_sample));
+ sample->start_time = start;
+ sample->end_time = end;
+ sample->type = type;
+ sample->next = c->samples;
+ sample->cpu = cpu;
+ c->samples = sample;
+
+ if (sample->type == TYPE_RUNNING && end > start && start > 0) {
+ c->total_time += (end-start);
+ p->total_time += (end-start);
+ }
+
+ if (c->start_time == 0 || c->start_time > start)
+ c->start_time = start;
+ if (p->start_time == 0 || p->start_time > start)
+ p->start_time = start;
+}
+
+#define MAX_CPUS 4096
+
+static u64 cpus_cstate_start_times[MAX_CPUS];
+static int cpus_cstate_state[MAX_CPUS];
+static u64 cpus_pstate_start_times[MAX_CPUS];
+static u64 cpus_pstate_state[MAX_CPUS];
+
+static int process_comm_event(event_t *event, struct sample_data *sample __used,
+ struct perf_session *session __used)
+{
+ pid_set_comm(event->comm.tid, event->comm.comm);
+ return 0;
+}
+
+static int process_fork_event(event_t *event, struct sample_data *sample __used,
+ struct perf_session *session __used)
+{
+ pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
+ return 0;
+}
+
+static int process_exit_event(event_t *event, struct sample_data *sample __used,
+ struct perf_session *session __used)
+{
+ pid_exit(event->fork.pid, event->fork.time);
+ return 0;
+}
+
+struct trace_entry {
+ unsigned short type;
+ unsigned char flags;
+ unsigned char preempt_count;
+ int pid;
+ int lock_depth;
+};
+
+#ifdef SUPPORT_OLD_POWER_EVENTS
+static int use_old_power_events;
+struct power_entry_old {
+ struct trace_entry te;
+ u64 type;
+ u64 value;
+ u64 cpu_id;
+};
+#endif
+
+struct power_processor_entry {
+ struct trace_entry te;
+ u32 state;
+ u32 cpu_id;
+};
+
+#define TASK_COMM_LEN 16
+struct wakeup_entry {
+ struct trace_entry te;
+ char comm[TASK_COMM_LEN];
+ int pid;
+ int prio;
+ int success;
+};
+
+/*
+ * trace_flag_type is an enumeration that holds different
+ * states when a trace occurs. These are:
+ * IRQS_OFF - interrupts were disabled
+ * IRQS_NOSUPPORT - arch does not support irqs_disabled_flags
+ * NEED_RESCED - reschedule is requested
+ * HARDIRQ - inside an interrupt handler
+ * SOFTIRQ - inside a softirq handler
+ */
+enum trace_flag_type {
+ TRACE_FLAG_IRQS_OFF = 0x01,
+ TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
+ TRACE_FLAG_NEED_RESCHED = 0x04,
+ TRACE_FLAG_HARDIRQ = 0x08,
+ TRACE_FLAG_SOFTIRQ = 0x10,
+};
+
+
+
+struct sched_switch {
+ struct trace_entry te;
+ char prev_comm[TASK_COMM_LEN];
+ int prev_pid;
+ int prev_prio;
+ long prev_state; /* Arjan weeps. */
+ char next_comm[TASK_COMM_LEN];
+ int next_pid;
+ int next_prio;
+};
+
+static void c_state_start(int cpu, u64 timestamp, int state)
+{
+ cpus_cstate_start_times[cpu] = timestamp;
+ cpus_cstate_state[cpu] = state;
+}
+
+static void c_state_end(int cpu, u64 timestamp)
+{
+ struct power_event *pwr;
+ pwr = malloc(sizeof(struct power_event));
+ if (!pwr)
+ return;
+ memset(pwr, 0, sizeof(struct power_event));
+
+ pwr->state = cpus_cstate_state[cpu];
+ pwr->start_time = cpus_cstate_start_times[cpu];
+ pwr->end_time = timestamp;
+ pwr->cpu = cpu;
+ pwr->type = CSTATE;
+ pwr->next = power_events;
+
+ power_events = pwr;
+}
+
+static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
+{
+ struct power_event *pwr;
+ pwr = malloc(sizeof(struct power_event));
+
+ if (new_freq > 8000000) /* detect invalid data */
+ return;
+
+ if (!pwr)
+ return;
+ memset(pwr, 0, sizeof(struct power_event));
+
+ pwr->state = cpus_pstate_state[cpu];
+ pwr->start_time = cpus_pstate_start_times[cpu];
+ pwr->end_time = timestamp;
+ pwr->cpu = cpu;
+ pwr->type = PSTATE;
+ pwr->next = power_events;
+
+ if (!pwr->start_time)
+ pwr->start_time = first_time;
+
+ power_events = pwr;
+
+ cpus_pstate_state[cpu] = new_freq;
+ cpus_pstate_start_times[cpu] = timestamp;
+
+ if ((u64)new_freq > max_freq)
+ max_freq = new_freq;
+
+ if (new_freq < min_freq || min_freq == 0)
+ min_freq = new_freq;
+
+ if (new_freq == max_freq - 1000)
+ turbo_frequency = max_freq;
+}
+
+static void
+sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
+{
+ struct wake_event *we;
+ struct per_pid *p;
+ struct wakeup_entry *wake = (void *)te;
+
+ we = malloc(sizeof(struct wake_event));
+ if (!we)
+ return;
+
+ memset(we, 0, sizeof(struct wake_event));
+ we->time = timestamp;
+ we->waker = pid;
+
+ if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
+ we->waker = -1;
+
+ we->wakee = wake->pid;
+ we->next = wake_events;
+ wake_events = we;
+ p = find_create_pid(we->wakee);
+
+ if (p && p->current && p->current->state == TYPE_NONE) {
+ p->current->state_since = timestamp;
+ p->current->state = TYPE_WAITING;
+ }
+ if (p && p->current && p->current->state == TYPE_BLOCKED) {
+ pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp);
+ p->current->state_since = timestamp;
+ p->current->state = TYPE_WAITING;
+ }
+}
+
+static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
+{
+ struct per_pid *p = NULL, *prev_p;
+ struct sched_switch *sw = (void *)te;
+
+
+ prev_p = find_create_pid(sw->prev_pid);
+
+ p = find_create_pid(sw->next_pid);
+
+ if (prev_p->current && prev_p->current->state != TYPE_NONE)
+ pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);
+ if (p && p->current) {
+ if (p->current->state != TYPE_NONE)
+ pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
+
+ p->current->state_since = timestamp;
+ p->current->state = TYPE_RUNNING;
+ }
+
+ if (prev_p->current) {
+ prev_p->current->state = TYPE_NONE;
+ prev_p->current->state_since = timestamp;
+ if (sw->prev_state & 2)
+ prev_p->current->state = TYPE_BLOCKED;
+ if (sw->prev_state == 0)
+ prev_p->current->state = TYPE_WAITING;
+ }
+}
+
+
+static int process_sample_event(event_t *event __used,
+ struct sample_data *sample,
+ struct perf_session *session)
+{
+ struct trace_entry *te;
+
+ if (session->sample_type & PERF_SAMPLE_TIME) {
+ if (!first_time || first_time > sample->time)
+ first_time = sample->time;
+ if (last_time < sample->time)
+ last_time = sample->time;
+ }
+
+ te = (void *)sample->raw_data;
+ if (session->sample_type & PERF_SAMPLE_RAW && sample->raw_size > 0) {
+ char *event_str;
+#ifdef SUPPORT_OLD_POWER_EVENTS
+ struct power_entry_old *peo;
+ peo = (void *)te;
+#endif
+ event_str = perf_header__find_event(te->type);
+
+ if (!event_str)
+ return 0;
+
+ if (sample->cpu > numcpus)
+ numcpus = sample->cpu;
+
+ if (strcmp(event_str, "power:cpu_idle") == 0) {
+ struct power_processor_entry *ppe = (void *)te;
+ if (ppe->state == (u32)PWR_EVENT_EXIT)
+ c_state_end(ppe->cpu_id, sample->time);
+ else
+ c_state_start(ppe->cpu_id, sample->time,
+ ppe->state);
+ }
+ else if (strcmp(event_str, "power:cpu_frequency") == 0) {
+ struct power_processor_entry *ppe = (void *)te;
+ p_state_change(ppe->cpu_id, sample->time, ppe->state);
+ }
+
+ else if (strcmp(event_str, "sched:sched_wakeup") == 0)
+ sched_wakeup(sample->cpu, sample->time, sample->pid, te);
+
+ else if (strcmp(event_str, "sched:sched_switch") == 0)
+ sched_switch(sample->cpu, sample->time, te);
+
+#ifdef SUPPORT_OLD_POWER_EVENTS
+ if (use_old_power_events) {
+ if (strcmp(event_str, "power:power_start") == 0)
+ c_state_start(peo->cpu_id, sample->time,
+ peo->value);
+
+ else if (strcmp(event_str, "power:power_end") == 0)
+ c_state_end(sample->cpu, sample->time);
+
+ else if (strcmp(event_str,
+ "power:power_frequency") == 0)
+ p_state_change(peo->cpu_id, sample->time,
+ peo->value);
+ }
+#endif
+ }
+ return 0;
+}
+
+/*
+ * After the last sample we need to wrap up the current C/P state
+ * and close out each CPU for these.
+ */
+static void end_sample_processing(void)
+{
+ u64 cpu;
+ struct power_event *pwr;
+
+ for (cpu = 0; cpu <= numcpus; cpu++) {
+ pwr = malloc(sizeof(struct power_event));
+ if (!pwr)
+ return;
+ memset(pwr, 0, sizeof(struct power_event));
+
+ /* C state */
+#if 0
+ pwr->state = cpus_cstate_state[cpu];
+ pwr->start_time = cpus_cstate_start_times[cpu];
+ pwr->end_time = last_time;
+ pwr->cpu = cpu;
+ pwr->type = CSTATE;
+ pwr->next = power_events;
+
+ power_events = pwr;
+#endif
+ /* P state */
+
+ pwr = malloc(sizeof(struct power_event));
+ if (!pwr)
+ return;
+ memset(pwr, 0, sizeof(struct power_event));
+
+ pwr->state = cpus_pstate_state[cpu];
+ pwr->start_time = cpus_pstate_start_times[cpu];
+ pwr->end_time = last_time;
+ pwr->cpu = cpu;
+ pwr->type = PSTATE;
+ pwr->next = power_events;
+
+ if (!pwr->start_time)
+ pwr->start_time = first_time;
+ if (!pwr->state)
+ pwr->state = min_freq;
+ power_events = pwr;
+ }
+}
+
+/*
+ * Sort the pid datastructure
+ */
+static void sort_pids(void)
+{
+ struct per_pid *new_list, *p, *cursor, *prev;
+ /* sort by ppid first, then by pid, lowest to highest */
+
+ new_list = NULL;
+
+ while (all_data) {
+ p = all_data;
+ all_data = p->next;
+ p->next = NULL;
+
+ if (new_list == NULL) {
+ new_list = p;
+ p->next = NULL;
+ continue;
+ }
+ prev = NULL;
+ cursor = new_list;
+ while (cursor) {
+ if (cursor->ppid > p->ppid ||
+ (cursor->ppid == p->ppid && cursor->pid > p->pid)) {
+ /* must insert before */
+ if (prev) {
+ p->next = prev->next;
+ prev->next = p;
+ cursor = NULL;
+ continue;
+ } else {
+ p->next = new_list;
+ new_list = p;
+ cursor = NULL;
+ continue;
+ }
+ }
+
+ prev = cursor;
+ cursor = cursor->next;
+ if (!cursor)
+ prev->next = p;
+ }
+ }
+ all_data = new_list;
+}
+
+
+static void draw_c_p_states(void)
+{
+ struct power_event *pwr;
+ pwr = power_events;
+
+ /*
+ * two pass drawing so that the P state bars are on top of the C state blocks
+ */
+ while (pwr) {
+ if (pwr->type == CSTATE)
+ svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
+ pwr = pwr->next;
+ }
+
+ pwr = power_events;
+ while (pwr) {
+ if (pwr->type == PSTATE) {
+ if (!pwr->state)
+ pwr->state = min_freq;
+ svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
+ }
+ pwr = pwr->next;
+ }
+}
+
+static void draw_wakeups(void)
+{
+ struct wake_event *we;
+ struct per_pid *p;
+ struct per_pidcomm *c;
+
+ we = wake_events;
+ while (we) {
+ int from = 0, to = 0;
+ char *task_from = NULL, *task_to = NULL;
+
+ /* locate the column of the waker and wakee */
+ p = all_data;
+ while (p) {
+ if (p->pid == we->waker || p->pid == we->wakee) {
+ c = p->all;
+ while (c) {
+ if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
+ if (p->pid == we->waker && !from) {
+ from = c->Y;
+ task_from = strdup(c->comm);
+ }
+ if (p->pid == we->wakee && !to) {
+ to = c->Y;
+ task_to = strdup(c->comm);
+ }
+ }
+ c = c->next;
+ }
+ c = p->all;
+ while (c) {
+ if (p->pid == we->waker && !from) {
+ from = c->Y;
+ task_from = strdup(c->comm);
+ }
+ if (p->pid == we->wakee && !to) {
+ to = c->Y;
+ task_to = strdup(c->comm);
+ }
+ c = c->next;
+ }
+ }
+ p = p->next;
+ }
+
+ if (!task_from) {
+ task_from = malloc(40);
+ sprintf(task_from, "[%i]", we->waker);
+ }
+ if (!task_to) {
+ task_to = malloc(40);
+ sprintf(task_to, "[%i]", we->wakee);
+ }
+
+ if (we->waker == -1)
+ svg_interrupt(we->time, to);
+ else if (from && to && abs(from - to) == 1)
+ svg_wakeline(we->time, from, to);
+ else
+ svg_partial_wakeline(we->time, from, task_from, to, task_to);
+ we = we->next;
+
+ free(task_from);
+ free(task_to);
+ }
+}
+
+static void draw_cpu_usage(void)
+{
+ struct per_pid *p;
+ struct per_pidcomm *c;
+ struct cpu_sample *sample;
+ p = all_data;
+ while (p) {
+ c = p->all;
+ while (c) {
+ sample = c->samples;
+ while (sample) {
+ if (sample->type == TYPE_RUNNING)
+ svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm);
+
+ sample = sample->next;
+ }
+ c = c->next;
+ }
+ p = p->next;
+ }
+}
+
+static void draw_process_bars(void)
+{
+ struct per_pid *p;
+ struct per_pidcomm *c;
+ struct cpu_sample *sample;
+ int Y = 0;
+
+ Y = 2 * numcpus + 2;
+
+ p = all_data;
+ while (p) {
+ c = p->all;
+ while (c) {
+ if (!c->display) {
+ c->Y = 0;
+ c = c->next;
+ continue;
+ }
+
+ svg_box(Y, c->start_time, c->end_time, "process");
+ sample = c->samples;
+ while (sample) {
+ if (sample->type == TYPE_RUNNING)
+ svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);
+ if (sample->type == TYPE_BLOCKED)
+ svg_box(Y, sample->start_time, sample->end_time, "blocked");
+ if (sample->type == TYPE_WAITING)
+ svg_waiting(Y, sample->start_time, sample->end_time);
+ sample = sample->next;
+ }
+
+ if (c->comm) {
+ char comm[256];
+ if (c->total_time > 5000000000) /* 5 seconds */
+ sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
+ else
+ sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
+
+ svg_text(Y, c->start_time, comm);
+ }
+ c->Y = Y;
+ Y++;
+ c = c->next;
+ }
+ p = p->next;
+ }
+}
+
+static void add_process_filter(const char *string)
+{
+ struct process_filter *filt;
+ int pid;
+
+ pid = strtoull(string, NULL, 10);
+ filt = malloc(sizeof(struct process_filter));
+ if (!filt)
+ return;
+
+ filt->name = strdup(string);
+ filt->pid = pid;
+ filt->next = process_filter;
+
+ process_filter = filt;
+}
+
+static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
+{
+ struct process_filter *filt;
+ if (!process_filter)
+ return 1;
+
+ filt = process_filter;
+ while (filt) {
+ if (filt->pid && p->pid == filt->pid)
+ return 1;
+ if (strcmp(filt->name, c->comm) == 0)
+ return 1;
+ filt = filt->next;
+ }
+ return 0;
+}
+
+static int determine_display_tasks_filtered(void)
+{
+ struct per_pid *p;
+ struct per_pidcomm *c;
+ int count = 0;
+
+ p = all_data;
+ while (p) {
+ p->display = 0;
+ if (p->start_time == 1)
+ p->start_time = first_time;
+
+ /* no exit marker, task kept running to the end */
+ if (p->end_time == 0)
+ p->end_time = last_time;
+
+ c = p->all;
+
+ while (c) {
+ c->display = 0;
+
+ if (c->start_time == 1)
+ c->start_time = first_time;
+
+ if (passes_filter(p, c)) {
+ c->display = 1;
+ p->display = 1;
+ count++;
+ }
+
+ if (c->end_time == 0)
+ c->end_time = last_time;
+
+ c = c->next;
+ }
+ p = p->next;
+ }
+ return count;
+}
+
+static int determine_display_tasks(u64 threshold)
+{
+ struct per_pid *p;
+ struct per_pidcomm *c;
+ int count = 0;
+
+ if (process_filter)
+ return determine_display_tasks_filtered();
+
+ p = all_data;
+ while (p) {
+ p->display = 0;
+ if (p->start_time == 1)
+ p->start_time = first_time;
+
+ /* no exit marker, task kept running to the end */
+ if (p->end_time == 0)
+ p->end_time = last_time;
+ if (p->total_time >= threshold && !power_only)
+ p->display = 1;
+
+ c = p->all;
+
+ while (c) {
+ c->display = 0;
+
+ if (c->start_time == 1)
+ c->start_time = first_time;
+
+ if (c->total_time >= threshold && !power_only) {
+ c->display = 1;
+ count++;
+ }
+
+ if (c->end_time == 0)
+ c->end_time = last_time;
+
+ c = c->next;
+ }
+ p = p->next;
+ }
+ return count;
+}
+
+
+
+#define TIME_THRESH 10000000
+
+static void write_svg_file(const char *filename)
+{
+ u64 i;
+ int count;
+
+ numcpus++;
+
+
+ count = determine_display_tasks(TIME_THRESH);
+
+ /* We'd like to show at least 15 tasks; be less picky if we have fewer */
+ if (count < 15)
+ count = determine_display_tasks(TIME_THRESH / 10);
+
+ open_svg(filename, numcpus, count, first_time, last_time);
+
+ svg_time_grid();
+ svg_legenda();
+
+ for (i = 0; i < numcpus; i++)
+ svg_cpu_box(i, max_freq, turbo_frequency);
+
+ draw_cpu_usage();
+ draw_process_bars();
+ draw_c_p_states();
+ draw_wakeups();
+
+ svg_close();
+}
+
+static struct perf_event_ops event_ops = {
+ .comm = process_comm_event,
+ .fork = process_fork_event,
+ .exit = process_exit_event,
+ .sample = process_sample_event,
+ .ordered_samples = true,
+};
+
+static int __cmd_timechart(void)
+{
+ struct perf_session *session = perf_session__new(input_name, O_RDONLY,
+ 0, false, &event_ops);
+ int ret = -EINVAL;
+
+ if (session == NULL)
+ return -ENOMEM;
+
+ if (!perf_session__has_traces(session, "timechart record"))
+ goto out_delete;
+
+ ret = perf_session__process_events(session, &event_ops);
+ if (ret)
+ goto out_delete;
+
+ end_sample_processing();
+
+ sort_pids();
+
+ write_svg_file(output_name);
+
+ pr_info("Written %2.1f seconds of trace to %s.\n",
+ (last_time - first_time) / 1000000000.0, output_name);
+out_delete:
+ perf_session__delete(session);
+ return ret;
+}
+
+static const char * const timechart_usage[] = {
+ "perf timechart [<options>] {record}",
+ NULL
+};
+
+#ifdef SUPPORT_OLD_POWER_EVENTS
+static const char * const record_old_args[] = {
+ "record",
+ "-a",
+ "-R",
+ "-f",
+ "-c", "1",
+ "-e", "power:power_start",
+ "-e", "power:power_end",
+ "-e", "power:power_frequency",
+ "-e", "sched:sched_wakeup",
+ "-e", "sched:sched_switch",
+};
+#endif
+
+static const char * const record_new_args[] = {
+ "record",
+ "-a",
+ "-R",
+ "-f",
+ "-c", "1",
+ "-e", "power:cpu_frequency",
+ "-e", "power:cpu_idle",
+ "-e", "sched:sched_wakeup",
+ "-e", "sched:sched_switch",
+};
+
+static int __cmd_record(int argc, const char **argv)
+{
+ unsigned int rec_argc, i, j;
+ const char **rec_argv;
+ const char * const *record_args = record_new_args;
+ unsigned int record_elems = ARRAY_SIZE(record_new_args);
+
+#ifdef SUPPORT_OLD_POWER_EVENTS
+ if (!is_valid_tracepoint("power:cpu_idle") &&
+ is_valid_tracepoint("power:power_start")) {
+ use_old_power_events = 1;
+ record_args = record_old_args;
+ record_elems = ARRAY_SIZE(record_old_args);
+ }
+#endif
+
+ rec_argc = record_elems + argc - 1;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+
+ if (rec_argv == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < record_elems; i++)
+ rec_argv[i] = strdup(record_args[i]);
+
+ for (j = 1; j < (unsigned int)argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ return cmd_record(i, rec_argv, NULL);
+}
+
+static int
+parse_process(const struct option *opt __used, const char *arg, int __used unset)
+{
+ if (arg)
+ add_process_filter(arg);
+ return 0;
+}
+
+static const struct option options[] = {
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_STRING('o', "output", &output_name, "file",
+ "output file name"),
+ OPT_INTEGER('w', "width", &svg_page_width,
+ "page width"),
+ OPT_BOOLEAN('P', "power-only", &power_only,
+ "output power data only"),
+ OPT_CALLBACK('p', "process", NULL, "process",
+ "process selector. Pass a pid or process name.",
+ parse_process),
+ OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+ "Look for files with symbols relative to this directory"),
+ OPT_END()
+};
+
+
+int cmd_timechart(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, options, timechart_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ symbol__init();
+
+ if (argc && !strncmp(argv[0], "rec", 3))
+ return __cmd_record(argc, argv);
+ else if (argc)
+ usage_with_options(timechart_usage, options);
+
+ setup_pager();
+
+ return __cmd_timechart();
+}
diff --git a/smartt-perf/builtin-top.c b/smartt-perf/builtin-top.c
new file mode 100644
index 0000000..5a29d9c
--- /dev/null
+++ b/smartt-perf/builtin-top.c
@@ -0,0 +1,1497 @@
+/*
+ * builtin-top.c
+ *
+ * Builtin top command: Display a continuously updated profile of
+ * any workload, CPU or specific PID.
+ *
+ * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
+ *
+ * Improvements and fixes by:
+ *
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Yanmin Zhang <yanmin.zhang@intel.com>
+ * Wu Fengguang <fengguang.wu@intel.com>
+ * Mike Galbraith <efault@gmx.de>
+ * Paul Mackerras <paulus@samba.org>
+ *
+ * Released under the GPL v2. (and only v2, not any later version)
+ */
+#include "builtin.h"
+
+#include "perf.h"
+
+#include "util/color.h"
+#include "util/evsel.h"
+#include "util/session.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/util.h"
+#include <linux/rbtree.h>
+#include "util/parse-options.h"
+#include "util/parse-events.h"
+#include "util/cpumap.h"
+#include "util/xyarray.h"
+
+#include "util/debug.h"
+
+#include <assert.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <errno.h>
+#include <time.h>
+#include <sched.h>
+#include <pthread.h>
+
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#include <linux/unistd.h>
+#include <linux/types.h>
+
+#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
+
+static bool system_wide = false;
+
+static int default_interval = 0;
+
+static int count_filter = 5;
+static int print_entries;
+
+static int target_pid = -1;
+static int target_tid = -1;
+static struct thread_map *threads;
+static bool inherit = false;
+static struct cpu_map *cpus;
+static int realtime_prio = 0;
+static bool group = false;
+static unsigned int page_size;
+static unsigned int mmap_pages = 16;
+static int freq = 1000; /* 1 KHz */
+
+static int delay_secs = 2;
+static bool zero = false;
+static bool dump_symtab = false;
+
+static bool hide_kernel_symbols = false;
+static bool hide_user_symbols = false;
+static struct winsize winsize;
+
+/*
+ * Source
+ */
+
+struct source_line {
+ u64 eip;
+ unsigned long count[MAX_COUNTERS];
+ char *line;
+ struct source_line *next;
+};
+
+static const char *sym_filter = NULL;
+struct sym_entry *sym_filter_entry = NULL;
+struct sym_entry *sym_filter_entry_sched = NULL;
+static int sym_pcnt_filter = 5;
+static int sym_counter = 0;
+static struct perf_evsel *sym_evsel = NULL;
+static int display_weighted = -1;
+static const char *cpu_list;
+
+/*
+ * Symbols
+ */
+
+struct sym_entry_source {
+ struct source_line *source;
+ struct source_line *lines;
+ struct source_line **lines_tail;
+ pthread_mutex_t lock;
+};
+
+struct sym_entry {
+ struct rb_node rb_node;
+ struct list_head node;
+ unsigned long snap_count;
+ double weight;
+ int skip;
+ u16 name_len;
+ u8 origin;
+ struct map *map;
+ struct sym_entry_source *src;
+ unsigned long count[0];
+};
+
+/*
+ * Source functions
+ */
+
+static inline struct symbol *sym_entry__symbol(struct sym_entry *self)
+{
+ return ((void *)self) + symbol_conf.priv_size;
+}
+
+void get_term_dimensions(struct winsize *ws)
+{
+ char *s = getenv("LINES");
+
+ if (s != NULL) {
+ ws->ws_row = atoi(s);
+ s = getenv("COLUMNS");
+ if (s != NULL) {
+ ws->ws_col = atoi(s);
+ if (ws->ws_row && ws->ws_col)
+ return;
+ }
+ }
+#ifdef TIOCGWINSZ
+ if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
+ ws->ws_row && ws->ws_col)
+ return;
+#endif
+ ws->ws_row = 25;
+ ws->ws_col = 80;
+}
+
+static void update_print_entries(struct winsize *ws)
+{
+ print_entries = ws->ws_row;
+
+ if (print_entries > 9)
+ print_entries -= 9;
+}
+
+static void sig_winch_handler(int sig __used)
+{
+ get_term_dimensions(&winsize);
+ update_print_entries(&winsize);
+}
+
+static int parse_source(struct sym_entry *syme)
+{
+ struct symbol *sym;
+ struct sym_entry_source *source;
+ struct map *map;
+ FILE *file;
+ char command[PATH_MAX*2];
+ const char *path;
+ u64 len;
+
+ if (!syme)
+ return -1;
+
+ sym = sym_entry__symbol(syme);
+ map = syme->map;
+
+ /*
+ * We can't annotate with just /proc/kallsyms
+ */
+ if (map->dso->origin == DSO__ORIG_KERNEL)
+ return -1;
+
+ if (syme->src == NULL) {
+ syme->src = zalloc(sizeof(*source));
+ if (syme->src == NULL)
+ return -1;
+ pthread_mutex_init(&syme->src->lock, NULL);
+ }
+
+ source = syme->src;
+
+ if (source->lines) {
+ pthread_mutex_lock(&source->lock);
+ goto out_assign;
+ }
+ path = map->dso->long_name;
+
+ len = sym->end - sym->start;
+
+ sprintf(command,
+ "objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s",
+ BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start),
+ BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path);
+
+ file = popen(command, "r");
+ if (!file)
+ return -1;
+
+ pthread_mutex_lock(&source->lock);
+ source->lines_tail = &source->lines;
+ while (!feof(file)) {
+ struct source_line *src;
+ size_t dummy = 0;
+ char *c, *sep;
+
+ src = malloc(sizeof(struct source_line));
+ assert(src != NULL);
+ memset(src, 0, sizeof(struct source_line));
+
+ if (getline(&src->line, &dummy, file) < 0)
+ break;
+ if (!src->line)
+ break;
+
+ c = strchr(src->line, '\n');
+ if (c)
+ *c = 0;
+
+ src->next = NULL;
+ *source->lines_tail = src;
+ source->lines_tail = &src->next;
+
+ src->eip = strtoull(src->line, &sep, 16);
+ if (*sep == ':')
+ src->eip = map__objdump_2ip(map, src->eip);
+ else /* this line has no ip info (e.g. source line) */
+ src->eip = 0;
+ }
+ pclose(file);
+out_assign:
+ sym_filter_entry = syme;
+ pthread_mutex_unlock(&source->lock);
+ return 0;
+}
+
+static void __zero_source_counters(struct sym_entry *syme)
+{
+ int i;
+ struct source_line *line;
+
+ line = syme->src->lines;
+ while (line) {
+ for (i = 0; i < nr_counters; i++)
+ line->count[i] = 0;
+ line = line->next;
+ }
+}
+
+static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
+{
+ struct source_line *line;
+
+ if (syme != sym_filter_entry)
+ return;
+
+ if (pthread_mutex_trylock(&syme->src->lock))
+ return;
+
+ if (syme->src == NULL || syme->src->source == NULL)
+ goto out_unlock;
+
+ for (line = syme->src->lines; line; line = line->next) {
+ /* skip lines without IP info */
+ if (line->eip == 0)
+ continue;
+ if (line->eip == ip) {
+ line->count[counter]++;
+ break;
+ }
+ if (line->eip > ip)
+ break;
+ }
+out_unlock:
+ pthread_mutex_unlock(&syme->src->lock);
+}
+
+#define PATTERN_LEN (BITS_PER_LONG / 4 + 2)
+
+static void lookup_sym_source(struct sym_entry *syme)
+{
+ struct symbol *symbol = sym_entry__symbol(syme);
+ struct source_line *line;
+ char pattern[PATTERN_LEN + 1];
+
+ sprintf(pattern, "%0*" PRIx64 " <", BITS_PER_LONG / 4,
+ map__rip_2objdump(syme->map, symbol->start));
+
+ pthread_mutex_lock(&syme->src->lock);
+ for (line = syme->src->lines; line; line = line->next) {
+ if (memcmp(line->line, pattern, PATTERN_LEN) == 0) {
+ syme->src->source = line;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&syme->src->lock);
+}
+
+static void show_lines(struct source_line *queue, int count, int total)
+{
+ int i;
+ struct source_line *line;
+
+ line = queue;
+ for (i = 0; i < count; i++) {
+ float pcnt = 100.0*(float)line->count[sym_counter]/(float)total;
+
+ printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line);
+ line = line->next;
+ }
+}
+
+#define TRACE_COUNT 3
+
+static void show_details(struct sym_entry *syme)
+{
+ struct symbol *symbol;
+ struct source_line *line;
+ struct source_line *line_queue = NULL;
+ int displayed = 0;
+ int line_queue_count = 0, total = 0, more = 0;
+
+ if (!syme)
+ return;
+
+ if (!syme->src->source)
+ lookup_sym_source(syme);
+
+ if (!syme->src->source)
+ return;
+
+ symbol = sym_entry__symbol(syme);
+ printf("Showing %s for %s\n", event_name(sym_evsel), symbol->name);
+ printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter);
+
+ pthread_mutex_lock(&syme->src->lock);
+ line = syme->src->source;
+ while (line) {
+ total += line->count[sym_counter];
+ line = line->next;
+ }
+
+ line = syme->src->source;
+ while (line) {
+ float pcnt = 0.0;
+
+ if (!line_queue_count)
+ line_queue = line;
+ line_queue_count++;
+
+ if (line->count[sym_counter])
+ pcnt = 100.0 * line->count[sym_counter] / (float)total;
+ if (pcnt >= (float)sym_pcnt_filter) {
+ if (displayed <= print_entries)
+ show_lines(line_queue, line_queue_count, total);
+ else more++;
+ displayed += line_queue_count;
+ line_queue_count = 0;
+ line_queue = NULL;
+ } else if (line_queue_count > TRACE_COUNT) {
+ line_queue = line_queue->next;
+ line_queue_count--;
+ }
+
+ line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8;
+ line = line->next;
+ }
+ pthread_mutex_unlock(&syme->src->lock);
+ if (more)
+ printf("%d lines not displayed, maybe increase display entries [e]\n", more);
+}
+
+/*
+ * Symbols will be added here in event__process_sample and will get out
+ * after decayed.
+ */
+static LIST_HEAD(active_symbols);
+static pthread_mutex_t active_symbols_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Ordering weight: count-1 * count-2 * ... / count-n
+ */
+static double sym_weight(const struct sym_entry *sym)
+{
+ double weight = sym->snap_count;
+ int counter;
+
+ if (!display_weighted)
+ return weight;
+
+ for (counter = 1; counter < nr_counters-1; counter++)
+ weight *= sym->count[counter];
+
+ weight /= (sym->count[counter] + 1);
+
+ return weight;
+}
+
+static long samples;
+static long kernel_samples, us_samples;
+static long exact_samples;
+static long guest_us_samples, guest_kernel_samples;
+static const char CONSOLE_CLEAR[] = "";
+
+static void __list_insert_active_sym(struct sym_entry *syme)
+{
+ list_add(&syme->node, &active_symbols);
+}
+
+static void list_remove_active_sym(struct sym_entry *syme)
+{
+ pthread_mutex_lock(&active_symbols_lock);
+ list_del_init(&syme->node);
+ pthread_mutex_unlock(&active_symbols_lock);
+}
+
+static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
+{
+ struct rb_node **p = &tree->rb_node;
+ struct rb_node *parent = NULL;
+ struct sym_entry *iter;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct sym_entry, rb_node);
+
+ if (se->weight > iter->weight)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&se->rb_node, parent, p);
+ rb_insert_color(&se->rb_node, tree);
+}
+
+static void print_sym_table(void)
+{
+ int printed = 0, j;
+ struct perf_evsel *counter;
+ int snap = !display_weighted ? sym_counter : 0;
+ float samples_per_sec = samples/delay_secs;
+ float ksamples_per_sec = kernel_samples/delay_secs;
+ float us_samples_per_sec = (us_samples)/delay_secs;
+ float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs;
+ float guest_us_samples_per_sec = (guest_us_samples)/delay_secs;
+ float esamples_percent = (100.0*exact_samples)/samples;
+ float sum_ksamples = 0.0;
+ struct sym_entry *syme, *n;
+ struct rb_root tmp = RB_ROOT;
+ struct rb_node *nd;
+ int sym_width = 0, dso_width = 0, dso_short_width = 0;
+ const int win_width = winsize.ws_col - 1;
+
+ samples = us_samples = kernel_samples = exact_samples = 0;
+ guest_kernel_samples = guest_us_samples = 0;
+
+ /* Sort the active symbols */
+ pthread_mutex_lock(&active_symbols_lock);
+ syme = list_entry(active_symbols.next, struct sym_entry, node);
+ pthread_mutex_unlock(&active_symbols_lock);
+
+ list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
+ syme->snap_count = syme->count[snap];
+ if (syme->snap_count != 0) {
+
+ if ((hide_user_symbols &&
+ syme->origin == PERF_RECORD_MISC_USER) ||
+ (hide_kernel_symbols &&
+ syme->origin == PERF_RECORD_MISC_KERNEL)) {
+ list_remove_active_sym(syme);
+ continue;
+ }
+ syme->weight = sym_weight(syme);
+ rb_insert_active_sym(&tmp, syme);
+ sum_ksamples += syme->snap_count;
+
+ for (j = 0; j < nr_counters; j++)
+ syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8;
+ } else
+ list_remove_active_sym(syme);
+ }
+
+ puts(CONSOLE_CLEAR);
+
+ printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
+ if (!perf_guest) {
+ printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%"
+ " exact: %4.1f%% [",
+ samples_per_sec,
+ 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) /
+ samples_per_sec)),
+ esamples_percent);
+ } else {
+ printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%"
+ " guest kernel:%4.1f%% guest us:%4.1f%%"
+ " exact: %4.1f%% [",
+ samples_per_sec,
+ 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) /
+ samples_per_sec)),
+ 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) /
+ samples_per_sec)),
+ 100.0 - (100.0 * ((samples_per_sec -
+ guest_kernel_samples_per_sec) /
+ samples_per_sec)),
+ 100.0 - (100.0 * ((samples_per_sec -
+ guest_us_samples_per_sec) /
+ samples_per_sec)),
+ esamples_percent);
+ }
+
+ if (nr_counters == 1 || !display_weighted) {
+ struct perf_evsel *first;
+ first = list_entry(evsel_list.next, struct perf_evsel, node);
+ printf("%" PRIu64, (uint64_t)first->attr.sample_period);
+ if (freq)
+ printf("Hz ");
+ else
+ printf(" ");
+ }
+
+ if (!display_weighted)
+ printf("%s", event_name(sym_evsel));
+ else list_for_each_entry(counter, &evsel_list, node) {
+ if (counter->idx)
+ printf("/");
+
+ printf("%s", event_name(counter));
+ }
+
+ printf( "], ");
+
+ if (target_pid != -1)
+ printf(" (target_pid: %d", target_pid);
+ else if (target_tid != -1)
+ printf(" (target_tid: %d", target_tid);
+ else
+ printf(" (all");
+
+ if (cpu_list)
+ printf(", CPU%s: %s)\n", cpus->nr > 1 ? "s" : "", cpu_list);
+ else {
+ if (target_tid != -1)
+ printf(")\n");
+ else
+ printf(", %d CPU%s)\n", cpus->nr, cpus->nr > 1 ? "s" : "");
+ }
+
+ printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
+
+ if (sym_filter_entry) {
+ show_details(sym_filter_entry);
+ return;
+ }
+
+ /*
+ * Find the longest symbol name that will be displayed
+ */
+ for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
+ syme = rb_entry(nd, struct sym_entry, rb_node);
+ if (++printed > print_entries ||
+ (int)syme->snap_count < count_filter)
+ continue;
+
+ if (syme->map->dso->long_name_len > dso_width)
+ dso_width = syme->map->dso->long_name_len;
+
+ if (syme->map->dso->short_name_len > dso_short_width)
+ dso_short_width = syme->map->dso->short_name_len;
+
+ if (syme->name_len > sym_width)
+ sym_width = syme->name_len;
+ }
+
+ printed = 0;
+
+ if (sym_width + dso_width > winsize.ws_col - 29) {
+ dso_width = dso_short_width;
+ if (sym_width + dso_width > winsize.ws_col - 29)
+ sym_width = winsize.ws_col - dso_width - 29;
+ }
+ putchar('\n');
+ if (nr_counters == 1)
+ printf(" samples pcnt");
+ else
+ printf(" weight samples pcnt");
+
+ if (verbose)
+ printf(" RIP ");
+ printf(" %-*.*s DSO\n", sym_width, sym_width, "function");
+ printf(" %s _______ _____",
+ nr_counters == 1 ? " " : "______");
+ if (verbose)
+ printf(" ________________");
+ printf(" %-*.*s", sym_width, sym_width, graph_line);
+ printf(" %-*.*s", dso_width, dso_width, graph_line);
+ puts("\n");
+
+ for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
+ struct symbol *sym;
+ double pcnt;
+
+ syme = rb_entry(nd, struct sym_entry, rb_node);
+ sym = sym_entry__symbol(syme);
+ if (++printed > print_entries || (int)syme->snap_count < count_filter)
+ continue;
+
+ pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
+ sum_ksamples));
+
+ if (nr_counters == 1 || !display_weighted)
+ printf("%20.2f ", syme->weight);
+ else
+ printf("%9.1f %10ld ", syme->weight, syme->snap_count);
+
+ percent_color_fprintf(stdout, "%4.1f%%", pcnt);
+ if (verbose)
+ printf(" %016" PRIx64, sym->start);
+ printf(" %-*.*s", sym_width, sym_width, sym->name);
+ printf(" %-*.*s\n", dso_width, dso_width,
+ dso_width >= syme->map->dso->long_name_len ?
+ syme->map->dso->long_name :
+ syme->map->dso->short_name);
+ }
+}
+
+static void prompt_integer(int *target, const char *msg)
+{
+ char *buf = malloc(0), *p;
+ size_t dummy = 0;
+ int tmp;
+
+ fprintf(stdout, "\n%s: ", msg);
+ if (getline(&buf, &dummy, stdin) < 0)
+ return;
+
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+
+ p = buf;
+ while(*p) {
+ if (!isdigit(*p))
+ goto out_free;
+ p++;
+ }
+ tmp = strtoul(buf, NULL, 10);
+ *target = tmp;
+out_free:
+ free(buf);
+}
+
+static void prompt_percent(int *target, const char *msg)
+{
+ int tmp = 0;
+
+ prompt_integer(&tmp, msg);
+ if (tmp >= 0 && tmp <= 100)
+ *target = tmp;
+}
+
+static void prompt_symbol(struct sym_entry **target, const char *msg)
+{
+ char *buf = malloc(0), *p;
+ struct sym_entry *syme = *target, *n, *found = NULL;
+ size_t dummy = 0;
+
+ /* zero counters of active symbol */
+ if (syme) {
+ pthread_mutex_lock(&syme->src->lock);
+ __zero_source_counters(syme);
+ *target = NULL;
+ pthread_mutex_unlock(&syme->src->lock);
+ }
+
+ fprintf(stdout, "\n%s: ", msg);
+ if (getline(&buf, &dummy, stdin) < 0)
+ goto out_free;
+
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+
+ pthread_mutex_lock(&active_symbols_lock);
+ syme = list_entry(active_symbols.next, struct sym_entry, node);
+ pthread_mutex_unlock(&active_symbols_lock);
+
+ list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
+ struct symbol *sym = sym_entry__symbol(syme);
+
+ if (!strcmp(buf, sym->name)) {
+ found = syme;
+ break;
+ }
+ }
+
+ if (!found) {
+ fprintf(stderr, "Sorry, %s is not active.\n", buf);
+ sleep(1);
+ return;
+ } else
+ parse_source(found);
+
+out_free:
+ free(buf);
+}
+
+static void print_mapped_keys(void)
+{
+ char *name = NULL;
+
+ if (sym_filter_entry) {
+ struct symbol *sym = sym_entry__symbol(sym_filter_entry);
+ name = sym->name;
+ }
+
+ fprintf(stdout, "\nMapped keys:\n");
+ fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
+ fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
+
+ if (nr_counters > 1)
+ fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel));
+
+ fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
+
+ fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
+ fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
+ fprintf(stdout, "\t[S] stop annotation.\n");
+
+ if (nr_counters > 1)
+ fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
+
+ fprintf(stdout,
+ "\t[K] hide kernel_symbols symbols. \t(%s)\n",
+ hide_kernel_symbols ? "yes" : "no");
+ fprintf(stdout,
+ "\t[U] hide user symbols. \t(%s)\n",
+ hide_user_symbols ? "yes" : "no");
+ fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0);
+ fprintf(stdout, "\t[qQ] quit.\n");
+}
+
+static int key_mapped(int c)
+{
+ switch (c) {
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'z':
+ case 'q':
+ case 'Q':
+ case 'K':
+ case 'U':
+ case 'F':
+ case 's':
+ case 'S':
+ return 1;
+ case 'E':
+ case 'w':
+ return nr_counters > 1 ? 1 : 0;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void handle_keypress(struct perf_session *session, int c)
+{
+ if (!key_mapped(c)) {
+ struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
+ struct termios tc, save;
+
+ print_mapped_keys();
+ fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
+ fflush(stdout);
+
+ tcgetattr(0, &save);
+ tc = save;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &tc);
+
+ poll(&stdin_poll, 1, -1);
+ c = getc(stdin);
+
+ tcsetattr(0, TCSAFLUSH, &save);
+ if (!key_mapped(c))
+ return;
+ }
+
+ switch (c) {
+ case 'd':
+ prompt_integer(&delay_secs, "Enter display delay");
+ if (delay_secs < 1)
+ delay_secs = 1;
+ break;
+ case 'e':
+ prompt_integer(&print_entries, "Enter display entries (lines)");
+ if (print_entries == 0) {
+ sig_winch_handler(SIGWINCH);
+ signal(SIGWINCH, sig_winch_handler);
+ } else
+ signal(SIGWINCH, SIG_DFL);
+ break;
+ case 'E':
+ if (nr_counters > 1) {
+ fprintf(stderr, "\nAvailable events:");
+
+ list_for_each_entry(sym_evsel, &evsel_list, node)
+ fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel));
+
+ prompt_integer(&sym_counter, "Enter details event counter");
+
+ if (sym_counter >= nr_counters) {
+ sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node);
+ sym_counter = 0;
+ fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel));
+ sleep(1);
+ break;
+ }
+ list_for_each_entry(sym_evsel, &evsel_list, node)
+ if (sym_evsel->idx == sym_counter)
+ break;
+ } else sym_counter = 0;
+ break;
+ case 'f':
+ prompt_integer(&count_filter, "Enter display event count filter");
+ break;
+ case 'F':
+ prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
+ break;
+ case 'K':
+ hide_kernel_symbols = !hide_kernel_symbols;
+ break;
+ case 'q':
+ case 'Q':
+ printf("exiting.\n");
+ if (dump_symtab)
+ perf_session__fprintf_dsos(session, stderr);
+ exit(0);
+ case 's':
+ prompt_symbol(&sym_filter_entry, "Enter details symbol");
+ break;
+ case 'S':
+ if (!sym_filter_entry)
+ break;
+ else {
+ struct sym_entry *syme = sym_filter_entry;
+
+ pthread_mutex_lock(&syme->src->lock);
+ sym_filter_entry = NULL;
+ __zero_source_counters(syme);
+ pthread_mutex_unlock(&syme->src->lock);
+ }
+ break;
+ case 'U':
+ hide_user_symbols = !hide_user_symbols;
+ break;
+ case 'w':
+ display_weighted = ~display_weighted;
+ break;
+ case 'z':
+ zero = !zero;
+ break;
+ default:
+ break;
+ }
+}
+
+static void *display_thread(void *arg __used)
+{
+ struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
+ struct termios tc, save;
+ int delay_msecs, c;
+ struct perf_session *session = (struct perf_session *) arg;
+
+ tcgetattr(0, &save);
+ tc = save;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
+
+repeat:
+ delay_msecs = delay_secs * 1000;
+ tcsetattr(0, TCSANOW, &tc);
+ /* trash return*/
+ getc(stdin);
+
+ do {
+ print_sym_table();
+ } while (!poll(&stdin_poll, 1, delay_msecs) == 1);
+
+ c = getc(stdin);
+ tcsetattr(0, TCSAFLUSH, &save);
+
+ handle_keypress(session, c);
+ goto repeat;
+
+ return NULL;
+}
+
+/* Tag samples to be skipped. */
+static const char *skip_symbols[] = {
+ "default_idle",
+ "cpu_idle",
+ "enter_idle",
+ "exit_idle",
+ "mwait_idle",
+ "mwait_idle_with_hints",
+ "poll_idle",
+ "ppc64_runlatch_off",
+ "pseries_dedicated_idle_sleep",
+ NULL
+};
+
+static int symbol_filter(struct map *map, struct symbol *sym)
+{
+ struct sym_entry *syme;
+ const char *name = sym->name;
+ int i;
+
+ /*
+ * ppc64 uses function descriptors and appends a '.' to the
+ * start of every instruction address. Remove it.
+ */
+ if (name[0] == '.')
+ name++;
+
+ if (!strcmp(name, "_text") ||
+ !strcmp(name, "_etext") ||
+ !strcmp(name, "_sinittext") ||
+ !strncmp("init_module", name, 11) ||
+ !strncmp("cleanup_module", name, 14) ||
+ strstr(name, "_text_start") ||
+ strstr(name, "_text_end"))
+ return 1;
+
+ syme = symbol__priv(sym);
+ syme->map = map;
+ syme->src = NULL;
+
+ if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) {
+ /* schedule initial sym_filter_entry setup */
+ sym_filter_entry_sched = syme;
+ sym_filter = NULL;
+ }
+
+ for (i = 0; skip_symbols[i]; i++) {
+ if (!strcmp(skip_symbols[i], name)) {
+ syme->skip = 1;
+ break;
+ }
+ }
+
+ if (!syme->skip)
+ syme->name_len = strlen(sym->name);
+
+ return 0;
+}
+
+static void event__process_sample(const event_t *self,
+ struct sample_data *sample,
+ struct perf_session *session,
+ struct perf_evsel *evsel)
+{
+ u64 ip = self->ip.ip;
+ struct sym_entry *syme;
+ struct addr_location al;
+ struct machine *machine;
+ u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+
+ ++samples;
+
+ switch (origin) {
+ case PERF_RECORD_MISC_USER:
+ ++us_samples;
+ if (hide_user_symbols)
+ return;
+ machine = perf_session__find_host_machine(session);
+ break;
+ case PERF_RECORD_MISC_KERNEL:
+ ++kernel_samples;
+ if (hide_kernel_symbols)
+ return;
+ machine = perf_session__find_host_machine(session);
+ break;
+ case PERF_RECORD_MISC_GUEST_KERNEL:
+ ++guest_kernel_samples;
+ machine = perf_session__find_machine(session, self->ip.pid);
+ break;
+ case PERF_RECORD_MISC_GUEST_USER:
+ ++guest_us_samples;
+ /*
+ * TODO: we don't process guest user from host side
+ * except simple counting.
+ */
+ return;
+ default:
+ return;
+ }
+
+ if (!machine && perf_guest) {
+ pr_err("Can't find guest [%d]'s kernel information\n",
+ self->ip.pid);
+ return;
+ }
+
+ if (self->header.misc & PERF_RECORD_MISC_EXACT_IP)
+ exact_samples++;
+
+ if (event__preprocess_sample(self, session, &al, sample,
+ symbol_filter) < 0 ||
+ al.filtered)
+ return;
+
+ if (al.sym == NULL) {
+ /*
+ * As we do lazy loading of symtabs we only will know if the
+ * specified vmlinux file is invalid when we actually have a
+ * hit in kernel space and then try to load it. So if we get
+ * here and there are _no_ symbols in the DSO backing the
+ * kernel map, bail out.
+ *
+ * We may never get here, for instance, if we use -K/
+ * --hide-kernel-symbols, even if the user specifies an
+ * invalid --vmlinux ;-)
+ */
+ if (al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
+ RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
+ pr_err("The %s file can't be used\n",
+ symbol_conf.vmlinux_name);
+ exit(1);
+ }
+
+ return;
+ }
+
+ /* let's see, whether we need to install initial sym_filter_entry */
+ if (sym_filter_entry_sched) {
+ sym_filter_entry = sym_filter_entry_sched;
+ sym_filter_entry_sched = NULL;
+ if (parse_source(sym_filter_entry) < 0) {
+ struct symbol *sym = sym_entry__symbol(sym_filter_entry);
+
+ pr_err("Can't annotate %s", sym->name);
+ if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) {
+ pr_err(": No vmlinux file was found in the path:\n");
+ machine__fprintf_vmlinux_path(machine, stderr);
+ } else
+ pr_err(".\n");
+ exit(1);
+ }
+ }
+
+ syme = symbol__priv(al.sym);
+ if (!syme->skip) {
+ syme->count[evsel->idx]++;
+ syme->origin = origin;
+ record_precise_ip(syme, evsel->idx, ip);
+ pthread_mutex_lock(&active_symbols_lock);
+ if (list_empty(&syme->node) || !syme->node.next)
+ __list_insert_active_sym(syme);
+ pthread_mutex_unlock(&active_symbols_lock);
+ }
+}
+
+struct mmap_data {
+ void *base;
+ int mask;
+ unsigned int prev;
+};
+
+static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel,
+ int ncpus, int nthreads)
+{
+ evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data));
+ return evsel->priv != NULL ? 0 : -ENOMEM;
+}
+
+static void perf_evsel__free_mmap(struct perf_evsel *evsel)
+{
+ xyarray__delete(evsel->priv);
+ evsel->priv = NULL;
+}
+
+static unsigned int mmap_read_head(struct mmap_data *md)
+{
+ struct perf_event_mmap_page *pc = md->base;
+ int head;
+
+ head = pc->data_head;
+ rmb();
+
+ return head;
+}
+
+static void perf_session__mmap_read_counter(struct perf_session *self,
+ struct perf_evsel *evsel,
+ int cpu, int thread_idx)
+{
+ struct xyarray *mmap_array = evsel->priv;
+ struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx);
+ unsigned int head = mmap_read_head(md);
+ unsigned int old = md->prev;
+ unsigned char *data = md->base + page_size;
+ struct sample_data sample;
+ int diff;
+
+ /*
+ * If we're further behind than half the buffer, there's a chance
+ * the writer will bite our tail and mess up the samples under us.
+ *
+ * If we somehow ended up ahead of the head, we got messed up.
+ *
+ * In either case, truncate and restart at head.
+ */
+ diff = head - old;
+ if (diff > md->mask / 2 || diff < 0) {
+ fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
+
+ /*
+ * head points to a known good entry, start there.
+ */
+ old = head;
+ }
+
+ for (; old != head;) {
+ event_t *event = (event_t *)&data[old & md->mask];
+
+ event_t event_copy;
+
+ size_t size = event->header.size;
+
+ /*
+ * Event straddles the mmap boundary -- header should always
+ * be inside due to u64 alignment of output.
+ */
+ if ((old & md->mask) + size != ((old + size) & md->mask)) {
+ unsigned int offset = old;
+ unsigned int len = min(sizeof(*event), size), cpy;
+ void *dst = &event_copy;
+
+ do {
+ cpy = min(md->mask + 1 - (offset & md->mask), len);
+ memcpy(dst, &data[offset & md->mask], cpy);
+ offset += cpy;
+ dst += cpy;
+ len -= cpy;
+ } while (len);
+
+ event = &event_copy;
+ }
+
+ event__parse_sample(event, self, &sample);
+ if (event->header.type == PERF_RECORD_SAMPLE)
+ event__process_sample(event, &sample, self, evsel);
+ else
+ event__process(event, &sample, self);
+ old += size;
+ }
+
+ md->prev = old;
+}
+
+static struct pollfd *event_array;
+
+static void perf_session__mmap_read(struct perf_session *self)
+{
+ struct perf_evsel *counter;
+ int i, thread_index;
+
+ for (i = 0; i < cpus->nr; i++) {
+ list_for_each_entry(counter, &evsel_list, node) {
+ for (thread_index = 0;
+ thread_index < threads->nr;
+ thread_index++) {
+ perf_session__mmap_read_counter(self,
+ counter, i, thread_index);
+ }
+ }
+ }
+}
+
+int nr_poll;
+int group_fd;
+
+static void start_counter(int i, struct perf_evsel *evsel)
+{
+ struct xyarray *mmap_array = evsel->priv;
+ struct mmap_data *mm;
+ struct perf_event_attr *attr;
+ int cpu = -1;
+ int thread_index;
+
+ if (target_tid == -1)
+ cpu = cpus->map[i];
+
+ attr = &evsel->attr;
+
+ attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
+
+ if (freq) {
+ attr->sample_type |= PERF_SAMPLE_PERIOD;
+ attr->freq = 1;
+ attr->sample_freq = freq;
+ }
+
+ attr->inherit = (cpu < 0) && inherit;
+ attr->mmap = 1;
+
+ for (thread_index = 0; thread_index < threads->nr; thread_index++) {
+try_again:
+ FD(evsel, i, thread_index) = sys_perf_event_open(attr,
+ threads->map[thread_index], cpu, group_fd, 0);
+
+ if (FD(evsel, i, thread_index) < 0) {
+ int err = errno;
+
+ if (err == EPERM || err == EACCES)
+ die("Permission error - are you root?\n"
+ "\t Consider tweaking"
+ " /proc/sys/kernel/perf_event_paranoid.\n");
+ /*
+ * If it's cycles then fall back to hrtimer
+ * based cpu-clock-tick sw counter, which
+ * is always available even if no PMU support:
+ */
+ if (attr->type == PERF_TYPE_HARDWARE
+ && attr->config == PERF_COUNT_HW_CPU_CYCLES) {
+
+ if (verbose)
+ warning(" ... trying to fall back to cpu-clock-ticks\n");
+
+ attr->type = PERF_TYPE_SOFTWARE;
+ attr->config = PERF_COUNT_SW_CPU_CLOCK;
+ goto try_again;
+ }
+ printf("\n");
+ error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
+ FD(evsel, i, thread_index), strerror(err));
+ die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+ exit(-1);
+ }
+ assert(FD(evsel, i, thread_index) >= 0);
+ fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK);
+
+ /*
+ * First counter acts as the group leader:
+ */
+ if (group && group_fd == -1)
+ group_fd = FD(evsel, i, thread_index);
+
+ event_array[nr_poll].fd = FD(evsel, i, thread_index);
+ event_array[nr_poll].events = POLLIN;
+ nr_poll++;
+
+ mm = xyarray__entry(mmap_array, i, thread_index);
+ mm->prev = 0;
+ mm->mask = mmap_pages*page_size - 1;
+ mm->base = mmap(NULL, (mmap_pages+1)*page_size,
+ PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0);
+ if (mm->base == MAP_FAILED)
+ die("failed to mmap with %d (%s)\n", errno, strerror(errno));
+ }
+}
+
+static int __cmd_top(void)
+{
+ pthread_t thread;
+ struct perf_evsel *counter;
+ int i, ret;
+ /*
+ * FIXME: perf_session__new should allow passing a O_MMAP, so that all this
+ * mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
+ */
+ struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL);
+ if (session == NULL)
+ return -ENOMEM;
+
+ if (target_tid != -1)
+ event__synthesize_thread_map(threads, event__process, session);
+ else
+ event__synthesize_threads(event__process, session);
+
+ for (i = 0; i < cpus->nr; i++) {
+ group_fd = -1;
+ list_for_each_entry(counter, &evsel_list, node)
+ start_counter(i, counter);
+ }
+
+ /* Wait for a minimal set of events before starting the snapshot */
+ poll(&event_array[0], nr_poll, 100);
+
+ perf_session__mmap_read(session);
+
+ if (pthread_create(&thread, NULL, display_thread, session)) {
+ printf("Could not create display thread.\n");
+ exit(-1);
+ }
+
+ if (realtime_prio) {
+ struct sched_param param;
+
+ param.sched_priority = realtime_prio;
+ if (sched_setscheduler(0, SCHED_FIFO, &param)) {
+ printf("Could not set realtime priority.\n");
+ exit(-1);
+ }
+ }
+
+ while (1) {
+ int hits = samples;
+
+ perf_session__mmap_read(session);
+
+ if (hits == samples)
+ ret = poll(event_array, nr_poll, 100);
+ }
+
+ return 0;
+}
+
+static const char * const top_usage[] = {
+ "perf top [<options>]",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_CALLBACK('e', "event", NULL, "event",
+ "event selector. use 'perf list' to list available events",
+ parse_events),
+ OPT_INTEGER('c', "count", &default_interval,
+ "event period to sample"),
+ OPT_INTEGER('p', "pid", &target_pid,
+ "profile events on existing process id"),
+ OPT_INTEGER('t', "tid", &target_tid,
+ "profile events on existing thread id"),
+ OPT_BOOLEAN('a', "all-cpus", &system_wide,
+ "system-wide collection from all CPUs"),
+ OPT_STRING('C', "cpu", &cpu_list, "cpu",
+ "list of cpus to monitor"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
+ OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols,
+ "hide kernel symbols"),
+ OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"),
+ OPT_INTEGER('r', "realtime", &realtime_prio,
+ "collect data with this RT SCHED_FIFO priority"),
+ OPT_INTEGER('d', "delay", &delay_secs,
+ "number of seconds to delay between refreshes"),
+ OPT_BOOLEAN('D', "dump-symtab", &dump_symtab,
+ "dump the symbol table used for profiling"),
+ OPT_INTEGER('f', "count-filter", &count_filter,
+ "only display functions with more events than this"),
+ OPT_BOOLEAN('g', "group", &group,
+ "put the counters into a counter group"),
+ OPT_BOOLEAN('i', "inherit", &inherit,
+ "child tasks inherit counters"),
+ OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name",
+ "symbol to annotate"),
+ OPT_BOOLEAN('z', "zero", &zero,
+ "zero history across updates"),
+ OPT_INTEGER('F', "freq", &freq,
+ "profile at this frequency"),
+ OPT_INTEGER('E', "entries", &print_entries,
+ "display this many functions"),
+ OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols,
+ "hide user symbols"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show counter open errors, etc)"),
+ OPT_END()
+};
+
+int cmd_top(int argc, const char **argv, const char *prefix __used)
+{
+ struct perf_evsel *pos;
+ int status = -ENOMEM;
+
+ page_size = sysconf(_SC_PAGE_SIZE);
+
+ argc = parse_options(argc, argv, options, top_usage, 0);
+ if (argc)
+ usage_with_options(top_usage, options);
+
+ if (target_pid != -1)
+ target_tid = target_pid;
+
+ threads = thread_map__new(target_pid, target_tid);
+ if (threads == NULL) {
+ pr_err("Problems finding threads of monitor\n");
+ usage_with_options(top_usage, options);
+ }
+
+ event_array = malloc((sizeof(struct pollfd) *
+ MAX_NR_CPUS * MAX_COUNTERS * threads->nr));
+ if (!event_array)
+ return -ENOMEM;
+
+ /* CPU and PID are mutually exclusive */
+ if (target_tid > 0 && cpu_list) {
+ printf("WARNING: PID switch overriding CPU\n");
+ sleep(1);
+ cpu_list = NULL;
+ }
+
+ if (!nr_counters && perf_evsel_list__create_default() < 0) {
+ pr_err("Not enough memory for event selector list\n");
+ return -ENOMEM;
+ }
+
+ if (delay_secs < 1)
+ delay_secs = 1;
+
+ /*
+ * User specified count overrides default frequency.
+ */
+ if (default_interval)
+ freq = 0;
+ else if (freq) {
+ default_interval = freq;
+ } else {
+ fprintf(stderr, "frequency and count are zero, aborting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (target_tid != -1)
+ cpus = cpu_map__dummy_new();
+ else
+ cpus = cpu_map__new(cpu_list);
+
+ if (cpus == NULL)
+ usage_with_options(top_usage, options);
+
+ list_for_each_entry(pos, &evsel_list, node) {
+ if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 ||
+ perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
+ goto out_free_fd;
+ /*
+ * Fill in the ones not specifically initialized via -c:
+ */
+ if (pos->attr.sample_period)
+ continue;
+
+ pos->attr.sample_period = default_interval;
+ }
+
+ sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node);
+
+ symbol_conf.priv_size = (sizeof(struct sym_entry) +
+ (nr_counters + 1) * sizeof(unsigned long));
+
+ symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
+ if (symbol__init() < 0)
+ return -1;
+
+ get_term_dimensions(&winsize);
+ if (print_entries == 0) {
+ update_print_entries(&winsize);
+ signal(SIGWINCH, sig_winch_handler);
+ }
+
+ status = __cmd_top();
+out_free_fd:
+ list_for_each_entry(pos, &evsel_list, node)
+ perf_evsel__free_mmap(pos);
+ perf_evsel_list__delete();
+
+ return status;
+}
diff --git a/smartt-perf/builtin-trace.c b/smartt-perf/builtin-trace.c
new file mode 100644
index 0000000..dddf3f0
--- /dev/null
+++ b/smartt-perf/builtin-trace.c
@@ -0,0 +1,717 @@
+#include "builtin.h"
+
+#include "util/util.h"
+#include "util/cache.h"
+#include "util/symbol.h"
+#include "util/thread.h"
+#include "util/header.h"
+#include "util/exec_cmd.h"
+#include "util/trace-event.h"
+#include "util/session.h"
+
+static char const *script_name;
+static char const *generate_script_lang;
+static bool debug_ordering;
+static u64 last_timestamp;
+
+static int default_start_script(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
+{
+ return 0;
+}
+
+static int default_stop_script(void)
+{
+ return 0;
+}
+
+static int default_generate_script(const char *outfile __unused)
+{
+ return 0;
+}
+
+static struct scripting_ops default_scripting_ops = {
+ .start_script = default_start_script,
+ .stop_script = default_stop_script,
+ .process_event = print_event,
+ .generate_script = default_generate_script,
+};
+
+static struct scripting_ops *scripting_ops;
+
+static void setup_scripting(void)
+{
+ /* make sure PERF_EXEC_PATH is set for scripts */
+ perf_set_argv_exec_path(perf_exec_path());
+
+ setup_perl_scripting();
+ setup_python_scripting();
+
+ scripting_ops = &default_scripting_ops;
+}
+
+static int cleanup_scripting(void)
+{
+ pr_debug("\nperf trace script stopped\n");
+
+ return scripting_ops->stop_script();
+}
+
+#include "util/parse-options.h"
+
+#include "perf.h"
+#include "util/debug.h"
+
+#include "util/trace-event.h"
+#include "util/exec_cmd.h"
+
+static char const *input_name = "perf.data";
+
+static int process_sample_event(event_t *event, struct perf_session *session)
+{
+ struct sample_data data;
+ struct thread *thread;
+
+ memset(&data, 0, sizeof(data));
+ data.time = -1;
+ data.cpu = -1;
+ data.period = 1;
+
+ event__parse_sample(event, session->sample_type, &data);
+
+ dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
+ data.pid, data.tid, data.ip, data.period);
+
+ thread = perf_session__findnew(session, event->ip.pid);
+ if (thread == NULL) {
+ pr_debug("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ if (session->sample_type & PERF_SAMPLE_RAW) {
+ if (debug_ordering) {
+ if (data.time < last_timestamp) {
+ pr_err("Samples misordered, previous: %llu "
+ "this: %llu\n", last_timestamp,
+ data.time);
+ }
+ last_timestamp = data.time;
+ }
+ /*
+ * FIXME: better resolve from pid from the struct trace_entry
+ * field, although it should be the same than this perf
+ * event pid
+ */
+ scripting_ops->process_event(data.cpu, data.raw_data,
+ data.raw_size,
+ data.time, thread->comm);
+ }
+
+ session->hists.stats.total_period += data.period;
+ return 0;
+}
+
+static struct perf_event_ops event_ops = {
+ .sample = process_sample_event,
+ .comm = event__process_comm,
+ .attr = event__process_attr,
+ .event_type = event__process_event_type,
+ .tracing_data = event__process_tracing_data,
+ .build_id = event__process_build_id,
+ .ordered_samples = true,
+};
+
+extern volatile int session_done;
+
+static void sig_handler(int sig __unused)
+{
+ session_done = 1;
+}
+
+static int __cmd_trace(struct perf_session *session)
+{
+ signal(SIGINT, sig_handler);
+
+ return perf_session__process_events(session, &event_ops);
+}
+
+struct script_spec {
+ struct list_head node;
+ struct scripting_ops *ops;
+ char spec[0];
+};
+
+LIST_HEAD(script_specs);
+
+static struct script_spec *script_spec__new(const char *spec,
+ struct scripting_ops *ops)
+{
+ struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
+
+ if (s != NULL) {
+ strcpy(s->spec, spec);
+ s->ops = ops;
+ }
+
+ return s;
+}
+
+static void script_spec__delete(struct script_spec *s)
+{
+ free(s->spec);
+ free(s);
+}
+
+static void script_spec__add(struct script_spec *s)
+{
+ list_add_tail(&s->node, &script_specs);
+}
+
+static struct script_spec *script_spec__find(const char *spec)
+{
+ struct script_spec *s;
+
+ list_for_each_entry(s, &script_specs, node)
+ if (strcasecmp(s->spec, spec) == 0)
+ return s;
+ return NULL;
+}
+
+static struct script_spec *script_spec__findnew(const char *spec,
+ struct scripting_ops *ops)
+{
+ struct script_spec *s = script_spec__find(spec);
+
+ if (s)
+ return s;
+
+ s = script_spec__new(spec, ops);
+ if (!s)
+ goto out_delete_spec;
+
+ script_spec__add(s);
+
+ return s;
+
+out_delete_spec:
+ script_spec__delete(s);
+
+ return NULL;
+}
+
+int script_spec_register(const char *spec, struct scripting_ops *ops)
+{
+ struct script_spec *s;
+
+ s = script_spec__find(spec);
+ if (s)
+ return -1;
+
+ s = script_spec__findnew(spec, ops);
+ if (!s)
+ return -1;
+
+ return 0;
+}
+
+static struct scripting_ops *script_spec__lookup(const char *spec)
+{
+ struct script_spec *s = script_spec__find(spec);
+ if (!s)
+ return NULL;
+
+ return s->ops;
+}
+
+static void list_available_languages(void)
+{
+ struct script_spec *s;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Scripting language extensions (used in "
+ "perf trace -s [spec:]script.[spec]):\n\n");
+
+ list_for_each_entry(s, &script_specs, node)
+ fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name);
+
+ fprintf(stderr, "\n");
+}
+
+static int parse_scriptname(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ char spec[PATH_MAX];
+ const char *script, *ext;
+ int len;
+
+ if (strcmp(str, "lang") == 0) {
+ list_available_languages();
+ exit(0);
+ }
+
+ script = strchr(str, ':');
+ if (script) {
+ len = script - str;
+ if (len >= PATH_MAX) {
+ fprintf(stderr, "invalid language specifier");
+ return -1;
+ }
+ strncpy(spec, str, len);
+ spec[len] = '\0';
+ scripting_ops = script_spec__lookup(spec);
+ if (!scripting_ops) {
+ fprintf(stderr, "invalid language specifier");
+ return -1;
+ }
+ script++;
+ } else {
+ script = str;
+ ext = strchr(script, '.');
+ if (!ext) {
+ fprintf(stderr, "invalid script extension");
+ return -1;
+ }
+ scripting_ops = script_spec__lookup(++ext);
+ if (!scripting_ops) {
+ fprintf(stderr, "invalid script extension");
+ return -1;
+ }
+ }
+
+ script_name = strdup(script);
+
+ return 0;
+}
+
+#define for_each_lang(scripts_dir, lang_dirent, lang_next) \
+ while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \
+ lang_next) \
+ if (lang_dirent.d_type == DT_DIR && \
+ (strcmp(lang_dirent.d_name, ".")) && \
+ (strcmp(lang_dirent.d_name, "..")))
+
+#define for_each_script(lang_dir, script_dirent, script_next) \
+ while (!readdir_r(lang_dir, &script_dirent, &script_next) && \
+ script_next) \
+ if (script_dirent.d_type != DT_DIR)
+
+
+#define RECORD_SUFFIX "-record"
+#define REPORT_SUFFIX "-report"
+
+struct script_desc {
+ struct list_head node;
+ char *name;
+ char *half_liner;
+ char *args;
+};
+
+LIST_HEAD(script_descs);
+
+static struct script_desc *script_desc__new(const char *name)
+{
+ struct script_desc *s = zalloc(sizeof(*s));
+
+ if (s != NULL)
+ s->name = strdup(name);
+
+ return s;
+}
+
+static void script_desc__delete(struct script_desc *s)
+{
+ free(s->name);
+ free(s);
+}
+
+static void script_desc__add(struct script_desc *s)
+{
+ list_add_tail(&s->node, &script_descs);
+}
+
+static struct script_desc *script_desc__find(const char *name)
+{
+ struct script_desc *s;
+
+ list_for_each_entry(s, &script_descs, node)
+ if (strcasecmp(s->name, name) == 0)
+ return s;
+ return NULL;
+}
+
+static struct script_desc *script_desc__findnew(const char *name)
+{
+ struct script_desc *s = script_desc__find(name);
+
+ if (s)
+ return s;
+
+ s = script_desc__new(name);
+ if (!s)
+ goto out_delete_desc;
+
+ script_desc__add(s);
+
+ return s;
+
+out_delete_desc:
+ script_desc__delete(s);
+
+ return NULL;
+}
+
+static char *ends_with(char *str, const char *suffix)
+{
+ size_t suffix_len = strlen(suffix);
+ char *p = str;
+
+ if (strlen(str) > suffix_len) {
+ p = str + strlen(str) - suffix_len;
+ if (!strncmp(p, suffix, suffix_len))
+ return p;
+ }
+
+ return NULL;
+}
+
+static char *ltrim(char *str)
+{
+ int len = strlen(str);
+
+ while (len && isspace(*str)) {
+ len--;
+ str++;
+ }
+
+ return str;
+}
+
+static int read_script_info(struct script_desc *desc, const char *filename)
+{
+ char line[BUFSIZ], *p;
+ FILE *fp;
+
+ fp = fopen(filename, "r");
+ if (!fp)
+ return -1;
+
+ while (fgets(line, sizeof(line), fp)) {
+ p = ltrim(line);
+ if (strlen(p) == 0)
+ continue;
+ if (*p != '#')
+ continue;
+ p++;
+ if (strlen(p) && *p == '!')
+ continue;
+
+ p = ltrim(p);
+ if (strlen(p) && p[strlen(p) - 1] == '\n')
+ p[strlen(p) - 1] = '\0';
+
+ if (!strncmp(p, "description:", strlen("description:"))) {
+ p += strlen("description:");
+ desc->half_liner = strdup(ltrim(p));
+ continue;
+ }
+
+ if (!strncmp(p, "args:", strlen("args:"))) {
+ p += strlen("args:");
+ desc->args = strdup(ltrim(p));
+ continue;
+ }
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+static int list_available_scripts(const struct option *opt __used,
+ const char *s __used, int unset __used)
+{
+ struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ char scripts_path[MAXPATHLEN];
+ DIR *scripts_dir, *lang_dir;
+ char script_path[MAXPATHLEN];
+ char lang_path[MAXPATHLEN];
+ struct script_desc *desc;
+ char first_half[BUFSIZ];
+ char *script_root;
+ char *str;
+
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+ scripts_dir = opendir(scripts_path);
+ if (!scripts_dir)
+ return -1;
+
+ for_each_lang(scripts_dir, lang_dirent, lang_next) {
+ snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
+ lang_dirent.d_name);
+ lang_dir = opendir(lang_path);
+ if (!lang_dir)
+ continue;
+
+ for_each_script(lang_dir, script_dirent, script_next) {
+ script_root = strdup(script_dirent.d_name);
+ str = ends_with(script_root, REPORT_SUFFIX);
+ if (str) {
+ *str = '\0';
+ desc = script_desc__findnew(script_root);
+ snprintf(script_path, MAXPATHLEN, "%s/%s",
+ lang_path, script_dirent.d_name);
+ read_script_info(desc, script_path);
+ }
+ free(script_root);
+ }
+ }
+
+ fprintf(stdout, "List of available trace scripts:\n");
+ list_for_each_entry(desc, &script_descs, node) {
+ sprintf(first_half, "%s %s", desc->name,
+ desc->args ? desc->args : "");
+ fprintf(stdout, " %-36s %s\n", first_half,
+ desc->half_liner ? desc->half_liner : "");
+ }
+
+ exit(0);
+}
+
+static char *get_script_path(const char *script_root, const char *suffix)
+{
+ struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ char scripts_path[MAXPATHLEN];
+ char script_path[MAXPATHLEN];
+ DIR *scripts_dir, *lang_dir;
+ char lang_path[MAXPATHLEN];
+ char *str, *__script_root;
+ char *path = NULL;
+
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+ scripts_dir = opendir(scripts_path);
+ if (!scripts_dir)
+ return NULL;
+
+ for_each_lang(scripts_dir, lang_dirent, lang_next) {
+ snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
+ lang_dirent.d_name);
+ lang_dir = opendir(lang_path);
+ if (!lang_dir)
+ continue;
+
+ for_each_script(lang_dir, script_dirent, script_next) {
+ __script_root = strdup(script_dirent.d_name);
+ str = ends_with(__script_root, suffix);
+ if (str) {
+ *str = '\0';
+ if (strcmp(__script_root, script_root))
+ continue;
+ snprintf(script_path, MAXPATHLEN, "%s/%s",
+ lang_path, script_dirent.d_name);
+ path = strdup(script_path);
+ free(__script_root);
+ break;
+ }
+ free(__script_root);
+ }
+ }
+
+ return path;
+}
+
+static const char * const trace_usage[] = {
+ "perf trace [<options>] <command>",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('L', "Latency", &latency_format,
+ "show latency attributes (irqs/preemption disabled, etc)"),
+ OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
+ list_available_scripts),
+ OPT_CALLBACK('s', "script", NULL, "name",
+ "script file name (lang:script name, script name, or *)",
+ parse_scriptname),
+ OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
+ "generate perf-trace.xx script in specified language"),
+ OPT_STRING('i', "input", &input_name, "file",
+ "input file name"),
+ OPT_BOOLEAN('d', "debug-ordering", &debug_ordering,
+ "check that samples time ordering is monotonic"),
+
+ OPT_END()
+};
+
+int cmd_trace(int argc, const char **argv, const char *prefix __used)
+{
+ struct perf_session *session;
+ const char *suffix = NULL;
+ const char **__argv;
+ char *script_path;
+ int i, err;
+
+ if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) {
+ if (argc < 3) {
+ fprintf(stderr,
+ "Please specify a record script\n");
+ return -1;
+ }
+ suffix = RECORD_SUFFIX;
+ }
+
+ if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) {
+ if (argc < 3) {
+ fprintf(stderr,
+ "Please specify a report script\n");
+ return -1;
+ }
+ suffix = REPORT_SUFFIX;
+ }
+
+ if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) {
+ char *record_script_path, *report_script_path;
+ int live_pipe[2];
+ pid_t pid;
+
+ record_script_path = get_script_path(argv[1], RECORD_SUFFIX);
+ if (!record_script_path) {
+ fprintf(stderr, "record script not found\n");
+ return -1;
+ }
+
+ report_script_path = get_script_path(argv[1], REPORT_SUFFIX);
+ if (!report_script_path) {
+ fprintf(stderr, "report script not found\n");
+ return -1;
+ }
+
+ if (pipe(live_pipe) < 0) {
+ perror("failed to create pipe");
+ exit(-1);
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ perror("failed to fork");
+ exit(-1);
+ }
+
+ if (!pid) {
+ dup2(live_pipe[1], 1);
+ close(live_pipe[0]);
+
+ __argv = malloc(5 * sizeof(const char *));
+ __argv[0] = "/bin/sh";
+ __argv[1] = record_script_path;
+ __argv[2] = "-o";
+ __argv[3] = "-";
+ __argv[4] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ exit(-1);
+ }
+
+ dup2(live_pipe[0], 0);
+ close(live_pipe[1]);
+
+ __argv = malloc((argc + 3) * sizeof(const char *));
+ __argv[0] = "/bin/sh";
+ __argv[1] = report_script_path;
+ for (i = 2; i < argc; i++)
+ __argv[i] = argv[i];
+ __argv[i++] = "-i";
+ __argv[i++] = "-";
+ __argv[i++] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ exit(-1);
+ }
+
+ if (suffix) {
+ script_path = get_script_path(argv[2], suffix);
+ if (!script_path) {
+ fprintf(stderr, "script not found\n");
+ return -1;
+ }
+
+ __argv = malloc((argc + 1) * sizeof(const char *));
+ __argv[0] = "/bin/sh";
+ __argv[1] = script_path;
+ for (i = 3; i < argc; i++)
+ __argv[i - 1] = argv[i];
+ __argv[argc - 1] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ exit(-1);
+ }
+
+ setup_scripting();
+
+ argc = parse_options(argc, argv, options, trace_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (symbol__init() < 0)
+ return -1;
+ if (!script_name)
+ setup_pager();
+
+ session = perf_session__new(input_name, O_RDONLY, 0, false);
+ if (session == NULL)
+ return -ENOMEM;
+
+ if (strcmp(input_name, "-") &&
+ !perf_session__has_traces(session, "record -R"))
+ return -EINVAL;
+
+ if (generate_script_lang) {
+ struct stat perf_stat;
+
+ int input = open(input_name, O_RDONLY);
+ if (input < 0) {
+ perror("failed to open file");
+ exit(-1);
+ }
+
+ err = fstat(input, &perf_stat);
+ if (err < 0) {
+ perror("failed to stat file");
+ exit(-1);
+ }
+
+ if (!perf_stat.st_size) {
+ fprintf(stderr, "zero-sized file, nothing to do!\n");
+ exit(0);
+ }
+
+ scripting_ops = script_spec__lookup(generate_script_lang);
+ if (!scripting_ops) {
+ fprintf(stderr, "invalid language specifier");
+ return -1;
+ }
+
+ err = scripting_ops->generate_script("perf-trace");
+ goto out;
+ }
+
+ if (script_name) {
+ err = scripting_ops->start_script(script_name, argc, argv);
+ if (err)
+ goto out;
+ pr_debug("perf trace started with script %s\n\n", script_name);
+ }
+
+ err = __cmd_trace(session);
+
+ perf_session__delete(session);
+ cleanup_scripting();
+out:
+ return err;
+}
diff --git a/smartt-perf/builtin.h b/smartt-perf/builtin.h
new file mode 100644
index 0000000..c7798c7
--- /dev/null
+++ b/smartt-perf/builtin.h
@@ -0,0 +1,39 @@
+#ifndef BUILTIN_H
+#define BUILTIN_H
+
+#include "util/util.h"
+#include "util/strbuf.h"
+
+extern const char perf_version_string[];
+extern const char perf_usage_string[];
+extern const char perf_more_info_string[];
+
+extern void list_common_cmds_help(void);
+extern const char *help_unknown_cmd(const char *cmd);
+extern void prune_packed_objects(int);
+extern int read_line_with_nul(char *buf, int size, FILE *file);
+extern int check_pager_config(const char *cmd);
+
+extern int cmd_annotate(int argc, const char **argv, const char *prefix);
+extern int cmd_bench(int argc, const char **argv, const char *prefix);
+extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix);
+extern int cmd_buildid_list(int argc, const char **argv, const char *prefix);
+extern int cmd_diff(int argc, const char **argv, const char *prefix);
+extern int cmd_help(int argc, const char **argv, const char *prefix);
+extern int cmd_sched(int argc, const char **argv, const char *prefix);
+extern int cmd_list(int argc, const char **argv, const char *prefix);
+extern int cmd_record(int argc, const char **argv, const char *prefix);
+extern int cmd_report(int argc, const char **argv, const char *prefix);
+extern int cmd_stat(int argc, const char **argv, const char *prefix);
+extern int cmd_timechart(int argc, const char **argv, const char *prefix);
+extern int cmd_top(int argc, const char **argv, const char *prefix);
+extern int cmd_script(int argc, const char **argv, const char *prefix);
+extern int cmd_version(int argc, const char **argv, const char *prefix);
+extern int cmd_probe(int argc, const char **argv, const char *prefix);
+extern int cmd_kmem(int argc, const char **argv, const char *prefix);
+extern int cmd_lock(int argc, const char **argv, const char *prefix);
+extern int cmd_kvm(int argc, const char **argv, const char *prefix);
+extern int cmd_test(int argc, const char **argv, const char *prefix);
+extern int cmd_inject(int argc, const char **argv, const char *prefix);
+
+#endif
diff --git a/smartt-perf/command-list.txt b/smartt-perf/command-list.txt
new file mode 100644
index 0000000..16b5088
--- /dev/null
+++ b/smartt-perf/command-list.txt
@@ -0,0 +1,24 @@
+#
+# List of known perf commands.
+# command name category [deprecated] [common]
+#
+perf-annotate mainporcelain common
+perf-archive mainporcelain common
+perf-bench mainporcelain common
+perf-buildid-cache mainporcelain common
+perf-buildid-list mainporcelain common
+perf-diff mainporcelain common
+perf-inject mainporcelain common
+perf-list mainporcelain common
+perf-sched mainporcelain common
+perf-record mainporcelain common
+perf-report mainporcelain common
+perf-stat mainporcelain common
+perf-timechart mainporcelain common
+perf-top mainporcelain common
+perf-script mainporcelain common
+perf-probe mainporcelain common
+perf-kmem mainporcelain common
+perf-lock mainporcelain common
+perf-kvm mainporcelain common
+perf-test mainporcelain common
diff --git a/smartt-perf/design.txt b/smartt-perf/design.txt
new file mode 100644
index 0000000..bd0bb1b
--- /dev/null
+++ b/smartt-perf/design.txt
@@ -0,0 +1,462 @@
+
+Performance Counters for Linux
+------------------------------
+
+Performance counters are special hardware registers available on most modern
+CPUs. These registers count the number of certain types of hw events: such
+as instructions executed, cachemisses suffered, or branches mis-predicted -
+without slowing down the kernel or applications. These registers can also
+trigger interrupts when a threshold number of events have passed - and can
+thus be used to profile the code that runs on that CPU.
+
+The Linux Performance Counter subsystem provides an abstraction of these
+hardware capabilities. It provides per task and per CPU counters, counter
+groups, and it provides event capabilities on top of those. It
+provides "virtual" 64-bit counters, regardless of the width of the
+underlying hardware counters.
+
+Performance counters are accessed via special file descriptors.
+There's one file descriptor per virtual counter used.
+
+The special file descriptor is opened via the perf_event_open()
+system call:
+
+ int sys_perf_event_open(struct perf_event_attr *hw_event_uptr,
+ pid_t pid, int cpu, int group_fd,
+ unsigned long flags);
+
+The syscall returns the new fd. The fd can be used via the normal
+VFS system calls: read() can be used to read the counter, fcntl()
+can be used to set the blocking mode, etc.
+
+Multiple counters can be kept open at a time, and the counters
+can be poll()ed.
+
+When creating a new counter fd, 'perf_event_attr' is:
+
+struct perf_event_attr {
+ /*
+ * The MSB of the config word signifies if the rest contains cpu
+ * specific (raw) counter configuration data, if unset, the next
+ * 7 bits are an event type and the rest of the bits are the event
+ * identifier.
+ */
+ __u64 config;
+
+ __u64 irq_period;
+ __u32 record_type;
+ __u32 read_format;
+
+ __u64 disabled : 1, /* off by default */
+ inherit : 1, /* children inherit it */
+ pinned : 1, /* must always be on PMU */
+ exclusive : 1, /* only group on PMU */
+ exclude_user : 1, /* don't count user */
+ exclude_kernel : 1, /* ditto kernel */
+ exclude_hv : 1, /* ditto hypervisor */
+ exclude_idle : 1, /* don't count when idle */
+ mmap : 1, /* include mmap data */
+ munmap : 1, /* include munmap data */
+ comm : 1, /* include comm data */
+
+ __reserved_1 : 52;
+
+ __u32 extra_config_len;
+ __u32 wakeup_events; /* wakeup every n events */
+
+ __u64 __reserved_2;
+ __u64 __reserved_3;
+};
+
+The 'config' field specifies what the counter should count. It
+is divided into 3 bit-fields:
+
+raw_type: 1 bit (most significant bit) 0x8000_0000_0000_0000
+type: 7 bits (next most significant) 0x7f00_0000_0000_0000
+event_id: 56 bits (least significant) 0x00ff_ffff_ffff_ffff
+
+If 'raw_type' is 1, then the counter will count a hardware event
+specified by the remaining 63 bits of event_config. The encoding is
+machine-specific.
+
+If 'raw_type' is 0, then the 'type' field says what kind of counter
+this is, with the following encoding:
+
+enum perf_event_types {
+ PERF_TYPE_HARDWARE = 0,
+ PERF_TYPE_SOFTWARE = 1,
+ PERF_TYPE_TRACEPOINT = 2,
+};
+
+A counter of PERF_TYPE_HARDWARE will count the hardware event
+specified by 'event_id':
+
+/*
+ * Generalized performance counter event types, used by the hw_event.event_id
+ * parameter of the sys_perf_event_open() syscall:
+ */
+enum hw_event_ids {
+ /*
+ * Common hardware events, generalized by the kernel:
+ */
+ PERF_COUNT_HW_CPU_CYCLES = 0,
+ PERF_COUNT_HW_INSTRUCTIONS = 1,
+ PERF_COUNT_HW_CACHE_REFERENCES = 2,
+ PERF_COUNT_HW_CACHE_MISSES = 3,
+ PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
+ PERF_COUNT_HW_BRANCH_MISSES = 5,
+ PERF_COUNT_HW_BUS_CYCLES = 6,
+};
+
+These are standardized types of events that work relatively uniformly
+on all CPUs that implement Performance Counters support under Linux,
+although there may be variations (e.g., different CPUs might count
+cache references and misses at different levels of the cache hierarchy).
+If a CPU is not able to count the selected event, then the system call
+will return -EINVAL.
+
+More hw_event_types are supported as well, but they are CPU-specific
+and accessed as raw events. For example, to count "External bus
+cycles while bus lock signal asserted" events on Intel Core CPUs, pass
+in a 0x4064 event_id value and set hw_event.raw_type to 1.
+
+A counter of type PERF_TYPE_SOFTWARE will count one of the available
+software events, selected by 'event_id':
+
+/*
+ * Special "software" counters provided by the kernel, even if the hardware
+ * does not support performance counters. These counters measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum sw_event_ids {
+ PERF_COUNT_SW_CPU_CLOCK = 0,
+ PERF_COUNT_SW_TASK_CLOCK = 1,
+ PERF_COUNT_SW_PAGE_FAULTS = 2,
+ PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
+ PERF_COUNT_SW_CPU_MIGRATIONS = 4,
+ PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
+ PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
+ PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
+ PERF_COUNT_SW_EMULATION_FAULTS = 8,
+};
+
+Counters of the type PERF_TYPE_TRACEPOINT are available when the ftrace event
+tracer is available, and event_id values can be obtained from
+/debug/tracing/events/*/*/id
+
+
+Counters come in two flavours: counting counters and sampling
+counters. A "counting" counter is one that is used for counting the
+number of events that occur, and is characterised by having
+irq_period = 0.
+
+
+A read() on a counter returns the current value of the counter and possible
+additional values as specified by 'read_format', each value is a u64 (8 bytes)
+in size.
+
+/*
+ * Bits that can be set in hw_event.read_format to request that
+ * reads on the counter should return the indicated quantities,
+ * in increasing order of bit value, after the counter value.
+ */
+enum perf_event_read_format {
+ PERF_FORMAT_TOTAL_TIME_ENABLED = 1,
+ PERF_FORMAT_TOTAL_TIME_RUNNING = 2,
+};
+
+Using these additional values one can establish the overcommit ratio for a
+particular counter allowing one to take the round-robin scheduling effect
+into account.
+
+
+A "sampling" counter is one that is set up to generate an interrupt
+every N events, where N is given by 'irq_period'. A sampling counter
+has irq_period > 0. The record_type controls what data is recorded on each
+interrupt:
+
+/*
+ * Bits that can be set in hw_event.record_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_record_format {
+ PERF_RECORD_IP = 1U << 0,
+ PERF_RECORD_TID = 1U << 1,
+ PERF_RECORD_TIME = 1U << 2,
+ PERF_RECORD_ADDR = 1U << 3,
+ PERF_RECORD_GROUP = 1U << 4,
+ PERF_RECORD_CALLCHAIN = 1U << 5,
+};
+
+Such (and other) events will be recorded in a ring-buffer, which is
+available to user-space using mmap() (see below).
+
+The 'disabled' bit specifies whether the counter starts out disabled
+or enabled. If it is initially disabled, it can be enabled by ioctl
+or prctl (see below).
+
+The 'inherit' bit, if set, specifies that this counter should count
+events on descendant tasks as well as the task specified. This only
+applies to new descendents, not to any existing descendents at the
+time the counter is created (nor to any new descendents of existing
+descendents).
+
+The 'pinned' bit, if set, specifies that the counter should always be
+on the CPU if at all possible. It only applies to hardware counters
+and only to group leaders. If a pinned counter cannot be put onto the
+CPU (e.g. because there are not enough hardware counters or because of
+a conflict with some other event), then the counter goes into an
+'error' state, where reads return end-of-file (i.e. read() returns 0)
+until the counter is subsequently enabled or disabled.
+
+The 'exclusive' bit, if set, specifies that when this counter's group
+is on the CPU, it should be the only group using the CPU's counters.
+In future, this will allow sophisticated monitoring programs to supply
+extra configuration information via 'extra_config_len' to exploit
+advanced features of the CPU's Performance Monitor Unit (PMU) that are
+not otherwise accessible and that might disrupt other hardware
+counters.
+
+The 'exclude_user', 'exclude_kernel' and 'exclude_hv' bits provide a
+way to request that counting of events be restricted to times when the
+CPU is in user, kernel and/or hypervisor mode.
+
+The 'mmap' and 'munmap' bits allow recording of PROT_EXEC mmap/munmap
+operations, these can be used to relate userspace IP addresses to actual
+code, even after the mapping (or even the whole process) is gone,
+these events are recorded in the ring-buffer (see below).
+
+The 'comm' bit allows tracking of process comm data on process creation.
+This too is recorded in the ring-buffer (see below).
+
+The 'pid' parameter to the perf_event_open() system call allows the
+counter to be specific to a task:
+
+ pid == 0: if the pid parameter is zero, the counter is attached to the
+ current task.
+
+ pid > 0: the counter is attached to a specific task (if the current task
+ has sufficient privilege to do so)
+
+ pid < 0: all tasks are counted (per cpu counters)
+
+The 'cpu' parameter allows a counter to be made specific to a CPU:
+
+ cpu >= 0: the counter is restricted to a specific CPU
+ cpu == -1: the counter counts on all CPUs
+
+(Note: the combination of 'pid == -1' and 'cpu == -1' is not valid.)
+
+A 'pid > 0' and 'cpu == -1' counter is a per task counter that counts
+events of that task and 'follows' that task to whatever CPU the task
+gets schedule to. Per task counters can be created by any user, for
+their own tasks.
+
+A 'pid == -1' and 'cpu == x' counter is a per CPU counter that counts
+all events on CPU-x. Per CPU counters need CAP_SYS_ADMIN privilege.
+
+The 'flags' parameter is currently unused and must be zero.
+
+The 'group_fd' parameter allows counter "groups" to be set up. A
+counter group has one counter which is the group "leader". The leader
+is created first, with group_fd = -1 in the perf_event_open call
+that creates it. The rest of the group members are created
+subsequently, with group_fd giving the fd of the group leader.
+(A single counter on its own is created with group_fd = -1 and is
+considered to be a group with only 1 member.)
+
+A counter group is scheduled onto the CPU as a unit, that is, it will
+only be put onto the CPU if all of the counters in the group can be
+put onto the CPU. This means that the values of the member counters
+can be meaningfully compared, added, divided (to get ratios), etc.,
+with each other, since they have counted events for the same set of
+executed instructions.
+
+
+Like stated, asynchronous events, like counter overflow or PROT_EXEC mmap
+tracking are logged into a ring-buffer. This ring-buffer is created and
+accessed through mmap().
+
+The mmap size should be 1+2^n pages, where the first page is a meta-data page
+(struct perf_event_mmap_page) that contains various bits of information such
+as where the ring-buffer head is.
+
+/*
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_event_mmap_page {
+ __u32 version; /* version number of this structure */
+ __u32 compat_version; /* lowest version this is compat with */
+
+ /*
+ * Bits needed to read the hw counters in user-space.
+ *
+ * u32 seq;
+ * s64 count;
+ *
+ * do {
+ * seq = pc->lock;
+ *
+ * barrier()
+ * if (pc->index) {
+ * count = pmc_read(pc->index - 1);
+ * count += pc->offset;
+ * } else
+ * goto regular_read;
+ *
+ * barrier();
+ * } while (pc->lock != seq);
+ *
+ * NOTE: for obvious reason this only works on self-monitoring
+ * processes.
+ */
+ __u32 lock; /* seqlock for synchronization */
+ __u32 index; /* hardware counter identifier */
+ __s64 offset; /* add to hardware counter value */
+
+ /*
+ * Control data for the mmap() data buffer.
+ *
+ * User-space reading this value should issue an rmb(), on SMP capable
+ * platforms, after reading this value -- see perf_event_wakeup().
+ */
+ __u32 data_head; /* head in the data section */
+};
+
+NOTE: the hw-counter userspace bits are arch specific and are currently only
+ implemented on powerpc.
+
+The following 2^n pages are the ring-buffer which contains events of the form:
+
+#define PERF_RECORD_MISC_KERNEL (1 << 0)
+#define PERF_RECORD_MISC_USER (1 << 1)
+#define PERF_RECORD_MISC_OVERFLOW (1 << 2)
+
+struct perf_event_header {
+ __u32 type;
+ __u16 misc;
+ __u16 size;
+};
+
+enum perf_event_type {
+
+ /*
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * };
+ */
+ PERF_RECORD_MMAP = 1,
+ PERF_RECORD_MUNMAP = 2,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * };
+ */
+ PERF_RECORD_COMM = 3,
+
+ /*
+ * When header.misc & PERF_RECORD_MISC_OVERFLOW the event_type field
+ * will be PERF_RECORD_*
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * { u64 ip; } && PERF_RECORD_IP
+ * { u32 pid, tid; } && PERF_RECORD_TID
+ * { u64 time; } && PERF_RECORD_TIME
+ * { u64 addr; } && PERF_RECORD_ADDR
+ *
+ * { u64 nr;
+ * { u64 event, val; } cnt[nr]; } && PERF_RECORD_GROUP
+ *
+ * { u16 nr,
+ * hv,
+ * kernel,
+ * user;
+ * u64 ips[nr]; } && PERF_RECORD_CALLCHAIN
+ * };
+ */
+};
+
+NOTE: PERF_RECORD_CALLCHAIN is arch specific and currently only implemented
+ on x86.
+
+Notification of new events is possible through poll()/select()/epoll() and
+fcntl() managing signals.
+
+Normally a notification is generated for every page filled, however one can
+additionally set perf_event_attr.wakeup_events to generate one every
+so many counter overflow events.
+
+Future work will include a splice() interface to the ring-buffer.
+
+
+Counters can be enabled and disabled in two ways: via ioctl and via
+prctl. When a counter is disabled, it doesn't count or generate
+events but does continue to exist and maintain its count value.
+
+An individual counter or counter group can be enabled with
+
+ ioctl(fd, PERF_EVENT_IOC_ENABLE);
+
+or disabled with
+
+ ioctl(fd, PERF_EVENT_IOC_DISABLE);
+
+Enabling or disabling the leader of a group enables or disables the
+whole group; that is, while the group leader is disabled, none of the
+counters in the group will count. Enabling or disabling a member of a
+group other than the leader only affects that counter - disabling an
+non-leader stops that counter from counting but doesn't affect any
+other counter.
+
+Additionally, non-inherited overflow counters can use
+
+ ioctl(fd, PERF_EVENT_IOC_REFRESH, nr);
+
+to enable a counter for 'nr' events, after which it gets disabled again.
+
+A process can enable or disable all the counter groups that are
+attached to it, using prctl:
+
+ prctl(PR_TASK_PERF_EVENTS_ENABLE);
+
+ prctl(PR_TASK_PERF_EVENTS_DISABLE);
+
+This applies to all counters on the current process, whether created
+by this process or by another, and doesn't affect any counters that
+this process has created on other processes. It only enables or
+disables the group leaders, not any other members in the groups.
+
+
+Arch requirements
+-----------------
+
+If your architecture does not have hardware performance metrics, you can
+still use the generic software counters based on hrtimers for sampling.
+
+So to start with, in order to add HAVE_PERF_EVENTS to your Kconfig, you
+will need at least this:
+ - asm/perf_event.h - a basic stub will suffice at first
+ - support for atomic64 types (and associated helper functions)
+ - set_perf_event_pending() implemented
+
+If your architecture does have hardware capabilities, you can override the
+weak stub hw_perf_event_init() to register hardware counters.
+
+Architectures that have d-cache aliassing issues, such as Sparc and ARM,
+should select PERF_USE_VMALLOC in order to avoid these for perf mmap().
diff --git a/smartt-perf/feature-tests.mak b/smartt-perf/feature-tests.mak
new file mode 100644
index 0000000..b041ca6
--- /dev/null
+++ b/smartt-perf/feature-tests.mak
@@ -0,0 +1,130 @@
+define SOURCE_HELLO
+#include <stdio.h>
+int main(void)
+{
+ return puts(\"hi\");
+}
+endef
+
+ifndef NO_DWARF
+define SOURCE_DWARF
+#include <dwarf.h>
+#include <elfutils/libdw.h>
+#include <elfutils/version.h>
+#ifndef _ELFUTILS_PREREQ
+#error
+#endif
+
+int main(void)
+{
+ Dwarf *dbg = dwarf_begin(0, DWARF_C_READ);
+ return (long)dbg;
+}
+endef
+endif
+
+define SOURCE_LIBELF
+#include <libelf.h>
+
+int main(void)
+{
+ Elf *elf = elf_begin(0, ELF_C_READ, 0);
+ return (long)elf;
+}
+endef
+
+define SOURCE_GLIBC
+#include <gnu/libc-version.h>
+
+int main(void)
+{
+ const char *version = gnu_get_libc_version();
+ return (long)version;
+}
+endef
+
+define SOURCE_ELF_MMAP
+#include <libelf.h>
+int main(void)
+{
+ Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0);
+ return (long)elf;
+}
+endef
+
+ifndef NO_NEWT
+define SOURCE_NEWT
+#include <newt.h>
+
+int main(void)
+{
+ newtInit();
+ newtCls();
+ return newtFinished();
+}
+endef
+endif
+
+ifndef NO_LIBPERL
+define SOURCE_PERL_EMBED
+#include <EXTERN.h>
+#include <perl.h>
+
+int main(void)
+{
+perl_alloc();
+return 0;
+}
+endef
+endif
+
+ifndef NO_LIBPYTHON
+define SOURCE_PYTHON_EMBED
+#include <Python.h>
+
+int main(void)
+{
+ Py_Initialize();
+ return 0;
+}
+endef
+endif
+
+define SOURCE_BFD
+#include <bfd.h>
+
+int main(void)
+{
+ bfd_demangle(0, 0, 0);
+ return 0;
+}
+endef
+
+define SOURCE_CPLUS_DEMANGLE
+extern char *cplus_demangle(const char *, int);
+
+int main(void)
+{
+ cplus_demangle(0, 0);
+ return 0;
+}
+endef
+
+define SOURCE_STRLCPY
+#include <stdlib.h>
+extern size_t strlcpy(char *dest, const char *src, size_t size);
+
+int main(void)
+{
+ strlcpy(NULL, NULL, 0);
+ return 0;
+}
+endef
+
+# try-cc
+# Usage: option = $(call try-cc, source-to-build, cc-options)
+try-cc = $(shell sh -c \
+ 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \
+ echo "$(1)" | \
+ $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \
+ rm -f "$$TMP"')
diff --git a/smartt-perf/perf-archive.sh b/smartt-perf/perf-archive.sh
new file mode 100644
index 0000000..677e59d
--- /dev/null
+++ b/smartt-perf/perf-archive.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# perf archive
+# Arnaldo Carvalho de Melo <acme@redhat.com>
+
+PERF_DATA=perf.data
+if [ $# -ne 0 ] ; then
+ PERF_DATA=$1
+fi
+
+#
+# PERF_BUILDID_DIR environment variable set by perf
+# path to buildid directory, default to $HOME/.debug
+#
+if [ -z $PERF_BUILDID_DIR ]; then
+ PERF_BUILDID_DIR=~/.debug/
+else
+ # append / to make substitutions work
+ PERF_BUILDID_DIR=$PERF_BUILDID_DIR/
+fi
+
+BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX)
+NOBUILDID=0000000000000000000000000000000000000000
+
+perf buildid-list -i $PERF_DATA --with-hits | grep -v "^$NOBUILDID " > $BUILDIDS
+if [ ! -s $BUILDIDS ] ; then
+ echo "perf archive: no build-ids found"
+ rm -f $BUILDIDS
+ exit 1
+fi
+
+MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX)
+
+cut -d ' ' -f 1 $BUILDIDS | \
+while read build_id ; do
+ linkname=$PERF_BUILDID_DIR.build-id/${build_id:0:2}/${build_id:2}
+ filename=$(readlink -f $linkname)
+ echo ${linkname#$PERF_BUILDID_DIR} >> $MANIFEST
+ echo ${filename#$PERF_BUILDID_DIR} >> $MANIFEST
+done
+
+tar cfj $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST
+rm -f $MANIFEST $BUILDIDS
+echo -e "Now please run:\n"
+echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n"
+echo "wherever you need to run 'perf report' on."
+exit 0
diff --git a/smartt-perf/perf.c b/smartt-perf/perf.c
new file mode 100644
index 0000000..595d0f4
--- /dev/null
+++ b/smartt-perf/perf.c
@@ -0,0 +1,508 @@
+/*
+ * perf.c
+ *
+ * Performance analysis utility.
+ *
+ * This is the main hub from which the sub-commands (perf stat,
+ * perf top, perf record, perf report, etc.) are started.
+ */
+#include "builtin.h"
+
+#include "util/exec_cmd.h"
+#include "util/cache.h"
+#include "util/quote.h"
+#include "util/run-command.h"
+#include "util/parse-events.h"
+#include "util/debugfs.h"
+
+const char perf_usage_string[] =
+ "perf [--version] [--help] COMMAND [ARGS]";
+
+const char perf_more_info_string[] =
+ "See 'perf help COMMAND' for more information on a specific command.";
+
+int use_browser = -1;
+static int use_pager = -1;
+
+struct pager_config {
+ const char *cmd;
+ int val;
+};
+
+static char debugfs_mntpt[MAXPATHLEN];
+
+static int pager_command_config(const char *var, const char *value, void *data)
+{
+ struct pager_config *c = data;
+ if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
+ c->val = perf_config_bool(var, value);
+ return 0;
+}
+
+/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
+int check_pager_config(const char *cmd)
+{
+ struct pager_config c;
+ c.cmd = cmd;
+ c.val = -1;
+ perf_config(pager_command_config, &c);
+ return c.val;
+}
+
+static int tui_command_config(const char *var, const char *value, void *data)
+{
+ struct pager_config *c = data;
+ if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd))
+ c->val = perf_config_bool(var, value);
+ return 0;
+}
+
+/* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */
+static int check_tui_config(const char *cmd)
+{
+ struct pager_config c;
+ c.cmd = cmd;
+ c.val = -1;
+ perf_config(tui_command_config, &c);
+ return c.val;
+}
+
+static void commit_pager_choice(void)
+{
+ switch (use_pager) {
+ case 0:
+ setenv("PERF_PAGER", "cat", 1);
+ break;
+ case 1:
+ /* setup_pager(); */
+ break;
+ default:
+ break;
+ }
+}
+
+static void set_debugfs_path(void)
+{
+ char *path;
+
+ path = getenv(PERF_DEBUGFS_ENVIRONMENT);
+ snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
+ "tracing/events");
+}
+
+static int handle_options(const char ***argv, int *argc, int *envchanged)
+{
+ int handled = 0;
+
+ while (*argc > 0) {
+ const char *cmd = (*argv)[0];
+ if (cmd[0] != '-')
+ break;
+
+ /*
+ * For legacy reasons, the "version" and "help"
+ * commands can be written with "--" prepended
+ * to make them look like flags.
+ */
+ if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
+ break;
+
+ /*
+ * Check remaining flags.
+ */
+ if (!prefixcmp(cmd, CMD_EXEC_PATH)) {
+ cmd += strlen(CMD_EXEC_PATH);
+ if (*cmd == '=')
+ perf_set_argv_exec_path(cmd + 1);
+ else {
+ puts(perf_exec_path());
+ exit(0);
+ }
+ } else if (!strcmp(cmd, "--html-path")) {
+ puts(system_path(PERF_HTML_PATH));
+ exit(0);
+ } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
+ use_pager = 1;
+ } else if (!strcmp(cmd, "--no-pager")) {
+ use_pager = 0;
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--perf-dir")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No directory given for --perf-dir.\n");
+ usage(perf_usage_string);
+ }
+ setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ handled++;
+ } else if (!prefixcmp(cmd, CMD_PERF_DIR)) {
+ setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--work-tree")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No directory given for --work-tree.\n");
+ usage(perf_usage_string);
+ }
+ setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, CMD_WORK_TREE)) {
+ setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--debugfs-dir")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No directory given for --debugfs-dir.\n");
+ usage(perf_usage_string);
+ }
+ strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);
+ debugfs_mntpt[MAXPATHLEN - 1] = '\0';
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) {
+ strncpy(debugfs_mntpt, cmd + strlen(CMD_DEBUGFS_DIR), MAXPATHLEN);
+ debugfs_mntpt[MAXPATHLEN - 1] = '\0';
+ if (envchanged)
+ *envchanged = 1;
+ } else {
+ fprintf(stderr, "Unknown option: %s\n", cmd);
+ usage(perf_usage_string);
+ }
+
+ (*argv)++;
+ (*argc)--;
+ handled++;
+ }
+ return handled;
+}
+
+static int handle_alias(int *argcp, const char ***argv)
+{
+ int envchanged = 0, ret = 0, saved_errno = errno;
+ int count, option_count;
+ const char **new_argv;
+ const char *alias_command;
+ char *alias_string;
+
+ alias_command = (*argv)[0];
+ alias_string = alias_lookup(alias_command);
+ if (alias_string) {
+ if (alias_string[0] == '!') {
+ if (*argcp > 1) {
+ struct strbuf buf;
+
+ strbuf_init(&buf, PATH_MAX);
+ strbuf_addstr(&buf, alias_string);
+ sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
+ free(alias_string);
+ alias_string = buf.buf;
+ }
+ ret = system(alias_string + 1);
+ if (ret >= 0 && WIFEXITED(ret) &&
+ WEXITSTATUS(ret) != 127)
+ exit(WEXITSTATUS(ret));
+ die("Failed to run '%s' when expanding alias '%s'",
+ alias_string + 1, alias_command);
+ }
+ count = split_cmdline(alias_string, &new_argv);
+ if (count < 0)
+ die("Bad alias.%s string", alias_command);
+ option_count = handle_options(&new_argv, &count, &envchanged);
+ if (envchanged)
+ die("alias '%s' changes environment variables\n"
+ "You can use '!perf' in the alias to do this.",
+ alias_command);
+ memmove(new_argv - option_count, new_argv,
+ count * sizeof(char *));
+ new_argv -= option_count;
+
+ if (count < 1)
+ die("empty alias for %s", alias_command);
+
+ if (!strcmp(alias_command, new_argv[0]))
+ die("recursive alias: %s", alias_command);
+
+ new_argv = realloc(new_argv, sizeof(char *) *
+ (count + *argcp + 1));
+ /* insert after command name */
+ memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
+ new_argv[count + *argcp] = NULL;
+
+ *argv = new_argv;
+ *argcp += count - 1;
+
+ ret = 1;
+ }
+
+ errno = saved_errno;
+
+ return ret;
+}
+
+const char perf_version_string[] = PERF_VERSION;
+
+#define RUN_SETUP (1<<0)
+#define USE_PAGER (1<<1)
+/*
+ * require working tree to be present -- anything uses this needs
+ * RUN_SETUP for reading from the configuration file.
+ */
+#define NEED_WORK_TREE (1<<2)
+
+struct cmd_struct {
+ const char *cmd;
+ int (*fn)(int, const char **, const char *);
+ int option;
+};
+
+static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
+{
+ int status;
+ struct stat st;
+ const char *prefix;
+
+ prefix = NULL;
+ if (p->option & RUN_SETUP)
+ prefix = NULL; /* setup_perf_directory(); */
+
+ if (use_browser == -1)
+ use_browser = check_tui_config(p->cmd);
+
+ if (use_pager == -1 && p->option & RUN_SETUP)
+ use_pager = check_pager_config(p->cmd);
+ if (use_pager == -1 && p->option & USE_PAGER)
+ use_pager = 1;
+ commit_pager_choice();
+ set_debugfs_path();
+
+ status = p->fn(argc, argv, prefix);
+ exit_browser(status);
+
+ if (status)
+ return status & 0xff;
+
+ /* Somebody closed stdout? */
+ if (fstat(fileno(stdout), &st))
+ return 0;
+ /* Ignore write errors for pipes and sockets.. */
+ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
+ return 0;
+
+ /* Check for ENOSPC and EIO errors.. */
+ if (fflush(stdout))
+ die("write failure on standard output: %s", strerror(errno));
+ if (ferror(stdout))
+ die("unknown write failure on standard output");
+ if (fclose(stdout))
+ die("close failed on standard output: %s", strerror(errno));
+ return 0;
+}
+
+static void handle_internal_command(int argc, const char **argv)
+{
+ const char *cmd = argv[0];
+ static struct cmd_struct commands[] = {
+ { "buildid-cache", cmd_buildid_cache, 0 },
+ { "buildid-list", cmd_buildid_list, 0 },
+ { "diff", cmd_diff, 0 },
+ { "help", cmd_help, 0 },
+ { "list", cmd_list, 0 },
+ { "record", cmd_record, 0 },
+ { "report", cmd_report, 0 },
+ { "bench", cmd_bench, 0 },
+ { "stat", cmd_stat, 0 },
+ { "timechart", cmd_timechart, 0 },
+ { "top", cmd_top, 0 },
+ { "annotate", cmd_annotate, 0 },
+ { "version", cmd_version, 0 },
+ { "script", cmd_script, 0 },
+ { "sched", cmd_sched, 0 },
+ { "probe", cmd_probe, 0 },
+ { "kmem", cmd_kmem, 0 },
+ { "lock", cmd_lock, 0 },
+ { "kvm", cmd_kvm, 0 },
+ { "test", cmd_test, 0 },
+ { "inject", cmd_inject, 0 },
+ };
+ unsigned int i;
+ static const char ext[] = STRIP_EXTENSION;
+
+ if (sizeof(ext) > 1) {
+ i = strlen(argv[0]) - strlen(ext);
+ if (i > 0 && !strcmp(argv[0] + i, ext)) {
+ char *argv0 = strdup(argv[0]);
+ argv[0] = cmd = argv0;
+ argv0[i] = '\0';
+ }
+ }
+
+ /* Turn "perf cmd --help" into "perf help cmd" */
+ if (argc > 1 && !strcmp(argv[1], "--help")) {
+ argv[1] = argv[0];
+ argv[0] = cmd = "help";
+ }
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ struct cmd_struct *p = commands+i;
+ if (strcmp(p->cmd, cmd))
+ continue;
+ exit(run_builtin(p, argc, argv));
+ }
+}
+
+static void execv_dashed_external(const char **argv)
+{
+ struct strbuf cmd = STRBUF_INIT;
+ const char *tmp;
+ int status;
+
+ strbuf_addf(&cmd, "perf-%s", argv[0]);
+
+ /*
+ * argv[0] must be the perf command, but the argv array
+ * belongs to the caller, and may be reused in
+ * subsequent loop iterations. Save argv[0] and
+ * restore it on error.
+ */
+ tmp = argv[0];
+ argv[0] = cmd.buf;
+
+ /*
+ * if we fail because the command is not found, it is
+ * OK to return. Otherwise, we just pass along the status code.
+ */
+ status = run_command_v_opt(argv, 0);
+ if (status != -ERR_RUN_COMMAND_EXEC) {
+ if (IS_RUN_COMMAND_ERR(status))
+ die("unable to run '%s'", argv[0]);
+ exit(-status);
+ }
+ errno = ENOENT; /* as if we called execvp */
+
+ argv[0] = tmp;
+
+ strbuf_release(&cmd);
+}
+
+static int run_argv(int *argcp, const char ***argv)
+{
+ int done_alias = 0;
+
+ while (1) {
+ /* See if it's an internal command */
+ handle_internal_command(*argcp, *argv);
+
+ /* .. then try the external ones */
+ execv_dashed_external(*argv);
+
+ /* It could be an alias -- this works around the insanity
+ * of overriding "perf log" with "perf show" by having
+ * alias.log = show
+ */
+ if (done_alias || !handle_alias(argcp, argv))
+ break;
+ done_alias = 1;
+ }
+
+ return done_alias;
+}
+
+/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
+static void get_debugfs_mntpt(void)
+{
+ const char *path = debugfs_mount(NULL);
+
+ if (path)
+ strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt));
+ else
+ debugfs_mntpt[0] = '\0';
+}
+
+int main(int argc, const char **argv)
+{
+ const char *cmd;
+
+ cmd = perf_extract_argv0_path(argv[0]);
+ if (!cmd)
+ cmd = "perf-help";
+ /* get debugfs mount point from /proc/mounts */
+ get_debugfs_mntpt();
+ /*
+ * "perf-xxxx" is the same as "perf xxxx", but we obviously:
+ *
+ * - cannot take flags in between the "perf" and the "xxxx".
+ * - cannot execute it externally (since it would just do
+ * the same thing over again)
+ *
+ * So we just directly call the internal command handler, and
+ * die if that one cannot handle it.
+ */
+ if (!prefixcmp(cmd, "perf-")) {
+ cmd += 5;
+ argv[0] = cmd;
+ handle_internal_command(argc, argv);
+ die("cannot handle %s internally", cmd);
+ }
+
+ /* Look for flags.. */
+ argv++;
+ argc--;
+ handle_options(&argv, &argc, NULL);
+ commit_pager_choice();
+ set_debugfs_path();
+ set_buildid_dir();
+
+ if (argc > 0) {
+ if (!prefixcmp(argv[0], "--"))
+ argv[0] += 2;
+ } else {
+ /* The user didn't specify a command; give them help */
+ printf("\n usage: %s\n\n", perf_usage_string);
+ list_common_cmds_help();
+ printf("\n %s\n\n", perf_more_info_string);
+ exit(1);
+ }
+ cmd = argv[0];
+
+ /*
+ * We use PATH to find perf commands, but we prepend some higher
+ * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH
+ * environment, and the $(perfexecdir) from the Makefile at build
+ * time.
+ */
+ setup_path();
+
+ while (1) {
+ static int done_help;
+ static int was_alias;
+
+ was_alias = run_argv(&argc, &argv);
+ if (errno != ENOENT)
+ break;
+
+ if (was_alias) {
+ fprintf(stderr, "Expansion of alias '%s' failed; "
+ "'%s' is not a perf-command\n",
+ cmd, argv[0]);
+ exit(1);
+ }
+ if (!done_help) {
+ cmd = argv[0] = help_unknown_cmd(cmd);
+ done_help = 1;
+ } else
+ break;
+ }
+
+ fprintf(stderr, "Failed to run command '%s': %s\n",
+ cmd, strerror(errno));
+
+ return 1;
+}
diff --git a/smartt-perf/perf.h b/smartt-perf/perf.h
new file mode 100644
index 0000000..428fa07
--- /dev/null
+++ b/smartt-perf/perf.h
@@ -0,0 +1,149 @@
+#ifndef _PERF_PERF_H
+#define _PERF_PERF_H
+
+struct winsize;
+
+void get_term_dimensions(struct winsize *ws);
+
+#if defined(__i386__)
+#include "arch/x86/include/asm/unistd.h"
+#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+#endif
+
+#if defined(__x86_64__)
+#include "arch/x86/include/asm/unistd.h"
+#define rmb() asm volatile("lfence" ::: "memory")
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+#endif
+
+#ifdef __powerpc__
+#include "arch/powerpc/include/asm/unistd.h"
+#define rmb() asm volatile ("sync" ::: "memory")
+#define cpu_relax() asm volatile ("" ::: "memory");
+#endif
+
+#ifdef __s390__
+#include "../../arch/s390/include/asm/unistd.h"
+#define rmb() asm volatile("bcr 15,0" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory");
+#endif
+
+#ifdef __sh__
+#include "../../arch/sh/include/asm/unistd.h"
+#if defined(__SH4A__) || defined(__SH5__)
+# define rmb() asm volatile("synco" ::: "memory")
+#else
+# define rmb() asm volatile("" ::: "memory")
+#endif
+#define cpu_relax() asm volatile("" ::: "memory")
+#endif
+
+#ifdef __hppa__
+#include "../../arch/parisc/include/asm/unistd.h"
+#define rmb() asm volatile("" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory");
+#endif
+
+#ifdef __sparc__
+#include "../../arch/sparc/include/asm/unistd.h"
+#define rmb() asm volatile("":::"memory")
+#define cpu_relax() asm volatile("":::"memory")
+#endif
+
+#ifdef __alpha__
+#include "../../arch/alpha/include/asm/unistd.h"
+#define rmb() asm volatile("mb" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory")
+#endif
+
+#ifdef __ia64__
+#include "arch/ia64/include/asm/unistd.h"
+#define rmb() asm volatile ("mf" ::: "memory")
+#define cpu_relax() asm volatile ("hint @pause" ::: "memory")
+#endif
+
+#ifdef __arm__
+#include "arch/arm/include/asm/unistd.h"
+/*
+ * Use the __kuser_memory_barrier helper in the CPU helper page. See
+ * arch/arm/kernel/entry-armv.S in the kernel source for details.
+ */
+#define rmb() ((void(*)(void))0xffff0fa0)()
+#define cpu_relax() asm volatile("":::"memory")
+#endif
+
+#ifdef __mips__
+#include "../../arch/mips/include/asm/unistd.h"
+#define rmb() asm volatile( \
+ ".set mips2\n\t" \
+ "sync\n\t" \
+ ".set mips0" \
+ : /* no output */ \
+ : /* no input */ \
+ : "memory")
+#define cpu_relax() asm volatile("" ::: "memory")
+#endif
+
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "../../include/linux/perf_event.h"
+#include "util/types.h"
+#include <stdbool.h>
+
+/*
+ * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
+ * counters in the current task.
+ */
+#define PR_TASK_PERF_EVENTS_DISABLE 31
+#define PR_TASK_PERF_EVENTS_ENABLE 32
+
+#ifndef NSEC_PER_SEC
+# define NSEC_PER_SEC 1000000000ULL
+#endif
+
+static inline unsigned long long rdclock(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+/*
+ * Pick up some kernel type conventions:
+ */
+#define __user
+#define asmlinkage
+
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+
+static inline int
+sys_perf_event_open(struct perf_event_attr *attr,
+ pid_t pid, int cpu, int group_fd,
+ unsigned long flags)
+{
+ attr->size = sizeof(*attr);
+ return syscall(__NR_perf_event_open, attr, pid, cpu,
+ group_fd, flags);
+}
+
+#define MAX_COUNTERS 256
+#define MAX_NR_CPUS 256
+
+struct ip_callchain {
+ u64 nr;
+ u64 ips[0];
+};
+
+extern bool perf_host, perf_guest;
+
+#endif
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/Context.c b/smartt-perf/scripts/perl/Perf-Trace-Util/Context.c
new file mode 100644
index 0000000..790ceba
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/Context.c
@@ -0,0 +1,135 @@
+/*
+ * This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the
+ * contents of Context.xs. Do not edit this file, edit Context.xs instead.
+ *
+ * ANY CHANGES MADE HERE WILL BE LOST!
+ *
+ */
+
+#line 1 "Context.xs"
+/*
+ * Context.xs. XS interfaces for perf script.
+ *
+ * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "../../../perf.h"
+#include "../../../util/trace-event.h"
+
+#ifndef PERL_UNUSED_VAR
+# define PERL_UNUSED_VAR(var) if (0) var = var
+#endif
+
+#line 42 "Context.c"
+
+XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prototypes */
+XS(XS_Perf__Trace__Context_common_pc)
+{
+#ifdef dVAR
+ dVAR; dXSARGS;
+#else
+ dXSARGS;
+#endif
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc", "context");
+ PERL_UNUSED_VAR(cv); /* -W */
+ {
+ struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
+ int RETVAL;
+ dXSTARG;
+
+ RETVAL = common_pc(context);
+ XSprePUSH; PUSHi((IV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+
+XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-prototypes */
+XS(XS_Perf__Trace__Context_common_flags)
+{
+#ifdef dVAR
+ dVAR; dXSARGS;
+#else
+ dXSARGS;
+#endif
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_flags", "context");
+ PERL_UNUSED_VAR(cv); /* -W */
+ {
+ struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
+ int RETVAL;
+ dXSTARG;
+
+ RETVAL = common_flags(context);
+ XSprePUSH; PUSHi((IV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+
+XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmissing-prototypes */
+XS(XS_Perf__Trace__Context_common_lock_depth)
+{
+#ifdef dVAR
+ dVAR; dXSARGS;
+#else
+ dXSARGS;
+#endif
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_lock_depth", "context");
+ PERL_UNUSED_VAR(cv); /* -W */
+ {
+ struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
+ int RETVAL;
+ dXSTARG;
+
+ RETVAL = common_lock_depth(context);
+ XSprePUSH; PUSHi((IV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+#ifdef __cplusplus
+extern "C"
+#endif
+XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */
+XS(boot_Perf__Trace__Context)
+{
+#ifdef dVAR
+ dVAR; dXSARGS;
+#else
+ dXSARGS;
+#endif
+ const char* file = __FILE__;
+
+ PERL_UNUSED_VAR(cv); /* -W */
+ PERL_UNUSED_VAR(items); /* -W */
+ XS_VERSION_BOOTCHECK ;
+
+ newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Context_common_pc, file, "$");
+ newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__Context_common_flags, file, "$");
+ newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Trace__Context_common_lock_depth, file, "$");
+ if (PL_unitcheckav)
+ call_list(PL_scopestack_ix, PL_unitcheckav);
+ XSRETURN_YES;
+}
+
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/Context.xs b/smartt-perf/scripts/perl/Perf-Trace-Util/Context.xs
new file mode 100644
index 0000000..c1e2ed1
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/Context.xs
@@ -0,0 +1,42 @@
+/*
+ * Context.xs. XS interfaces for perf script.
+ *
+ * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "../../../perf.h"
+#include "../../../util/script-event.h"
+
+MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context
+PROTOTYPES: ENABLE
+
+int
+common_pc(context)
+ struct scripting_context * context
+
+int
+common_flags(context)
+ struct scripting_context * context
+
+int
+common_lock_depth(context)
+ struct scripting_context * context
+
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/Makefile.PL b/smartt-perf/scripts/perl/Perf-Trace-Util/Makefile.PL
new file mode 100644
index 0000000..decdeb0
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/Makefile.PL
@@ -0,0 +1,17 @@
+use 5.010000;
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ NAME => 'Perf::Trace::Context',
+ VERSION_FROM => 'lib/Perf/Trace/Context.pm', # finds $VERSION
+ PREREQ_PM => {}, # e.g., Module::Name => 1.1
+ ($] >= 5.005 ? ## Add these new keywords supported since 5.005
+ (ABSTRACT_FROM => 'lib/Perf/Trace/Context.pm', # retrieve abstract from module
+ AUTHOR => 'Tom Zanussi <tzanussi@gmail.com>') : ()),
+ LIBS => [''], # e.g., '-lm'
+ DEFINE => '-I ../..', # e.g., '-DHAVE_SOMETHING'
+ INC => '-I.', # e.g., '-I. -I/usr/include/other'
+ # Un-comment this if you add C files to link with later:
+ OBJECT => 'Context.o', # link all the C files too
+);
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/README b/smartt-perf/scripts/perl/Perf-Trace-Util/README
new file mode 100644
index 0000000..2f0c7f3
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/README
@@ -0,0 +1,59 @@
+Perf-Trace-Util version 0.01
+============================
+
+This module contains utility functions for use with perf script.
+
+Core.pm and Util.pm are pure Perl modules; Core.pm contains routines
+that the core perf support for Perl calls on and should always be
+'used', while Util.pm contains useful but optional utility functions
+that scripts may want to use. Context.pm contains the Perl->C
+interface that allows scripts to access data in the embedding perf
+executable; scripts wishing to do that should 'use Context.pm'.
+
+The Perl->C perf interface is completely driven by Context.xs. If you
+want to add new Perl functions that end up accessing C data in the
+perf executable, you add desciptions of the new functions here.
+scripting_context is a pointer to the perf data in the perf executable
+that you want to access - it's passed as the second parameter,
+$context, to all handler functions.
+
+After you do that:
+
+ perl Makefile.PL # to create a Makefile for the next step
+ make # to create Context.c
+
+ edit Context.c to add const to the char* file = __FILE__ line in
+ XS(boot_Perf__Trace__Context) to silence a warning/error.
+
+ You can delete the Makefile, object files and anything else that was
+ generated e.g. blib and shared library, etc, except for of course
+ Context.c
+
+ You should then be able to run the normal perf make as usual.
+
+INSTALLATION
+
+Building perf with perf script Perl scripting should install this
+module in the right place.
+
+You should make sure libperl and ExtUtils/Embed.pm are installed first
+e.g. apt-get install libperl-dev or yum install perl-ExtUtils-Embed.
+
+DEPENDENCIES
+
+This module requires these other modules and libraries:
+
+ None
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2009 by Tom Zanussi <tzanussi@gmail.com>
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.10.0 or,
+at your option, any later version of Perl 5 you may have available.
+
+Alternatively, this software may be distributed under the terms of the
+GNU General Public License ("GPL") version 2 as published by the Free
+Software Foundation.
+
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm b/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm
new file mode 100644
index 0000000..4e2f603
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm
@@ -0,0 +1,55 @@
+package Perf::Trace::Context;
+
+use 5.010000;
+use strict;
+use warnings;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+our %EXPORT_TAGS = ( 'all' => [ qw(
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+
+our @EXPORT = qw(
+ common_pc common_flags common_lock_depth
+);
+
+our $VERSION = '0.01';
+
+require XSLoader;
+XSLoader::load('Perf::Trace::Context', $VERSION);
+
+1;
+__END__
+=head1 NAME
+
+Perf::Trace::Context - Perl extension for accessing functions in perf.
+
+=head1 SYNOPSIS
+
+ use Perf::Trace::Context;
+
+=head1 SEE ALSO
+
+Perf (script) documentation
+
+=head1 AUTHOR
+
+Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2009 by Tom Zanussi
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.10.0 or,
+at your option, any later version of Perl 5 you may have available.
+
+Alternatively, this software may be distributed under the terms of the
+GNU General Public License ("GPL") version 2 as published by the Free
+Software Foundation.
+
+=cut
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm b/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm
new file mode 100644
index 0000000..9158458
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm
@@ -0,0 +1,192 @@
+package Perf::Trace::Core;
+
+use 5.010000;
+use strict;
+use warnings;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+our %EXPORT_TAGS = ( 'all' => [ qw(
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+
+our @EXPORT = qw(
+define_flag_field define_flag_value flag_str dump_flag_fields
+define_symbolic_field define_symbolic_value symbol_str dump_symbolic_fields
+trace_flag_str
+);
+
+our $VERSION = '0.01';
+
+my %trace_flags = (0x00 => "NONE",
+ 0x01 => "IRQS_OFF",
+ 0x02 => "IRQS_NOSUPPORT",
+ 0x04 => "NEED_RESCHED",
+ 0x08 => "HARDIRQ",
+ 0x10 => "SOFTIRQ");
+
+sub trace_flag_str
+{
+ my ($value) = @_;
+
+ my $string;
+
+ my $print_delim = 0;
+
+ foreach my $idx (sort {$a <=> $b} keys %trace_flags) {
+ if (!$value && !$idx) {
+ $string .= "NONE";
+ last;
+ }
+
+ if ($idx && ($value & $idx) == $idx) {
+ if ($print_delim) {
+ $string .= " | ";
+ }
+ $string .= "$trace_flags{$idx}";
+ $print_delim = 1;
+ $value &= ~$idx;
+ }
+ }
+
+ return $string;
+}
+
+my %flag_fields;
+my %symbolic_fields;
+
+sub flag_str
+{
+ my ($event_name, $field_name, $value) = @_;
+
+ my $string;
+
+ if ($flag_fields{$event_name}{$field_name}) {
+ my $print_delim = 0;
+ foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event_name}{$field_name}{"values"}}) {
+ if (!$value && !$idx) {
+ $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
+ last;
+ }
+ if ($idx && ($value & $idx) == $idx) {
+ if ($print_delim && $flag_fields{$event_name}{$field_name}{'delim'}) {
+ $string .= " $flag_fields{$event_name}{$field_name}{'delim'} ";
+ }
+ $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
+ $print_delim = 1;
+ $value &= ~$idx;
+ }
+ }
+ }
+
+ return $string;
+}
+
+sub define_flag_field
+{
+ my ($event_name, $field_name, $delim) = @_;
+
+ $flag_fields{$event_name}{$field_name}{"delim"} = $delim;
+}
+
+sub define_flag_value
+{
+ my ($event_name, $field_name, $value, $field_str) = @_;
+
+ $flag_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
+}
+
+sub dump_flag_fields
+{
+ for my $event (keys %flag_fields) {
+ print "event $event:\n";
+ for my $field (keys %{$flag_fields{$event}}) {
+ print " field: $field:\n";
+ print " delim: $flag_fields{$event}{$field}{'delim'}\n";
+ foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event}{$field}{"values"}}) {
+ print " value $idx: $flag_fields{$event}{$field}{'values'}{$idx}\n";
+ }
+ }
+ }
+}
+
+sub symbol_str
+{
+ my ($event_name, $field_name, $value) = @_;
+
+ if ($symbolic_fields{$event_name}{$field_name}) {
+ foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event_name}{$field_name}{"values"}}) {
+ if (!$value && !$idx) {
+ return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
+ last;
+ }
+ if ($value == $idx) {
+ return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
+ }
+ }
+ }
+
+ return undef;
+}
+
+sub define_symbolic_field
+{
+ my ($event_name, $field_name) = @_;
+
+ # nothing to do, really
+}
+
+sub define_symbolic_value
+{
+ my ($event_name, $field_name, $value, $field_str) = @_;
+
+ $symbolic_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
+}
+
+sub dump_symbolic_fields
+{
+ for my $event (keys %symbolic_fields) {
+ print "event $event:\n";
+ for my $field (keys %{$symbolic_fields{$event}}) {
+ print " field: $field:\n";
+ foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event}{$field}{"values"}}) {
+ print " value $idx: $symbolic_fields{$event}{$field}{'values'}{$idx}\n";
+ }
+ }
+ }
+}
+
+1;
+__END__
+=head1 NAME
+
+Perf::Trace::Core - Perl extension for perf script
+
+=head1 SYNOPSIS
+
+ use Perf::Trace::Core
+
+=head1 SEE ALSO
+
+Perf (script) documentation
+
+=head1 AUTHOR
+
+Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2009 by Tom Zanussi
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.10.0 or,
+at your option, any later version of Perl 5 you may have available.
+
+Alternatively, this software may be distributed under the terms of the
+GNU General Public License ("GPL") version 2 as published by the Free
+Software Foundation.
+
+=cut
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm b/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm
new file mode 100644
index 0000000..0535001
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm
@@ -0,0 +1,94 @@
+package Perf::Trace::Util;
+
+use 5.010000;
+use strict;
+use warnings;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+our %EXPORT_TAGS = ( 'all' => [ qw(
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+
+our @EXPORT = qw(
+avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs
+clear_term
+);
+
+our $VERSION = '0.01';
+
+sub avg
+{
+ my ($total, $n) = @_;
+
+ return $total / $n;
+}
+
+my $NSECS_PER_SEC = 1000000000;
+
+sub nsecs
+{
+ my ($secs, $nsecs) = @_;
+
+ return $secs * $NSECS_PER_SEC + $nsecs;
+}
+
+sub nsecs_secs {
+ my ($nsecs) = @_;
+
+ return $nsecs / $NSECS_PER_SEC;
+}
+
+sub nsecs_nsecs {
+ my ($nsecs) = @_;
+
+ return $nsecs % $NSECS_PER_SEC;
+}
+
+sub nsecs_str {
+ my ($nsecs) = @_;
+
+ my $str = sprintf("%5u.%09u", nsecs_secs($nsecs), nsecs_nsecs($nsecs));
+
+ return $str;
+}
+
+sub clear_term
+{
+ print "\x1b[H\x1b[2J";
+}
+
+1;
+__END__
+=head1 NAME
+
+Perf::Trace::Util - Perl extension for perf script
+
+=head1 SYNOPSIS
+
+ use Perf::Trace::Util;
+
+=head1 SEE ALSO
+
+Perf (script) documentation
+
+=head1 AUTHOR
+
+Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2009 by Tom Zanussi
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.10.0 or,
+at your option, any later version of Perl 5 you may have available.
+
+Alternatively, this software may be distributed under the terms of the
+GNU General Public License ("GPL") version 2 as published by the Free
+Software Foundation.
+
+=cut
diff --git a/smartt-perf/scripts/perl/Perf-Trace-Util/typemap b/smartt-perf/scripts/perl/Perf-Trace-Util/typemap
new file mode 100644
index 0000000..8408368
--- /dev/null
+++ b/smartt-perf/scripts/perl/Perf-Trace-Util/typemap
@@ -0,0 +1 @@
+struct scripting_context * T_PTR
diff --git a/smartt-perf/scripts/perl/bin/check-perf-trace-record b/smartt-perf/scripts/perl/bin/check-perf-trace-record
new file mode 100644
index 0000000..423ad6a
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/check-perf-trace-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -a -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree
diff --git a/smartt-perf/scripts/perl/bin/failed-syscalls-record b/smartt-perf/scripts/perl/bin/failed-syscalls-record
new file mode 100644
index 0000000..8104895
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/failed-syscalls-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e raw_syscalls:sys_exit $@
diff --git a/smartt-perf/scripts/perl/bin/failed-syscalls-report b/smartt-perf/scripts/perl/bin/failed-syscalls-report
new file mode 100644
index 0000000..9f83cc1
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/failed-syscalls-report
@@ -0,0 +1,10 @@
+#!/bin/bash
+# description: system-wide failed syscalls
+# args: [comm]
+if [ $# -gt 0 ] ; then
+ if ! expr match "$1" "-" > /dev/null ; then
+ comm=$1
+ shift
+ fi
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm
diff --git a/smartt-perf/scripts/perl/bin/rw-by-file-record b/smartt-perf/scripts/perl/bin/rw-by-file-record
new file mode 100644
index 0000000..33efc86
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/rw-by-file-record
@@ -0,0 +1,3 @@
+#!/bin/bash
+perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
+
diff --git a/smartt-perf/scripts/perl/bin/rw-by-file-report b/smartt-perf/scripts/perl/bin/rw-by-file-report
new file mode 100644
index 0000000..77200b3
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/rw-by-file-report
@@ -0,0 +1,10 @@
+#!/bin/bash
+# description: r/w activity for a program, by file
+# args: <comm>
+if [ $# -lt 1 ] ; then
+ echo "usage: rw-by-file <comm>"
+ exit
+fi
+comm=$1
+shift
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm
diff --git a/smartt-perf/scripts/perl/bin/rw-by-pid-record b/smartt-perf/scripts/perl/bin/rw-by-pid-record
new file mode 100644
index 0000000..7cb9db2
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/rw-by-pid-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
diff --git a/smartt-perf/scripts/perl/bin/rw-by-pid-report b/smartt-perf/scripts/perl/bin/rw-by-pid-report
new file mode 100644
index 0000000..a27b9f3
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/rw-by-pid-report
@@ -0,0 +1,3 @@
+#!/bin/bash
+# description: system-wide r/w activity
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl
diff --git a/smartt-perf/scripts/perl/bin/rwtop-record b/smartt-perf/scripts/perl/bin/rwtop-record
new file mode 100644
index 0000000..7cb9db2
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/rwtop-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
diff --git a/smartt-perf/scripts/perl/bin/rwtop-report b/smartt-perf/scripts/perl/bin/rwtop-report
new file mode 100644
index 0000000..83e11ec
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/rwtop-report
@@ -0,0 +1,20 @@
+#!/bin/bash
+# description: system-wide r/w top
+# args: [interval]
+n_args=0
+for i in "$@"
+do
+ if expr match "$i" "-" > /dev/null ; then
+ break
+ fi
+ n_args=$(( $n_args + 1 ))
+done
+if [ "$n_args" -gt 1 ] ; then
+ echo "usage: rwtop-report [interval]"
+ exit
+fi
+if [ "$n_args" -gt 0 ] ; then
+ interval=$1
+ shift
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval
diff --git a/smartt-perf/scripts/perl/bin/wakeup-latency-record b/smartt-perf/scripts/perl/bin/wakeup-latency-record
new file mode 100644
index 0000000..464251a
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/wakeup-latency-record
@@ -0,0 +1,6 @@
+#!/bin/bash
+perf record -e sched:sched_switch -e sched:sched_wakeup $@
+
+
+
+
diff --git a/smartt-perf/scripts/perl/bin/wakeup-latency-report b/smartt-perf/scripts/perl/bin/wakeup-latency-report
new file mode 100644
index 0000000..889e813
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/wakeup-latency-report
@@ -0,0 +1,3 @@
+#!/bin/bash
+# description: system-wide min/max/avg wakeup latency
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl
diff --git a/smartt-perf/scripts/perl/bin/workqueue-stats-record b/smartt-perf/scripts/perl/bin/workqueue-stats-record
new file mode 100644
index 0000000..8edda90
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/workqueue-stats-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
diff --git a/smartt-perf/scripts/perl/bin/workqueue-stats-report b/smartt-perf/scripts/perl/bin/workqueue-stats-report
new file mode 100644
index 0000000..6d91411
--- /dev/null
+++ b/smartt-perf/scripts/perl/bin/workqueue-stats-report
@@ -0,0 +1,3 @@
+#!/bin/bash
+# description: workqueue stats (ins/exe/create/destroy)
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl
diff --git a/smartt-perf/scripts/perl/check-perf-trace.pl b/smartt-perf/scripts/perl/check-perf-trace.pl
new file mode 100644
index 0000000..4e7076c
--- /dev/null
+++ b/smartt-perf/scripts/perl/check-perf-trace.pl
@@ -0,0 +1,106 @@
+# perf script event handlers, generated by perf script -g perl
+# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+# This script tests basic functionality such as flag and symbol
+# strings, common_xxx() calls back into perf, begin, end, unhandled
+# events, etc. Basically, if this script runs successfully and
+# displays expected results, perl scripting support should be ok.
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Context;
+use Perf::Trace::Util;
+
+sub trace_begin
+{
+ print "trace_begin\n";
+}
+
+sub trace_end
+{
+ print "trace_end\n";
+
+ print_unhandled();
+}
+
+sub irq::softirq_entry
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $vec) = @_;
+
+ print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm);
+
+ print_uncommon($context);
+
+ printf("vec=%s\n",
+ symbol_str("irq::softirq_entry", "vec", $vec));
+}
+
+sub kmem::kmalloc
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $call_site, $ptr, $bytes_req, $bytes_alloc,
+ $gfp_flags) = @_;
+
+ print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm);
+
+ print_uncommon($context);
+
+ printf("call_site=%p, ptr=%p, bytes_req=%u, bytes_alloc=%u, ".
+ "gfp_flags=%s\n",
+ $call_site, $ptr, $bytes_req, $bytes_alloc,
+
+ flag_str("kmem::kmalloc", "gfp_flags", $gfp_flags));
+}
+
+# print trace fields not included in handler args
+sub print_uncommon
+{
+ my ($context) = @_;
+
+ printf("common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, ",
+ common_pc($context), trace_flag_str(common_flags($context)),
+ common_lock_depth($context));
+
+}
+
+my %unhandled;
+
+sub print_unhandled
+{
+ if ((scalar keys %unhandled) == 0) {
+ return;
+ }
+
+ print "\nunhandled events:\n\n";
+
+ printf("%-40s %10s\n", "event", "count");
+ printf("%-40s %10s\n", "----------------------------------------",
+ "-----------");
+
+ foreach my $event_name (keys %unhandled) {
+ printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
+ }
+}
+
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm) = @_;
+
+ $unhandled{$event_name}++;
+}
+
+sub print_header
+{
+ my ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;
+
+ printf("%-20s %5u %05u.%09u %8u %-20s ",
+ $event_name, $cpu, $secs, $nsecs, $pid, $comm);
+}
diff --git a/smartt-perf/scripts/perl/failed-syscalls.pl b/smartt-perf/scripts/perl/failed-syscalls.pl
new file mode 100644
index 0000000..94bc25a
--- /dev/null
+++ b/smartt-perf/scripts/perl/failed-syscalls.pl
@@ -0,0 +1,42 @@
+# failed system call counts
+# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# Displays system-wide failed system call totals
+# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Context;
+use Perf::Trace::Util;
+
+my $for_comm = shift;
+
+my %failed_syscalls;
+
+sub raw_syscalls::sys_exit
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $id, $ret) = @_;
+
+ if ($ret < 0) {
+ $failed_syscalls{$common_comm}++;
+ }
+}
+
+sub trace_end
+{
+ printf("\nfailed syscalls by comm:\n\n");
+
+ printf("%-20s %10s\n", "comm", "# errors");
+ printf("%-20s %6s %10s\n", "--------------------", "----------");
+
+ foreach my $comm (sort {$failed_syscalls{$b} <=> $failed_syscalls{$a}}
+ keys %failed_syscalls) {
+ next if ($for_comm && $comm ne $for_comm);
+
+ printf("%-20s %10s\n", $comm, $failed_syscalls{$comm});
+ }
+}
diff --git a/smartt-perf/scripts/perl/rw-by-file.pl b/smartt-perf/scripts/perl/rw-by-file.pl
new file mode 100644
index 0000000..74844ee
--- /dev/null
+++ b/smartt-perf/scripts/perl/rw-by-file.pl
@@ -0,0 +1,106 @@
+#!/usr/bin/perl -w
+# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+# Display r/w activity for files read/written to for a given program
+
+# The common_* event handler fields are the most useful fields common to
+# all events. They don't necessarily correspond to the 'common_*' fields
+# in the status files. Those fields not available as handler params can
+# be retrieved via script functions of the form get_common_*().
+
+use 5.010000;
+use strict;
+use warnings;
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Util;
+
+my $usage = "perf script -s rw-by-file.pl <comm>\n";
+
+my $for_comm = shift or die $usage;
+
+my %reads;
+my %writes;
+
+sub syscalls::sys_enter_read
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_;
+
+ if ($common_comm eq $for_comm) {
+ $reads{$fd}{bytes_requested} += $count;
+ $reads{$fd}{total_reads}++;
+ }
+}
+
+sub syscalls::sys_enter_write
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_;
+
+ if ($common_comm eq $for_comm) {
+ $writes{$fd}{bytes_written} += $count;
+ $writes{$fd}{total_writes}++;
+ }
+}
+
+sub trace_end
+{
+ printf("file read counts for $for_comm:\n\n");
+
+ printf("%6s %10s %10s\n", "fd", "# reads", "bytes_requested");
+ printf("%6s %10s %10s\n", "------", "----------", "-----------");
+
+ foreach my $fd (sort {$reads{$b}{bytes_requested} <=>
+ $reads{$a}{bytes_requested}} keys %reads) {
+ my $total_reads = $reads{$fd}{total_reads};
+ my $bytes_requested = $reads{$fd}{bytes_requested};
+ printf("%6u %10u %10u\n", $fd, $total_reads, $bytes_requested);
+ }
+
+ printf("\nfile write counts for $for_comm:\n\n");
+
+ printf("%6s %10s %10s\n", "fd", "# writes", "bytes_written");
+ printf("%6s %10s %10s\n", "------", "----------", "-----------");
+
+ foreach my $fd (sort {$writes{$b}{bytes_written} <=>
+ $writes{$a}{bytes_written}} keys %writes) {
+ my $total_writes = $writes{$fd}{total_writes};
+ my $bytes_written = $writes{$fd}{bytes_written};
+ printf("%6u %10u %10u\n", $fd, $total_writes, $bytes_written);
+ }
+
+ print_unhandled();
+}
+
+my %unhandled;
+
+sub print_unhandled
+{
+ if ((scalar keys %unhandled) == 0) {
+ return;
+ }
+
+ print "\nunhandled events:\n\n";
+
+ printf("%-40s %10s\n", "event", "count");
+ printf("%-40s %10s\n", "----------------------------------------",
+ "-----------");
+
+ foreach my $event_name (keys %unhandled) {
+ printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
+ }
+}
+
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm) = @_;
+
+ $unhandled{$event_name}++;
+}
+
+
diff --git a/smartt-perf/scripts/perl/rw-by-pid.pl b/smartt-perf/scripts/perl/rw-by-pid.pl
new file mode 100644
index 0000000..9db23c9
--- /dev/null
+++ b/smartt-perf/scripts/perl/rw-by-pid.pl
@@ -0,0 +1,184 @@
+#!/usr/bin/perl -w
+# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+# Display r/w activity for all processes
+
+# The common_* event handler fields are the most useful fields common to
+# all events. They don't necessarily correspond to the 'common_*' fields
+# in the status files. Those fields not available as handler params can
+# be retrieved via script functions of the form get_common_*().
+
+use 5.010000;
+use strict;
+use warnings;
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Util;
+
+my %reads;
+my %writes;
+
+sub syscalls::sys_exit_read
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $ret) = @_;
+
+ if ($ret > 0) {
+ $reads{$common_pid}{bytes_read} += $ret;
+ } else {
+ if (!defined ($reads{$common_pid}{bytes_read})) {
+ $reads{$common_pid}{bytes_read} = 0;
+ }
+ $reads{$common_pid}{errors}{$ret}++;
+ }
+}
+
+sub syscalls::sys_enter_read
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $fd, $buf, $count) = @_;
+
+ $reads{$common_pid}{bytes_requested} += $count;
+ $reads{$common_pid}{total_reads}++;
+ $reads{$common_pid}{comm} = $common_comm;
+}
+
+sub syscalls::sys_exit_write
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $ret) = @_;
+
+ if ($ret <= 0) {
+ $writes{$common_pid}{errors}{$ret}++;
+ }
+}
+
+sub syscalls::sys_enter_write
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $fd, $buf, $count) = @_;
+
+ $writes{$common_pid}{bytes_written} += $count;
+ $writes{$common_pid}{total_writes}++;
+ $writes{$common_pid}{comm} = $common_comm;
+}
+
+sub trace_end
+{
+ printf("read counts by pid:\n\n");
+
+ printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
+ "# reads", "bytes_requested", "bytes_read");
+ printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
+ "-----------", "----------", "----------");
+
+ foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
+ ($reads{$a}{bytes_read} || 0) } keys %reads) {
+ my $comm = $reads{$pid}{comm} || "";
+ my $total_reads = $reads{$pid}{total_reads} || 0;
+ my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
+ my $bytes_read = $reads{$pid}{bytes_read} || 0;
+
+ printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
+ $total_reads, $bytes_requested, $bytes_read);
+ }
+
+ printf("\nfailed reads by pid:\n\n");
+
+ printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
+ printf("%6s %20s %6s %10s\n", "------", "--------------------",
+ "------", "----------");
+
+ my @errcounts = ();
+
+ foreach my $pid (keys %reads) {
+ foreach my $error (keys %{$reads{$pid}{errors}}) {
+ my $comm = $reads{$pid}{comm} || "";
+ my $errcount = $reads{$pid}{errors}{$error} || 0;
+ push @errcounts, [$pid, $comm, $error, $errcount];
+ }
+ }
+
+ @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
+
+ for my $i (0 .. $#errcounts) {
+ printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
+ $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
+ }
+
+ printf("\nwrite counts by pid:\n\n");
+
+ printf("%6s %20s %10s %10s\n", "pid", "comm",
+ "# writes", "bytes_written");
+ printf("%6s %-20s %10s %10s\n", "------", "--------------------",
+ "-----------", "----------");
+
+ foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
+ ($writes{$a}{bytes_written} || 0)} keys %writes) {
+ my $comm = $writes{$pid}{comm} || "";
+ my $total_writes = $writes{$pid}{total_writes} || 0;
+ my $bytes_written = $writes{$pid}{bytes_written} || 0;
+
+ printf("%6s %-20s %10s %10s\n", $pid, $comm,
+ $total_writes, $bytes_written);
+ }
+
+ printf("\nfailed writes by pid:\n\n");
+
+ printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
+ printf("%6s %20s %6s %10s\n", "------", "--------------------",
+ "------", "----------");
+
+ @errcounts = ();
+
+ foreach my $pid (keys %writes) {
+ foreach my $error (keys %{$writes{$pid}{errors}}) {
+ my $comm = $writes{$pid}{comm} || "";
+ my $errcount = $writes{$pid}{errors}{$error} || 0;
+ push @errcounts, [$pid, $comm, $error, $errcount];
+ }
+ }
+
+ @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
+
+ for my $i (0 .. $#errcounts) {
+ printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
+ $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
+ }
+
+ print_unhandled();
+}
+
+my %unhandled;
+
+sub print_unhandled
+{
+ if ((scalar keys %unhandled) == 0) {
+ return;
+ }
+
+ print "\nunhandled events:\n\n";
+
+ printf("%-40s %10s\n", "event", "count");
+ printf("%-40s %10s\n", "----------------------------------------",
+ "-----------");
+
+ foreach my $event_name (keys %unhandled) {
+ printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
+ }
+}
+
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm) = @_;
+
+ $unhandled{$event_name}++;
+}
diff --git a/smartt-perf/scripts/perl/rwtop.pl b/smartt-perf/scripts/perl/rwtop.pl
new file mode 100644
index 0000000..4bb3ecd
--- /dev/null
+++ b/smartt-perf/scripts/perl/rwtop.pl
@@ -0,0 +1,199 @@
+#!/usr/bin/perl -w
+# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+# read/write top
+#
+# Periodically displays system-wide r/w call activity, broken down by
+# pid. If an [interval] arg is specified, the display will be
+# refreshed every [interval] seconds. The default interval is 3
+# seconds.
+
+use 5.010000;
+use strict;
+use warnings;
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Util;
+
+my $default_interval = 3;
+my $nlines = 20;
+my $print_thread;
+my $print_pending = 0;
+
+my %reads;
+my %writes;
+
+my $interval = shift;
+if (!$interval) {
+ $interval = $default_interval;
+}
+
+sub syscalls::sys_exit_read
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $ret) = @_;
+
+ print_check();
+
+ if ($ret > 0) {
+ $reads{$common_pid}{bytes_read} += $ret;
+ } else {
+ if (!defined ($reads{$common_pid}{bytes_read})) {
+ $reads{$common_pid}{bytes_read} = 0;
+ }
+ $reads{$common_pid}{errors}{$ret}++;
+ }
+}
+
+sub syscalls::sys_enter_read
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $fd, $buf, $count) = @_;
+
+ print_check();
+
+ $reads{$common_pid}{bytes_requested} += $count;
+ $reads{$common_pid}{total_reads}++;
+ $reads{$common_pid}{comm} = $common_comm;
+}
+
+sub syscalls::sys_exit_write
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $ret) = @_;
+
+ print_check();
+
+ if ($ret <= 0) {
+ $writes{$common_pid}{errors}{$ret}++;
+ }
+}
+
+sub syscalls::sys_enter_write
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $fd, $buf, $count) = @_;
+
+ print_check();
+
+ $writes{$common_pid}{bytes_written} += $count;
+ $writes{$common_pid}{total_writes}++;
+ $writes{$common_pid}{comm} = $common_comm;
+}
+
+sub trace_begin
+{
+ $SIG{ALRM} = \&set_print_pending;
+ alarm 1;
+}
+
+sub trace_end
+{
+ print_unhandled();
+ print_totals();
+}
+
+sub print_check()
+{
+ if ($print_pending == 1) {
+ $print_pending = 0;
+ print_totals();
+ }
+}
+
+sub set_print_pending()
+{
+ $print_pending = 1;
+ alarm $interval;
+}
+
+sub print_totals
+{
+ my $count;
+
+ $count = 0;
+
+ clear_term();
+
+ printf("\nread counts by pid:\n\n");
+
+ printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
+ "# reads", "bytes_req", "bytes_read");
+ printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
+ "----------", "----------", "----------");
+
+ foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
+ ($reads{$a}{bytes_read} || 0) } keys %reads) {
+ my $comm = $reads{$pid}{comm} || "";
+ my $total_reads = $reads{$pid}{total_reads} || 0;
+ my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
+ my $bytes_read = $reads{$pid}{bytes_read} || 0;
+
+ printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
+ $total_reads, $bytes_requested, $bytes_read);
+
+ if (++$count == $nlines) {
+ last;
+ }
+ }
+
+ $count = 0;
+
+ printf("\nwrite counts by pid:\n\n");
+
+ printf("%6s %20s %10s %13s\n", "pid", "comm",
+ "# writes", "bytes_written");
+ printf("%6s %-20s %10s %13s\n", "------", "--------------------",
+ "----------", "-------------");
+
+ foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
+ ($writes{$a}{bytes_written} || 0)} keys %writes) {
+ my $comm = $writes{$pid}{comm} || "";
+ my $total_writes = $writes{$pid}{total_writes} || 0;
+ my $bytes_written = $writes{$pid}{bytes_written} || 0;
+
+ printf("%6s %-20s %10s %13s\n", $pid, $comm,
+ $total_writes, $bytes_written);
+
+ if (++$count == $nlines) {
+ last;
+ }
+ }
+
+ %reads = ();
+ %writes = ();
+}
+
+my %unhandled;
+
+sub print_unhandled
+{
+ if ((scalar keys %unhandled) == 0) {
+ return;
+ }
+
+ print "\nunhandled events:\n\n";
+
+ printf("%-40s %10s\n", "event", "count");
+ printf("%-40s %10s\n", "----------------------------------------",
+ "-----------");
+
+ foreach my $event_name (keys %unhandled) {
+ printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
+ }
+}
+
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm) = @_;
+
+ $unhandled{$event_name}++;
+}
diff --git a/smartt-perf/scripts/perl/wakeup-latency.pl b/smartt-perf/scripts/perl/wakeup-latency.pl
new file mode 100644
index 0000000..d9143dc
--- /dev/null
+++ b/smartt-perf/scripts/perl/wakeup-latency.pl
@@ -0,0 +1,107 @@
+#!/usr/bin/perl -w
+# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+# Display avg/min/max wakeup latency
+
+# The common_* event handler fields are the most useful fields common to
+# all events. They don't necessarily correspond to the 'common_*' fields
+# in the status files. Those fields not available as handler params can
+# be retrieved via script functions of the form get_common_*().
+
+use 5.010000;
+use strict;
+use warnings;
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Util;
+
+my %last_wakeup;
+
+my $max_wakeup_latency;
+my $min_wakeup_latency;
+my $total_wakeup_latency = 0;
+my $total_wakeups = 0;
+
+sub sched::sched_switch
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $prev_comm, $prev_pid, $prev_prio, $prev_state, $next_comm, $next_pid,
+ $next_prio) = @_;
+
+ my $wakeup_ts = $last_wakeup{$common_cpu}{ts};
+ if ($wakeup_ts) {
+ my $switch_ts = nsecs($common_secs, $common_nsecs);
+ my $wakeup_latency = $switch_ts - $wakeup_ts;
+ if ($wakeup_latency > $max_wakeup_latency) {
+ $max_wakeup_latency = $wakeup_latency;
+ }
+ if ($wakeup_latency < $min_wakeup_latency) {
+ $min_wakeup_latency = $wakeup_latency;
+ }
+ $total_wakeup_latency += $wakeup_latency;
+ $total_wakeups++;
+ }
+ $last_wakeup{$common_cpu}{ts} = 0;
+}
+
+sub sched::sched_wakeup
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $comm, $pid, $prio, $success, $target_cpu) = @_;
+
+ $last_wakeup{$target_cpu}{ts} = nsecs($common_secs, $common_nsecs);
+}
+
+sub trace_begin
+{
+ $min_wakeup_latency = 1000000000;
+ $max_wakeup_latency = 0;
+}
+
+sub trace_end
+{
+ printf("wakeup_latency stats:\n\n");
+ print "total_wakeups: $total_wakeups\n";
+ if ($total_wakeups) {
+ printf("avg_wakeup_latency (ns): %u\n",
+ avg($total_wakeup_latency, $total_wakeups));
+ } else {
+ printf("avg_wakeup_latency (ns): N/A\n");
+ }
+ printf("min_wakeup_latency (ns): %u\n", $min_wakeup_latency);
+ printf("max_wakeup_latency (ns): %u\n", $max_wakeup_latency);
+
+ print_unhandled();
+}
+
+my %unhandled;
+
+sub print_unhandled
+{
+ if ((scalar keys %unhandled) == 0) {
+ return;
+ }
+
+ print "\nunhandled events:\n\n";
+
+ printf("%-40s %10s\n", "event", "count");
+ printf("%-40s %10s\n", "----------------------------------------",
+ "-----------");
+
+ foreach my $event_name (keys %unhandled) {
+ printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
+ }
+}
+
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm) = @_;
+
+ $unhandled{$event_name}++;
+}
diff --git a/smartt-perf/scripts/perl/workqueue-stats.pl b/smartt-perf/scripts/perl/workqueue-stats.pl
new file mode 100644
index 0000000..a8eaff5
--- /dev/null
+++ b/smartt-perf/scripts/perl/workqueue-stats.pl
@@ -0,0 +1,129 @@
+#!/usr/bin/perl -w
+# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+# Displays workqueue stats
+#
+# Usage:
+#
+# perf record -c 1 -f -a -R -e workqueue:workqueue_creation -e
+# workqueue:workqueue_destruction -e workqueue:workqueue_execution
+# -e workqueue:workqueue_insertion
+#
+# perf script -p -s tools/perf/scripts/perl/workqueue-stats.pl
+
+use 5.010000;
+use strict;
+use warnings;
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Util;
+
+my @cpus;
+
+sub workqueue::workqueue_destruction
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $thread_comm, $thread_pid) = @_;
+
+ $cpus[$common_cpu]{$thread_pid}{destroyed}++;
+ $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
+}
+
+sub workqueue::workqueue_creation
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $thread_comm, $thread_pid, $cpu) = @_;
+
+ $cpus[$common_cpu]{$thread_pid}{created}++;
+ $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
+}
+
+sub workqueue::workqueue_execution
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $thread_comm, $thread_pid, $func) = @_;
+
+ $cpus[$common_cpu]{$thread_pid}{executed}++;
+ $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
+}
+
+sub workqueue::workqueue_insertion
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $thread_comm, $thread_pid, $func) = @_;
+
+ $cpus[$common_cpu]{$thread_pid}{inserted}++;
+ $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
+}
+
+sub trace_end
+{
+ print "workqueue work stats:\n\n";
+ my $cpu = 0;
+ printf("%3s %6s %6s\t%-20s\n", "cpu", "ins", "exec", "name");
+ printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----");
+ foreach my $pidhash (@cpus) {
+ while ((my $pid, my $wqhash) = each %$pidhash) {
+ my $ins = $$wqhash{'inserted'} || 0;
+ my $exe = $$wqhash{'executed'} || 0;
+ my $comm = $$wqhash{'comm'} || "";
+ if ($ins || $exe) {
+ printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm);
+ }
+ }
+ $cpu++;
+ }
+
+ $cpu = 0;
+ print "\nworkqueue lifecycle stats:\n\n";
+ printf("%3s %6s %6s\t%-20s\n", "cpu", "created", "destroyed", "name");
+ printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----");
+ foreach my $pidhash (@cpus) {
+ while ((my $pid, my $wqhash) = each %$pidhash) {
+ my $created = $$wqhash{'created'} || 0;
+ my $destroyed = $$wqhash{'destroyed'} || 0;
+ my $comm = $$wqhash{'comm'} || "";
+ if ($created || $destroyed) {
+ printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed,
+ $comm);
+ }
+ }
+ $cpu++;
+ }
+
+ print_unhandled();
+}
+
+my %unhandled;
+
+sub print_unhandled
+{
+ if ((scalar keys %unhandled) == 0) {
+ return;
+ }
+
+ print "\nunhandled events:\n\n";
+
+ printf("%-40s %10s\n", "event", "count");
+ printf("%-40s %10s\n", "----------------------------------------",
+ "-----------");
+
+ foreach my $event_name (keys %unhandled) {
+ printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
+ }
+}
+
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm) = @_;
+
+ $unhandled{$event_name}++;
+}
diff --git a/smartt-perf/scripts/python/Perf-Trace-Util/Context.c b/smartt-perf/scripts/python/Perf-Trace-Util/Context.c
new file mode 100644
index 0000000..315067b
--- /dev/null
+++ b/smartt-perf/scripts/python/Perf-Trace-Util/Context.c
@@ -0,0 +1,88 @@
+/*
+ * Context.c. Python interfaces for perf script.
+ *
+ * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <Python.h>
+#include "../../../perf.h"
+#include "../../../util/trace-event.h"
+
+PyMODINIT_FUNC initperf_trace_context(void);
+
+static PyObject *perf_trace_context_common_pc(PyObject *self, PyObject *args)
+{
+ static struct scripting_context *scripting_context;
+ PyObject *context;
+ int retval;
+
+ if (!PyArg_ParseTuple(args, "O", &context))
+ return NULL;
+
+ scripting_context = PyCObject_AsVoidPtr(context);
+ retval = common_pc(scripting_context);
+
+ return Py_BuildValue("i", retval);
+}
+
+static PyObject *perf_trace_context_common_flags(PyObject *self,
+ PyObject *args)
+{
+ static struct scripting_context *scripting_context;
+ PyObject *context;
+ int retval;
+
+ if (!PyArg_ParseTuple(args, "O", &context))
+ return NULL;
+
+ scripting_context = PyCObject_AsVoidPtr(context);
+ retval = common_flags(scripting_context);
+
+ return Py_BuildValue("i", retval);
+}
+
+static PyObject *perf_trace_context_common_lock_depth(PyObject *self,
+ PyObject *args)
+{
+ static struct scripting_context *scripting_context;
+ PyObject *context;
+ int retval;
+
+ if (!PyArg_ParseTuple(args, "O", &context))
+ return NULL;
+
+ scripting_context = PyCObject_AsVoidPtr(context);
+ retval = common_lock_depth(scripting_context);
+
+ return Py_BuildValue("i", retval);
+}
+
+static PyMethodDef ContextMethods[] = {
+ { "common_pc", perf_trace_context_common_pc, METH_VARARGS,
+ "Get the common preempt count event field value."},
+ { "common_flags", perf_trace_context_common_flags, METH_VARARGS,
+ "Get the common flags event field value."},
+ { "common_lock_depth", perf_trace_context_common_lock_depth,
+ METH_VARARGS, "Get the common lock depth event field value."},
+ { NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC initperf_trace_context(void)
+{
+ (void) Py_InitModule("perf_trace_context", ContextMethods);
+}
diff --git a/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py b/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py
new file mode 100644
index 0000000..de7211e
--- /dev/null
+++ b/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py
@@ -0,0 +1,121 @@
+# Core.py - Python extension for perf script, core functions
+#
+# Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com>
+#
+# This software may be distributed under the terms of the GNU General
+# Public License ("GPL") version 2 as published by the Free Software
+# Foundation.
+
+from collections import defaultdict
+
+def autodict():
+ return defaultdict(autodict)
+
+flag_fields = autodict()
+symbolic_fields = autodict()
+
+def define_flag_field(event_name, field_name, delim):
+ flag_fields[event_name][field_name]['delim'] = delim
+
+def define_flag_value(event_name, field_name, value, field_str):
+ flag_fields[event_name][field_name]['values'][value] = field_str
+
+def define_symbolic_field(event_name, field_name):
+ # nothing to do, really
+ pass
+
+def define_symbolic_value(event_name, field_name, value, field_str):
+ symbolic_fields[event_name][field_name]['values'][value] = field_str
+
+def flag_str(event_name, field_name, value):
+ string = ""
+
+ if flag_fields[event_name][field_name]:
+ print_delim = 0
+ keys = flag_fields[event_name][field_name]['values'].keys()
+ keys.sort()
+ for idx in keys:
+ if not value and not idx:
+ string += flag_fields[event_name][field_name]['values'][idx]
+ break
+ if idx and (value & idx) == idx:
+ if print_delim and flag_fields[event_name][field_name]['delim']:
+ string += " " + flag_fields[event_name][field_name]['delim'] + " "
+ string += flag_fields[event_name][field_name]['values'][idx]
+ print_delim = 1
+ value &= ~idx
+
+ return string
+
+def symbol_str(event_name, field_name, value):
+ string = ""
+
+ if symbolic_fields[event_name][field_name]:
+ keys = symbolic_fields[event_name][field_name]['values'].keys()
+ keys.sort()
+ for idx in keys:
+ if not value and not idx:
+ string = symbolic_fields[event_name][field_name]['values'][idx]
+ break
+ if (value == idx):
+ string = symbolic_fields[event_name][field_name]['values'][idx]
+ break
+
+ return string
+
+trace_flags = { 0x00: "NONE", \
+ 0x01: "IRQS_OFF", \
+ 0x02: "IRQS_NOSUPPORT", \
+ 0x04: "NEED_RESCHED", \
+ 0x08: "HARDIRQ", \
+ 0x10: "SOFTIRQ" }
+
+def trace_flag_str(value):
+ string = ""
+ print_delim = 0
+
+ keys = trace_flags.keys()
+
+ for idx in keys:
+ if not value and not idx:
+ string += "NONE"
+ break
+
+ if idx and (value & idx) == idx:
+ if print_delim:
+ string += " | ";
+ string += trace_flags[idx]
+ print_delim = 1
+ value &= ~idx
+
+ return string
+
+
+def taskState(state):
+ states = {
+ 0 : "R",
+ 1 : "S",
+ 2 : "D",
+ 64: "DEAD"
+ }
+
+ if state not in states:
+ return "Unknown"
+
+ return states[state]
+
+
+class EventHeaders:
+ def __init__(self, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm):
+ self.cpu = common_cpu
+ self.secs = common_secs
+ self.nsecs = common_nsecs
+ self.pid = common_pid
+ self.comm = common_comm
+
+ def ts(self):
+ return (self.secs * (10 ** 9)) + self.nsecs
+
+ def ts_format(self):
+ return "%d.%d" % (self.secs, int(self.nsecs / 1000))
diff --git a/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py b/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py
new file mode 100644
index 0000000..fdd92f6
--- /dev/null
+++ b/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py
@@ -0,0 +1,184 @@
+# SchedGui.py - Python extension for perf script, basic GUI code for
+# traces drawing and overview.
+#
+# Copyright (C) 2010 by Frederic Weisbecker <fweisbec@gmail.com>
+#
+# This software is distributed under the terms of the GNU General
+# Public License ("GPL") version 2 as published by the Free Software
+# Foundation.
+
+
+try:
+ import wx
+except ImportError:
+ raise ImportError, "You need to install the wxpython lib for this script"
+
+
+class RootFrame(wx.Frame):
+ Y_OFFSET = 100
+ RECT_HEIGHT = 100
+ RECT_SPACE = 50
+ EVENT_MARKING_WIDTH = 5
+
+ def __init__(self, sched_tracer, title, parent = None, id = -1):
+ wx.Frame.__init__(self, parent, id, title)
+
+ (self.screen_width, self.screen_height) = wx.GetDisplaySize()
+ self.screen_width -= 10
+ self.screen_height -= 10
+ self.zoom = 0.5
+ self.scroll_scale = 20
+ self.sched_tracer = sched_tracer
+ self.sched_tracer.set_root_win(self)
+ (self.ts_start, self.ts_end) = sched_tracer.interval()
+ self.update_width_virtual()
+ self.nr_rects = sched_tracer.nr_rectangles() + 1
+ self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
+
+ # whole window panel
+ self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height))
+
+ # scrollable container
+ self.scroll = wx.ScrolledWindow(self.panel)
+ self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale)
+ self.scroll.EnableScrolling(True, True)
+ self.scroll.SetFocus()
+
+ # scrollable drawing area
+ self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2))
+ self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint)
+ self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press)
+ self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
+ self.scroll.Bind(wx.EVT_PAINT, self.on_paint)
+ self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press)
+ self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
+
+ self.scroll.Fit()
+ self.Fit()
+
+ self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING)
+
+ self.txt = None
+
+ self.Show(True)
+
+ def us_to_px(self, val):
+ return val / (10 ** 3) * self.zoom
+
+ def px_to_us(self, val):
+ return (val / self.zoom) * (10 ** 3)
+
+ def scroll_start(self):
+ (x, y) = self.scroll.GetViewStart()
+ return (x * self.scroll_scale, y * self.scroll_scale)
+
+ def scroll_start_us(self):
+ (x, y) = self.scroll_start()
+ return self.px_to_us(x)
+
+ def paint_rectangle_zone(self, nr, color, top_color, start, end):
+ offset_px = self.us_to_px(start - self.ts_start)
+ width_px = self.us_to_px(end - self.ts_start)
+
+ offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
+ width_py = RootFrame.RECT_HEIGHT
+
+ dc = self.dc
+
+ if top_color is not None:
+ (r, g, b) = top_color
+ top_color = wx.Colour(r, g, b)
+ brush = wx.Brush(top_color, wx.SOLID)
+ dc.SetBrush(brush)
+ dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH)
+ width_py -= RootFrame.EVENT_MARKING_WIDTH
+ offset_py += RootFrame.EVENT_MARKING_WIDTH
+
+ (r ,g, b) = color
+ color = wx.Colour(r, g, b)
+ brush = wx.Brush(color, wx.SOLID)
+ dc.SetBrush(brush)
+ dc.DrawRectangle(offset_px, offset_py, width_px, width_py)
+
+ def update_rectangles(self, dc, start, end):
+ start += self.ts_start
+ end += self.ts_start
+ self.sched_tracer.fill_zone(start, end)
+
+ def on_paint(self, event):
+ dc = wx.PaintDC(self.scroll_panel)
+ self.dc = dc
+
+ width = min(self.width_virtual, self.screen_width)
+ (x, y) = self.scroll_start()
+ start = self.px_to_us(x)
+ end = self.px_to_us(x + width)
+ self.update_rectangles(dc, start, end)
+
+ def rect_from_ypixel(self, y):
+ y -= RootFrame.Y_OFFSET
+ rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
+ height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
+
+ if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT:
+ return -1
+
+ return rect
+
+ def update_summary(self, txt):
+ if self.txt:
+ self.txt.Destroy()
+ self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50))
+
+
+ def on_mouse_down(self, event):
+ (x, y) = event.GetPositionTuple()
+ rect = self.rect_from_ypixel(y)
+ if rect == -1:
+ return
+
+ t = self.px_to_us(x) + self.ts_start
+
+ self.sched_tracer.mouse_down(rect, t)
+
+
+ def update_width_virtual(self):
+ self.width_virtual = self.us_to_px(self.ts_end - self.ts_start)
+
+ def __zoom(self, x):
+ self.update_width_virtual()
+ (xpos, ypos) = self.scroll.GetViewStart()
+ xpos = self.us_to_px(x) / self.scroll_scale
+ self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos)
+ self.Refresh()
+
+ def zoom_in(self):
+ x = self.scroll_start_us()
+ self.zoom *= 2
+ self.__zoom(x)
+
+ def zoom_out(self):
+ x = self.scroll_start_us()
+ self.zoom /= 2
+ self.__zoom(x)
+
+
+ def on_key_press(self, event):
+ key = event.GetRawKeyCode()
+ if key == ord("+"):
+ self.zoom_in()
+ return
+ if key == ord("-"):
+ self.zoom_out()
+ return
+
+ key = event.GetKeyCode()
+ (x, y) = self.scroll.GetViewStart()
+ if key == wx.WXK_RIGHT:
+ self.scroll.Scroll(x + 1, y)
+ elif key == wx.WXK_LEFT:
+ self.scroll.Scroll(x - 1, y)
+ elif key == wx.WXK_DOWN:
+ self.scroll.Scroll(x, y + 1)
+ elif key == wx.WXK_UP:
+ self.scroll.Scroll(x, y - 1)
diff --git a/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py
new file mode 100644
index 0000000..15c8400
--- /dev/null
+++ b/smartt-perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py
@@ -0,0 +1,86 @@
+# Util.py - Python extension for perf script, miscellaneous utility code
+#
+# Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com>
+#
+# This software may be distributed under the terms of the GNU General
+# Public License ("GPL") version 2 as published by the Free Software
+# Foundation.
+
+import errno, os
+
+FUTEX_WAIT = 0
+FUTEX_WAKE = 1
+FUTEX_PRIVATE_FLAG = 128
+FUTEX_CLOCK_REALTIME = 256
+FUTEX_CMD_MASK = ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+
+NSECS_PER_SEC = 1000000000
+
+def avg(total, n):
+ return total / n
+
+def nsecs(secs, nsecs):
+ return secs * NSECS_PER_SEC + nsecs
+
+def nsecs_secs(nsecs):
+ return nsecs / NSECS_PER_SEC
+
+def nsecs_nsecs(nsecs):
+ return nsecs % NSECS_PER_SEC
+
+def nsecs_str(nsecs):
+ str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)),
+ return str
+
+def add_stats(dict, key, value):
+ if not dict.has_key(key):
+ dict[key] = (value, value, value, 1)
+ else:
+ min, max, avg, count = dict[key]
+ if value < min:
+ min = value
+ if value > max:
+ max = value
+ avg = (avg + value) / 2
+ dict[key] = (min, max, avg, count + 1)
+
+def clear_term():
+ print("\x1b[H\x1b[2J")
+
+audit_package_warned = False
+
+try:
+ import audit
+ machine_to_id = {
+ 'x86_64': audit.MACH_86_64,
+ 'alpha' : audit.MACH_ALPHA,
+ 'ia64' : audit.MACH_IA64,
+ 'ppc' : audit.MACH_PPC,
+ 'ppc64' : audit.MACH_PPC64,
+ 's390' : audit.MACH_S390,
+ 's390x' : audit.MACH_S390X,
+ 'i386' : audit.MACH_X86,
+ 'i586' : audit.MACH_X86,
+ 'i686' : audit.MACH_X86,
+ }
+ try:
+ machine_to_id['armeb'] = audit.MACH_ARMEB
+ except:
+ pass
+ machine_id = machine_to_id[os.uname()[4]]
+except:
+ if not audit_package_warned:
+ audit_package_warned = True
+ print "Install the audit-libs-python package to get syscall names"
+
+def syscall_name(id):
+ try:
+ return audit.audit_syscall_to_name(id, machine_id)
+ except:
+ return str(id)
+
+def strerror(nr):
+ try:
+ return errno.errorcode[abs(nr)]
+ except:
+ return "Unknown %d errno" % nr
diff --git a/smartt-perf/scripts/python/bin/failed-syscalls-by-pid-record b/smartt-perf/scripts/python/bin/failed-syscalls-by-pid-record
new file mode 100644
index 0000000..8104895
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/failed-syscalls-by-pid-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e raw_syscalls:sys_exit $@
diff --git a/smartt-perf/scripts/python/bin/failed-syscalls-by-pid-report b/smartt-perf/scripts/python/bin/failed-syscalls-by-pid-report
new file mode 100644
index 0000000..fda5096
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/failed-syscalls-by-pid-report
@@ -0,0 +1,10 @@
+#!/bin/bash
+# description: system-wide failed syscalls, by pid
+# args: [comm]
+if [ $# -gt 0 ] ; then
+ if ! expr match "$1" "-" > /dev/null ; then
+ comm=$1
+ shift
+ fi
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/failed-syscalls-by-pid.py $comm
diff --git a/smartt-perf/scripts/python/bin/futex-contention-record b/smartt-perf/scripts/python/bin/futex-contention-record
new file mode 100644
index 0000000..b1495c9
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/futex-contention-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@
diff --git a/smartt-perf/scripts/python/bin/futex-contention-report b/smartt-perf/scripts/python/bin/futex-contention-report
new file mode 100644
index 0000000..6c44271
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/futex-contention-report
@@ -0,0 +1,4 @@
+#!/bin/bash
+# description: futext contention measurement
+
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/futex-contention.py
diff --git a/smartt-perf/scripts/python/bin/netdev-times-record b/smartt-perf/scripts/python/bin/netdev-times-record
new file mode 100644
index 0000000..558754b
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/netdev-times-record
@@ -0,0 +1,8 @@
+#!/bin/bash
+perf record -e net:net_dev_xmit -e net:net_dev_queue \
+ -e net:netif_receive_skb -e net:netif_rx \
+ -e skb:consume_skb -e skb:kfree_skb \
+ -e skb:skb_copy_datagram_iovec -e napi:napi_poll \
+ -e irq:irq_handler_entry -e irq:irq_handler_exit \
+ -e irq:softirq_entry -e irq:softirq_exit \
+ -e irq:softirq_raise $@
diff --git a/smartt-perf/scripts/python/bin/netdev-times-report b/smartt-perf/scripts/python/bin/netdev-times-report
new file mode 100644
index 0000000..8f75929
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/netdev-times-report
@@ -0,0 +1,5 @@
+#!/bin/bash
+# description: display a process of packet and processing time
+# args: [tx] [rx] [dev=] [debug]
+
+perf script -s "$PERF_EXEC_PATH"/scripts/python/netdev-times.py $@
diff --git a/smartt-perf/scripts/python/bin/sched-migration-record b/smartt-perf/scripts/python/bin/sched-migration-record
new file mode 100644
index 0000000..7493fdd
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/sched-migration-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -m 16384 -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@
diff --git a/smartt-perf/scripts/python/bin/sched-migration-report b/smartt-perf/scripts/python/bin/sched-migration-report
new file mode 100644
index 0000000..68b037a
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/sched-migration-report
@@ -0,0 +1,3 @@
+#!/bin/bash
+# description: sched migration overview
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/sched-migration.py
diff --git a/smartt-perf/scripts/python/bin/sctop-record b/smartt-perf/scripts/python/bin/sctop-record
new file mode 100644
index 0000000..4efbfaa
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/sctop-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e raw_syscalls:sys_enter $@
diff --git a/smartt-perf/scripts/python/bin/sctop-report b/smartt-perf/scripts/python/bin/sctop-report
new file mode 100644
index 0000000..c32db29
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/sctop-report
@@ -0,0 +1,24 @@
+#!/bin/bash
+# description: syscall top
+# args: [comm] [interval]
+n_args=0
+for i in "$@"
+do
+ if expr match "$i" "-" > /dev/null ; then
+ break
+ fi
+ n_args=$(( $n_args + 1 ))
+done
+if [ "$n_args" -gt 2 ] ; then
+ echo "usage: sctop-report [comm] [interval]"
+ exit
+fi
+if [ "$n_args" -gt 1 ] ; then
+ comm=$1
+ interval=$2
+ shift 2
+elif [ "$n_args" -gt 0 ] ; then
+ interval=$1
+ shift
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/sctop.py $comm $interval
diff --git a/smartt-perf/scripts/python/bin/syscall-counts-by-pid-record b/smartt-perf/scripts/python/bin/syscall-counts-by-pid-record
new file mode 100644
index 0000000..4efbfaa
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/syscall-counts-by-pid-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e raw_syscalls:sys_enter $@
diff --git a/smartt-perf/scripts/python/bin/syscall-counts-by-pid-report b/smartt-perf/scripts/python/bin/syscall-counts-by-pid-report
new file mode 100644
index 0000000..16eb8d6
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/syscall-counts-by-pid-report
@@ -0,0 +1,10 @@
+#!/bin/bash
+# description: system-wide syscall counts, by pid
+# args: [comm]
+if [ $# -gt 0 ] ; then
+ if ! expr match "$1" "-" > /dev/null ; then
+ comm=$1
+ shift
+ fi
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts-by-pid.py $comm
diff --git a/smartt-perf/scripts/python/bin/syscall-counts-record b/smartt-perf/scripts/python/bin/syscall-counts-record
new file mode 100644
index 0000000..4efbfaa
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/syscall-counts-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -e raw_syscalls:sys_enter $@
diff --git a/smartt-perf/scripts/python/bin/syscall-counts-report b/smartt-perf/scripts/python/bin/syscall-counts-report
new file mode 100644
index 0000000..0f0e9d4
--- /dev/null
+++ b/smartt-perf/scripts/python/bin/syscall-counts-report
@@ -0,0 +1,10 @@
+#!/bin/bash
+# description: system-wide syscall counts
+# args: [comm]
+if [ $# -gt 0 ] ; then
+ if ! expr match "$1" "-" > /dev/null ; then
+ comm=$1
+ shift
+ fi
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts.py $comm
diff --git a/smartt-perf/scripts/python/check-perf-trace.py b/smartt-perf/scripts/python/check-perf-trace.py
new file mode 100644
index 0000000..4647a76
--- /dev/null
+++ b/smartt-perf/scripts/python/check-perf-trace.py
@@ -0,0 +1,82 @@
+# perf script event handlers, generated by perf script -g python
+# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# This script tests basic functionality such as flag and symbol
+# strings, common_xxx() calls back into perf, begin, end, unhandled
+# events, etc. Basically, if this script runs successfully and
+# displays expected results, Python scripting support should be ok.
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from Core import *
+from perf_trace_context import *
+
+unhandled = autodict()
+
+def trace_begin():
+ print "trace_begin"
+ pass
+
+def trace_end():
+ print_unhandled()
+
+def irq__softirq_entry(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ vec):
+ print_header(event_name, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+
+ print_uncommon(context)
+
+ print "vec=%s\n" % \
+ (symbol_str("irq__softirq_entry", "vec", vec)),
+
+def kmem__kmalloc(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ call_site, ptr, bytes_req, bytes_alloc,
+ gfp_flags):
+ print_header(event_name, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+
+ print_uncommon(context)
+
+ print "call_site=%u, ptr=%u, bytes_req=%u, " \
+ "bytes_alloc=%u, gfp_flags=%s\n" % \
+ (call_site, ptr, bytes_req, bytes_alloc,
+
+ flag_str("kmem__kmalloc", "gfp_flags", gfp_flags)),
+
+def trace_unhandled(event_name, context, event_fields_dict):
+ try:
+ unhandled[event_name] += 1
+ except TypeError:
+ unhandled[event_name] = 1
+
+def print_header(event_name, cpu, secs, nsecs, pid, comm):
+ print "%-20s %5u %05u.%09u %8u %-20s " % \
+ (event_name, cpu, secs, nsecs, pid, comm),
+
+# print trace fields not included in handler args
+def print_uncommon(context):
+ print "common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, " \
+ % (common_pc(context), trace_flag_str(common_flags(context)), \
+ common_lock_depth(context))
+
+def print_unhandled():
+ keys = unhandled.keys()
+ if not keys:
+ return
+
+ print "\nunhandled events:\n\n",
+
+ print "%-40s %10s\n" % ("event", "count"),
+ print "%-40s %10s\n" % ("----------------------------------------", \
+ "-----------"),
+
+ for event_name in keys:
+ print "%-40s %10d\n" % (event_name, unhandled[event_name])
diff --git a/smartt-perf/scripts/python/failed-syscalls-by-pid.py b/smartt-perf/scripts/python/failed-syscalls-by-pid.py
new file mode 100644
index 0000000..85805fa
--- /dev/null
+++ b/smartt-perf/scripts/python/failed-syscalls-by-pid.py
@@ -0,0 +1,73 @@
+# failed system call counts, by pid
+# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# Displays system-wide failed system call totals, broken down by pid.
+# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import *
+
+usage = "perf script -s syscall-counts-by-pid.py [comm|pid]\n";
+
+for_comm = None
+for_pid = None
+
+if len(sys.argv) > 2:
+ sys.exit(usage)
+
+if len(sys.argv) > 1:
+ try:
+ for_pid = int(sys.argv[1])
+ except:
+ for_comm = sys.argv[1]
+
+syscalls = autodict()
+
+def trace_begin():
+ print "Press control+C to stop and show the summary"
+
+def trace_end():
+ print_error_totals()
+
+def raw_syscalls__sys_exit(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, ret):
+ if (for_comm and common_comm != for_comm) or \
+ (for_pid and common_pid != for_pid ):
+ return
+
+ if ret < 0:
+ try:
+ syscalls[common_comm][common_pid][id][ret] += 1
+ except TypeError:
+ syscalls[common_comm][common_pid][id][ret] = 1
+
+def print_error_totals():
+ if for_comm is not None:
+ print "\nsyscall errors for %s:\n\n" % (for_comm),
+ else:
+ print "\nsyscall errors:\n\n",
+
+ print "%-30s %10s\n" % ("comm [pid]", "count"),
+ print "%-30s %10s\n" % ("------------------------------", \
+ "----------"),
+
+ comm_keys = syscalls.keys()
+ for comm in comm_keys:
+ pid_keys = syscalls[comm].keys()
+ for pid in pid_keys:
+ print "\n%s [%d]\n" % (comm, pid),
+ id_keys = syscalls[comm][pid].keys()
+ for id in id_keys:
+ print " syscall: %-16s\n" % syscall_name(id),
+ ret_keys = syscalls[comm][pid][id].keys()
+ for ret, val in sorted(syscalls[comm][pid][id].iteritems(), key = lambda(k, v): (v, k), reverse = True):
+ print " err = %-20s %10d\n" % (strerror(ret), val),
diff --git a/smartt-perf/scripts/python/futex-contention.py b/smartt-perf/scripts/python/futex-contention.py
new file mode 100644
index 0000000..11e70a3
--- /dev/null
+++ b/smartt-perf/scripts/python/futex-contention.py
@@ -0,0 +1,50 @@
+# futex contention
+# (c) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# Translation of:
+#
+# http://sourceware.org/systemtap/wiki/WSFutexContention
+#
+# to perf python scripting.
+#
+# Measures futex contention
+
+import os, sys
+sys.path.append(os.environ['PERF_EXEC_PATH'] + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+from Util import *
+
+process_names = {}
+thread_thislock = {}
+thread_blocktime = {}
+
+lock_waits = {} # long-lived stats on (tid,lock) blockage elapsed time
+process_names = {} # long-lived pid-to-execname mapping
+
+def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm,
+ nr, uaddr, op, val, utime, uaddr2, val3):
+ cmd = op & FUTEX_CMD_MASK
+ if cmd != FUTEX_WAIT:
+ return # we don't care about originators of WAKE events
+
+ process_names[tid] = comm
+ thread_thislock[tid] = uaddr
+ thread_blocktime[tid] = nsecs(s, ns)
+
+def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm,
+ nr, ret):
+ if thread_blocktime.has_key(tid):
+ elapsed = nsecs(s, ns) - thread_blocktime[tid]
+ add_stats(lock_waits, (tid, thread_thislock[tid]), elapsed)
+ del thread_blocktime[tid]
+ del thread_thislock[tid]
+
+def trace_begin():
+ print "Press control+C to stop and show the summary"
+
+def trace_end():
+ for (tid, lock) in lock_waits:
+ min, max, avg, count = lock_waits[tid, lock]
+ print "%s[%d] lock %x contended %d times, %d avg ns" % \
+ (process_names[tid], tid, lock, count, avg)
+
diff --git a/smartt-perf/scripts/python/netdev-times.py b/smartt-perf/scripts/python/netdev-times.py
new file mode 100644
index 0000000..9aa0a32
--- /dev/null
+++ b/smartt-perf/scripts/python/netdev-times.py
@@ -0,0 +1,464 @@
+# Display a process of packets and processed time.
+# It helps us to investigate networking or network device.
+#
+# options
+# tx: show only tx chart
+# rx: show only rx chart
+# dev=: show only thing related to specified device
+# debug: work with debug mode. It shows buffer status.
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import *
+
+all_event_list = []; # insert all tracepoint event related with this script
+irq_dic = {}; # key is cpu and value is a list which stacks irqs
+ # which raise NET_RX softirq
+net_rx_dic = {}; # key is cpu and value include time of NET_RX softirq-entry
+ # and a list which stacks receive
+receive_hunk_list = []; # a list which include a sequence of receive events
+rx_skb_list = []; # received packet list for matching
+ # skb_copy_datagram_iovec
+
+buffer_budget = 65536; # the budget of rx_skb_list, tx_queue_list and
+ # tx_xmit_list
+of_count_rx_skb_list = 0; # overflow count
+
+tx_queue_list = []; # list of packets which pass through dev_queue_xmit
+of_count_tx_queue_list = 0; # overflow count
+
+tx_xmit_list = []; # list of packets which pass through dev_hard_start_xmit
+of_count_tx_xmit_list = 0; # overflow count
+
+tx_free_list = []; # list of packets which is freed
+
+# options
+show_tx = 0;
+show_rx = 0;
+dev = 0; # store a name of device specified by option "dev="
+debug = 0;
+
+# indices of event_info tuple
+EINFO_IDX_NAME= 0
+EINFO_IDX_CONTEXT=1
+EINFO_IDX_CPU= 2
+EINFO_IDX_TIME= 3
+EINFO_IDX_PID= 4
+EINFO_IDX_COMM= 5
+
+# Calculate a time interval(msec) from src(nsec) to dst(nsec)
+def diff_msec(src, dst):
+ return (dst - src) / 1000000.0
+
+# Display a process of transmitting a packet
+def print_transmit(hunk):
+ if dev != 0 and hunk['dev'].find(dev) < 0:
+ return
+ print "%7s %5d %6d.%06dsec %12.3fmsec %12.3fmsec" % \
+ (hunk['dev'], hunk['len'],
+ nsecs_secs(hunk['queue_t']),
+ nsecs_nsecs(hunk['queue_t'])/1000,
+ diff_msec(hunk['queue_t'], hunk['xmit_t']),
+ diff_msec(hunk['xmit_t'], hunk['free_t']))
+
+# Format for displaying rx packet processing
+PF_IRQ_ENTRY= " irq_entry(+%.3fmsec irq=%d:%s)"
+PF_SOFT_ENTRY=" softirq_entry(+%.3fmsec)"
+PF_NAPI_POLL= " napi_poll_exit(+%.3fmsec %s)"
+PF_JOINT= " |"
+PF_WJOINT= " | |"
+PF_NET_RECV= " |---netif_receive_skb(+%.3fmsec skb=%x len=%d)"
+PF_NET_RX= " |---netif_rx(+%.3fmsec skb=%x)"
+PF_CPY_DGRAM= " | skb_copy_datagram_iovec(+%.3fmsec %d:%s)"
+PF_KFREE_SKB= " | kfree_skb(+%.3fmsec location=%x)"
+PF_CONS_SKB= " | consume_skb(+%.3fmsec)"
+
+# Display a process of received packets and interrputs associated with
+# a NET_RX softirq
+def print_receive(hunk):
+ show_hunk = 0
+ irq_list = hunk['irq_list']
+ cpu = irq_list[0]['cpu']
+ base_t = irq_list[0]['irq_ent_t']
+ # check if this hunk should be showed
+ if dev != 0:
+ for i in range(len(irq_list)):
+ if irq_list[i]['name'].find(dev) >= 0:
+ show_hunk = 1
+ break
+ else:
+ show_hunk = 1
+ if show_hunk == 0:
+ return
+
+ print "%d.%06dsec cpu=%d" % \
+ (nsecs_secs(base_t), nsecs_nsecs(base_t)/1000, cpu)
+ for i in range(len(irq_list)):
+ print PF_IRQ_ENTRY % \
+ (diff_msec(base_t, irq_list[i]['irq_ent_t']),
+ irq_list[i]['irq'], irq_list[i]['name'])
+ print PF_JOINT
+ irq_event_list = irq_list[i]['event_list']
+ for j in range(len(irq_event_list)):
+ irq_event = irq_event_list[j]
+ if irq_event['event'] == 'netif_rx':
+ print PF_NET_RX % \
+ (diff_msec(base_t, irq_event['time']),
+ irq_event['skbaddr'])
+ print PF_JOINT
+ print PF_SOFT_ENTRY % \
+ diff_msec(base_t, hunk['sirq_ent_t'])
+ print PF_JOINT
+ event_list = hunk['event_list']
+ for i in range(len(event_list)):
+ event = event_list[i]
+ if event['event_name'] == 'napi_poll':
+ print PF_NAPI_POLL % \
+ (diff_msec(base_t, event['event_t']), event['dev'])
+ if i == len(event_list) - 1:
+ print ""
+ else:
+ print PF_JOINT
+ else:
+ print PF_NET_RECV % \
+ (diff_msec(base_t, event['event_t']), event['skbaddr'],
+ event['len'])
+ if 'comm' in event.keys():
+ print PF_WJOINT
+ print PF_CPY_DGRAM % \
+ (diff_msec(base_t, event['comm_t']),
+ event['pid'], event['comm'])
+ elif 'handle' in event.keys():
+ print PF_WJOINT
+ if event['handle'] == "kfree_skb":
+ print PF_KFREE_SKB % \
+ (diff_msec(base_t,
+ event['comm_t']),
+ event['location'])
+ elif event['handle'] == "consume_skb":
+ print PF_CONS_SKB % \
+ diff_msec(base_t,
+ event['comm_t'])
+ print PF_JOINT
+
+def trace_begin():
+ global show_tx
+ global show_rx
+ global dev
+ global debug
+
+ for i in range(len(sys.argv)):
+ if i == 0:
+ continue
+ arg = sys.argv[i]
+ if arg == 'tx':
+ show_tx = 1
+ elif arg =='rx':
+ show_rx = 1
+ elif arg.find('dev=',0, 4) >= 0:
+ dev = arg[4:]
+ elif arg == 'debug':
+ debug = 1
+ if show_tx == 0 and show_rx == 0:
+ show_tx = 1
+ show_rx = 1
+
+def trace_end():
+ # order all events in time
+ all_event_list.sort(lambda a,b :cmp(a[EINFO_IDX_TIME],
+ b[EINFO_IDX_TIME]))
+ # process all events
+ for i in range(len(all_event_list)):
+ event_info = all_event_list[i]
+ name = event_info[EINFO_IDX_NAME]
+ if name == 'irq__softirq_exit':
+ handle_irq_softirq_exit(event_info)
+ elif name == 'irq__softirq_entry':
+ handle_irq_softirq_entry(event_info)
+ elif name == 'irq__softirq_raise':
+ handle_irq_softirq_raise(event_info)
+ elif name == 'irq__irq_handler_entry':
+ handle_irq_handler_entry(event_info)
+ elif name == 'irq__irq_handler_exit':
+ handle_irq_handler_exit(event_info)
+ elif name == 'napi__napi_poll':
+ handle_napi_poll(event_info)
+ elif name == 'net__netif_receive_skb':
+ handle_netif_receive_skb(event_info)
+ elif name == 'net__netif_rx':
+ handle_netif_rx(event_info)
+ elif name == 'skb__skb_copy_datagram_iovec':
+ handle_skb_copy_datagram_iovec(event_info)
+ elif name == 'net__net_dev_queue':
+ handle_net_dev_queue(event_info)
+ elif name == 'net__net_dev_xmit':
+ handle_net_dev_xmit(event_info)
+ elif name == 'skb__kfree_skb':
+ handle_kfree_skb(event_info)
+ elif name == 'skb__consume_skb':
+ handle_consume_skb(event_info)
+ # display receive hunks
+ if show_rx:
+ for i in range(len(receive_hunk_list)):
+ print_receive(receive_hunk_list[i])
+ # display transmit hunks
+ if show_tx:
+ print " dev len Qdisc " \
+ " netdevice free"
+ for i in range(len(tx_free_list)):
+ print_transmit(tx_free_list[i])
+ if debug:
+ print "debug buffer status"
+ print "----------------------------"
+ print "xmit Qdisc:remain:%d overflow:%d" % \
+ (len(tx_queue_list), of_count_tx_queue_list)
+ print "xmit netdevice:remain:%d overflow:%d" % \
+ (len(tx_xmit_list), of_count_tx_xmit_list)
+ print "receive:remain:%d overflow:%d" % \
+ (len(rx_skb_list), of_count_rx_skb_list)
+
+# called from perf, when it finds a correspoinding event
+def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, vec):
+ if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
+ return
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
+ all_event_list.append(event_info)
+
+def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, vec):
+ if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
+ return
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
+ all_event_list.append(event_info)
+
+def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, vec):
+ if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
+ return
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
+ all_event_list.append(event_info)
+
+def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm,
+ irq, irq_name):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ irq, irq_name)
+ all_event_list.append(event_info)
+
+def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, irq, ret):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret)
+ all_event_list.append(event_info)
+
+def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, napi, dev_name):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ napi, dev_name)
+ all_event_list.append(event_info)
+
+def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr,
+ skblen, dev_name):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ skbaddr, skblen, dev_name)
+ all_event_list.append(event_info)
+
+def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, skbaddr,
+ skblen, dev_name):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ skbaddr, skblen, dev_name)
+ all_event_list.append(event_info)
+
+def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm,
+ skbaddr, skblen, dev_name):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ skbaddr, skblen, dev_name)
+ all_event_list.append(event_info)
+
+def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm,
+ skbaddr, skblen, rc, dev_name):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ skbaddr, skblen, rc ,dev_name)
+ all_event_list.append(event_info)
+
+def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm,
+ skbaddr, protocol, location):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ skbaddr, protocol, location)
+ all_event_list.append(event_info)
+
+def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ skbaddr)
+ all_event_list.append(event_info)
+
+def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm,
+ skbaddr, skblen):
+ event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
+ skbaddr, skblen)
+ all_event_list.append(event_info)
+
+def handle_irq_handler_entry(event_info):
+ (name, context, cpu, time, pid, comm, irq, irq_name) = event_info
+ if cpu not in irq_dic.keys():
+ irq_dic[cpu] = []
+ irq_record = {'irq':irq, 'name':irq_name, 'cpu':cpu, 'irq_ent_t':time}
+ irq_dic[cpu].append(irq_record)
+
+def handle_irq_handler_exit(event_info):
+ (name, context, cpu, time, pid, comm, irq, ret) = event_info
+ if cpu not in irq_dic.keys():
+ return
+ irq_record = irq_dic[cpu].pop()
+ if irq != irq_record['irq']:
+ return
+ irq_record.update({'irq_ext_t':time})
+ # if an irq doesn't include NET_RX softirq, drop.
+ if 'event_list' in irq_record.keys():
+ irq_dic[cpu].append(irq_record)
+
+def handle_irq_softirq_raise(event_info):
+ (name, context, cpu, time, pid, comm, vec) = event_info
+ if cpu not in irq_dic.keys() \
+ or len(irq_dic[cpu]) == 0:
+ return
+ irq_record = irq_dic[cpu].pop()
+ if 'event_list' in irq_record.keys():
+ irq_event_list = irq_record['event_list']
+ else:
+ irq_event_list = []
+ irq_event_list.append({'time':time, 'event':'sirq_raise'})
+ irq_record.update({'event_list':irq_event_list})
+ irq_dic[cpu].append(irq_record)
+
+def handle_irq_softirq_entry(event_info):
+ (name, context, cpu, time, pid, comm, vec) = event_info
+ net_rx_dic[cpu] = {'sirq_ent_t':time, 'event_list':[]}
+
+def handle_irq_softirq_exit(event_info):
+ (name, context, cpu, time, pid, comm, vec) = event_info
+ irq_list = []
+ event_list = 0
+ if cpu in irq_dic.keys():
+ irq_list = irq_dic[cpu]
+ del irq_dic[cpu]
+ if cpu in net_rx_dic.keys():
+ sirq_ent_t = net_rx_dic[cpu]['sirq_ent_t']
+ event_list = net_rx_dic[cpu]['event_list']
+ del net_rx_dic[cpu]
+ if irq_list == [] or event_list == 0:
+ return
+ rec_data = {'sirq_ent_t':sirq_ent_t, 'sirq_ext_t':time,
+ 'irq_list':irq_list, 'event_list':event_list}
+ # merge information realted to a NET_RX softirq
+ receive_hunk_list.append(rec_data)
+
+def handle_napi_poll(event_info):
+ (name, context, cpu, time, pid, comm, napi, dev_name) = event_info
+ if cpu in net_rx_dic.keys():
+ event_list = net_rx_dic[cpu]['event_list']
+ rec_data = {'event_name':'napi_poll',
+ 'dev':dev_name, 'event_t':time}
+ event_list.append(rec_data)
+
+def handle_netif_rx(event_info):
+ (name, context, cpu, time, pid, comm,
+ skbaddr, skblen, dev_name) = event_info
+ if cpu not in irq_dic.keys() \
+ or len(irq_dic[cpu]) == 0:
+ return
+ irq_record = irq_dic[cpu].pop()
+ if 'event_list' in irq_record.keys():
+ irq_event_list = irq_record['event_list']
+ else:
+ irq_event_list = []
+ irq_event_list.append({'time':time, 'event':'netif_rx',
+ 'skbaddr':skbaddr, 'skblen':skblen, 'dev_name':dev_name})
+ irq_record.update({'event_list':irq_event_list})
+ irq_dic[cpu].append(irq_record)
+
+def handle_netif_receive_skb(event_info):
+ global of_count_rx_skb_list
+
+ (name, context, cpu, time, pid, comm,
+ skbaddr, skblen, dev_name) = event_info
+ if cpu in net_rx_dic.keys():
+ rec_data = {'event_name':'netif_receive_skb',
+ 'event_t':time, 'skbaddr':skbaddr, 'len':skblen}
+ event_list = net_rx_dic[cpu]['event_list']
+ event_list.append(rec_data)
+ rx_skb_list.insert(0, rec_data)
+ if len(rx_skb_list) > buffer_budget:
+ rx_skb_list.pop()
+ of_count_rx_skb_list += 1
+
+def handle_net_dev_queue(event_info):
+ global of_count_tx_queue_list
+
+ (name, context, cpu, time, pid, comm,
+ skbaddr, skblen, dev_name) = event_info
+ skb = {'dev':dev_name, 'skbaddr':skbaddr, 'len':skblen, 'queue_t':time}
+ tx_queue_list.insert(0, skb)
+ if len(tx_queue_list) > buffer_budget:
+ tx_queue_list.pop()
+ of_count_tx_queue_list += 1
+
+def handle_net_dev_xmit(event_info):
+ global of_count_tx_xmit_list
+
+ (name, context, cpu, time, pid, comm,
+ skbaddr, skblen, rc, dev_name) = event_info
+ if rc == 0: # NETDEV_TX_OK
+ for i in range(len(tx_queue_list)):
+ skb = tx_queue_list[i]
+ if skb['skbaddr'] == skbaddr:
+ skb['xmit_t'] = time
+ tx_xmit_list.insert(0, skb)
+ del tx_queue_list[i]
+ if len(tx_xmit_list) > buffer_budget:
+ tx_xmit_list.pop()
+ of_count_tx_xmit_list += 1
+ return
+
+def handle_kfree_skb(event_info):
+ (name, context, cpu, time, pid, comm,
+ skbaddr, protocol, location) = event_info
+ for i in range(len(tx_queue_list)):
+ skb = tx_queue_list[i]
+ if skb['skbaddr'] == skbaddr:
+ del tx_queue_list[i]
+ return
+ for i in range(len(tx_xmit_list)):
+ skb = tx_xmit_list[i]
+ if skb['skbaddr'] == skbaddr:
+ skb['free_t'] = time
+ tx_free_list.append(skb)
+ del tx_xmit_list[i]
+ return
+ for i in range(len(rx_skb_list)):
+ rec_data = rx_skb_list[i]
+ if rec_data['skbaddr'] == skbaddr:
+ rec_data.update({'handle':"kfree_skb",
+ 'comm':comm, 'pid':pid, 'comm_t':time})
+ del rx_skb_list[i]
+ return
+
+def handle_consume_skb(event_info):
+ (name, context, cpu, time, pid, comm, skbaddr) = event_info
+ for i in range(len(tx_xmit_list)):
+ skb = tx_xmit_list[i]
+ if skb['skbaddr'] == skbaddr:
+ skb['free_t'] = time
+ tx_free_list.append(skb)
+ del tx_xmit_list[i]
+ return
+
+def handle_skb_copy_datagram_iovec(event_info):
+ (name, context, cpu, time, pid, comm, skbaddr, skblen) = event_info
+ for i in range(len(rx_skb_list)):
+ rec_data = rx_skb_list[i]
+ if skbaddr == rec_data['skbaddr']:
+ rec_data.update({'handle':"skb_copy_datagram_iovec",
+ 'comm':comm, 'pid':pid, 'comm_t':time})
+ del rx_skb_list[i]
+ return
diff --git a/smartt-perf/scripts/python/sched-migration.py b/smartt-perf/scripts/python/sched-migration.py
new file mode 100644
index 0000000..74d55ec
--- /dev/null
+++ b/smartt-perf/scripts/python/sched-migration.py
@@ -0,0 +1,461 @@
+#!/usr/bin/python
+#
+# Cpu task migration overview toy
+#
+# Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com>
+#
+# perf script event handlers have been generated by perf script -g python
+#
+# This software is distributed under the terms of the GNU General
+# Public License ("GPL") version 2 as published by the Free Software
+# Foundation.
+
+
+import os
+import sys
+
+from collections import defaultdict
+from UserList import UserList
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from SchedGui import *
+
+
+threads = { 0 : "idle"}
+
+def thread_name(pid):
+ return "%s:%d" % (threads[pid], pid)
+
+class RunqueueEventUnknown:
+ @staticmethod
+ def color():
+ return None
+
+ def __repr__(self):
+ return "unknown"
+
+class RunqueueEventSleep:
+ @staticmethod
+ def color():
+ return (0, 0, 0xff)
+
+ def __init__(self, sleeper):
+ self.sleeper = sleeper
+
+ def __repr__(self):
+ return "%s gone to sleep" % thread_name(self.sleeper)
+
+class RunqueueEventWakeup:
+ @staticmethod
+ def color():
+ return (0xff, 0xff, 0)
+
+ def __init__(self, wakee):
+ self.wakee = wakee
+
+ def __repr__(self):
+ return "%s woke up" % thread_name(self.wakee)
+
+class RunqueueEventFork:
+ @staticmethod
+ def color():
+ return (0, 0xff, 0)
+
+ def __init__(self, child):
+ self.child = child
+
+ def __repr__(self):
+ return "new forked task %s" % thread_name(self.child)
+
+class RunqueueMigrateIn:
+ @staticmethod
+ def color():
+ return (0, 0xf0, 0xff)
+
+ def __init__(self, new):
+ self.new = new
+
+ def __repr__(self):
+ return "task migrated in %s" % thread_name(self.new)
+
+class RunqueueMigrateOut:
+ @staticmethod
+ def color():
+ return (0xff, 0, 0xff)
+
+ def __init__(self, old):
+ self.old = old
+
+ def __repr__(self):
+ return "task migrated out %s" % thread_name(self.old)
+
+class RunqueueSnapshot:
+ def __init__(self, tasks = [0], event = RunqueueEventUnknown()):
+ self.tasks = tuple(tasks)
+ self.event = event
+
+ def sched_switch(self, prev, prev_state, next):
+ event = RunqueueEventUnknown()
+
+ if taskState(prev_state) == "R" and next in self.tasks \
+ and prev in self.tasks:
+ return self
+
+ if taskState(prev_state) != "R":
+ event = RunqueueEventSleep(prev)
+
+ next_tasks = list(self.tasks[:])
+ if prev in self.tasks:
+ if taskState(prev_state) != "R":
+ next_tasks.remove(prev)
+ elif taskState(prev_state) == "R":
+ next_tasks.append(prev)
+
+ if next not in next_tasks:
+ next_tasks.append(next)
+
+ return RunqueueSnapshot(next_tasks, event)
+
+ def migrate_out(self, old):
+ if old not in self.tasks:
+ return self
+ next_tasks = [task for task in self.tasks if task != old]
+
+ return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old))
+
+ def __migrate_in(self, new, event):
+ if new in self.tasks:
+ self.event = event
+ return self
+ next_tasks = self.tasks[:] + tuple([new])
+
+ return RunqueueSnapshot(next_tasks, event)
+
+ def migrate_in(self, new):
+ return self.__migrate_in(new, RunqueueMigrateIn(new))
+
+ def wake_up(self, new):
+ return self.__migrate_in(new, RunqueueEventWakeup(new))
+
+ def wake_up_new(self, new):
+ return self.__migrate_in(new, RunqueueEventFork(new))
+
+ def load(self):
+ """ Provide the number of tasks on the runqueue.
+ Don't count idle"""
+ return len(self.tasks) - 1
+
+ def __repr__(self):
+ ret = self.tasks.__repr__()
+ ret += self.origin_tostring()
+
+ return ret
+
+class TimeSlice:
+ def __init__(self, start, prev):
+ self.start = start
+ self.prev = prev
+ self.end = start
+ # cpus that triggered the event
+ self.event_cpus = []
+ if prev is not None:
+ self.total_load = prev.total_load
+ self.rqs = prev.rqs.copy()
+ else:
+ self.rqs = defaultdict(RunqueueSnapshot)
+ self.total_load = 0
+
+ def __update_total_load(self, old_rq, new_rq):
+ diff = new_rq.load() - old_rq.load()
+ self.total_load += diff
+
+ def sched_switch(self, ts_list, prev, prev_state, next, cpu):
+ old_rq = self.prev.rqs[cpu]
+ new_rq = old_rq.sched_switch(prev, prev_state, next)
+
+ if old_rq is new_rq:
+ return
+
+ self.rqs[cpu] = new_rq
+ self.__update_total_load(old_rq, new_rq)
+ ts_list.append(self)
+ self.event_cpus = [cpu]
+
+ def migrate(self, ts_list, new, old_cpu, new_cpu):
+ if old_cpu == new_cpu:
+ return
+ old_rq = self.prev.rqs[old_cpu]
+ out_rq = old_rq.migrate_out(new)
+ self.rqs[old_cpu] = out_rq
+ self.__update_total_load(old_rq, out_rq)
+
+ new_rq = self.prev.rqs[new_cpu]
+ in_rq = new_rq.migrate_in(new)
+ self.rqs[new_cpu] = in_rq
+ self.__update_total_load(new_rq, in_rq)
+
+ ts_list.append(self)
+
+ if old_rq is not out_rq:
+ self.event_cpus.append(old_cpu)
+ self.event_cpus.append(new_cpu)
+
+ def wake_up(self, ts_list, pid, cpu, fork):
+ old_rq = self.prev.rqs[cpu]
+ if fork:
+ new_rq = old_rq.wake_up_new(pid)
+ else:
+ new_rq = old_rq.wake_up(pid)
+
+ if new_rq is old_rq:
+ return
+ self.rqs[cpu] = new_rq
+ self.__update_total_load(old_rq, new_rq)
+ ts_list.append(self)
+ self.event_cpus = [cpu]
+
+ def next(self, t):
+ self.end = t
+ return TimeSlice(t, self)
+
+class TimeSliceList(UserList):
+ def __init__(self, arg = []):
+ self.data = arg
+
+ def get_time_slice(self, ts):
+ if len(self.data) == 0:
+ slice = TimeSlice(ts, TimeSlice(-1, None))
+ else:
+ slice = self.data[-1].next(ts)
+ return slice
+
+ def find_time_slice(self, ts):
+ start = 0
+ end = len(self.data)
+ found = -1
+ searching = True
+ while searching:
+ if start == end or start == end - 1:
+ searching = False
+
+ i = (end + start) / 2
+ if self.data[i].start <= ts and self.data[i].end >= ts:
+ found = i
+ end = i
+ continue
+
+ if self.data[i].end < ts:
+ start = i
+
+ elif self.data[i].start > ts:
+ end = i
+
+ return found
+
+ def set_root_win(self, win):
+ self.root_win = win
+
+ def mouse_down(self, cpu, t):
+ idx = self.find_time_slice(t)
+ if idx == -1:
+ return
+
+ ts = self[idx]
+ rq = ts.rqs[cpu]
+ raw = "CPU: %d\n" % cpu
+ raw += "Last event : %s\n" % rq.event.__repr__()
+ raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000)
+ raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6))
+ raw += "Load = %d\n" % rq.load()
+ for t in rq.tasks:
+ raw += "%s \n" % thread_name(t)
+
+ self.root_win.update_summary(raw)
+
+ def update_rectangle_cpu(self, slice, cpu):
+ rq = slice.rqs[cpu]
+
+ if slice.total_load != 0:
+ load_rate = rq.load() / float(slice.total_load)
+ else:
+ load_rate = 0
+
+ red_power = int(0xff - (0xff * load_rate))
+ color = (0xff, red_power, red_power)
+
+ top_color = None
+
+ if cpu in slice.event_cpus:
+ top_color = rq.event.color()
+
+ self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end)
+
+ def fill_zone(self, start, end):
+ i = self.find_time_slice(start)
+ if i == -1:
+ return
+
+ for i in xrange(i, len(self.data)):
+ timeslice = self.data[i]
+ if timeslice.start > end:
+ return
+
+ for cpu in timeslice.rqs:
+ self.update_rectangle_cpu(timeslice, cpu)
+
+ def interval(self):
+ if len(self.data) == 0:
+ return (0, 0)
+
+ return (self.data[0].start, self.data[-1].end)
+
+ def nr_rectangles(self):
+ last_ts = self.data[-1]
+ max_cpu = 0
+ for cpu in last_ts.rqs:
+ if cpu > max_cpu:
+ max_cpu = cpu
+ return max_cpu
+
+
+class SchedEventProxy:
+ def __init__(self):
+ self.current_tsk = defaultdict(lambda : -1)
+ self.timeslices = TimeSliceList()
+
+ def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_state,
+ next_comm, next_pid, next_prio):
+ """ Ensure the task we sched out this cpu is really the one
+ we logged. Otherwise we may have missed traces """
+
+ on_cpu_task = self.current_tsk[headers.cpu]
+
+ if on_cpu_task != -1 and on_cpu_task != prev_pid:
+ print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \
+ (headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next_pid)
+
+ threads[prev_pid] = prev_comm
+ threads[next_pid] = next_comm
+ self.current_tsk[headers.cpu] = next_pid
+
+ ts = self.timeslices.get_time_slice(headers.ts())
+ ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu)
+
+ def migrate(self, headers, pid, prio, orig_cpu, dest_cpu):
+ ts = self.timeslices.get_time_slice(headers.ts())
+ ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu)
+
+ def wake_up(self, headers, comm, pid, success, target_cpu, fork):
+ if success == 0:
+ return
+ ts = self.timeslices.get_time_slice(headers.ts())
+ ts.wake_up(self.timeslices, pid, target_cpu, fork)
+
+
+def trace_begin():
+ global parser
+ parser = SchedEventProxy()
+
+def trace_end():
+ app = wx.App(False)
+ timeslices = parser.timeslices
+ frame = RootFrame(timeslices, "Migration")
+ app.MainLoop()
+
+def sched__sched_stat_runtime(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, runtime, vruntime):
+ pass
+
+def sched__sched_stat_iowait(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, delay):
+ pass
+
+def sched__sched_stat_sleep(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, delay):
+ pass
+
+def sched__sched_stat_wait(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, delay):
+ pass
+
+def sched__sched_process_fork(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ parent_comm, parent_pid, child_comm, child_pid):
+ pass
+
+def sched__sched_process_wait(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, prio):
+ pass
+
+def sched__sched_process_exit(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, prio):
+ pass
+
+def sched__sched_process_free(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, prio):
+ pass
+
+def sched__sched_migrate_task(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, prio, orig_cpu,
+ dest_cpu):
+ headers = EventHeaders(common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+ parser.migrate(headers, pid, prio, orig_cpu, dest_cpu)
+
+def sched__sched_switch(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ prev_comm, prev_pid, prev_prio, prev_state,
+ next_comm, next_pid, next_prio):
+
+ headers = EventHeaders(common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+ parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state,
+ next_comm, next_pid, next_prio)
+
+def sched__sched_wakeup_new(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, prio, success,
+ target_cpu):
+ headers = EventHeaders(common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+ parser.wake_up(headers, comm, pid, success, target_cpu, 1)
+
+def sched__sched_wakeup(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, prio, success,
+ target_cpu):
+ headers = EventHeaders(common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm)
+ parser.wake_up(headers, comm, pid, success, target_cpu, 0)
+
+def sched__sched_wait_task(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid, prio):
+ pass
+
+def sched__sched_kthread_stop_ret(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ ret):
+ pass
+
+def sched__sched_kthread_stop(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ comm, pid):
+ pass
+
+def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
+ common_pid, common_comm):
+ pass
diff --git a/smartt-perf/scripts/python/sctop.py b/smartt-perf/scripts/python/sctop.py
new file mode 100644
index 0000000..42c267e
--- /dev/null
+++ b/smartt-perf/scripts/python/sctop.py
@@ -0,0 +1,75 @@
+# system call top
+# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# Periodically displays system-wide system call totals, broken down by
+# syscall. If a [comm] arg is specified, only syscalls called by
+# [comm] are displayed. If an [interval] arg is specified, the display
+# will be refreshed every [interval] seconds. The default interval is
+# 3 seconds.
+
+import os, sys, thread, time
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import *
+
+usage = "perf script -s sctop.py [comm] [interval]\n";
+
+for_comm = None
+default_interval = 3
+interval = default_interval
+
+if len(sys.argv) > 3:
+ sys.exit(usage)
+
+if len(sys.argv) > 2:
+ for_comm = sys.argv[1]
+ interval = int(sys.argv[2])
+elif len(sys.argv) > 1:
+ try:
+ interval = int(sys.argv[1])
+ except ValueError:
+ for_comm = sys.argv[1]
+ interval = default_interval
+
+syscalls = autodict()
+
+def trace_begin():
+ thread.start_new_thread(print_syscall_totals, (interval,))
+ pass
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+ if for_comm is not None:
+ if common_comm != for_comm:
+ return
+ try:
+ syscalls[id] += 1
+ except TypeError:
+ syscalls[id] = 1
+
+def print_syscall_totals(interval):
+ while 1:
+ clear_term()
+ if for_comm is not None:
+ print "\nsyscall events for %s:\n\n" % (for_comm),
+ else:
+ print "\nsyscall events:\n\n",
+
+ print "%-40s %10s\n" % ("event", "count"),
+ print "%-40s %10s\n" % ("----------------------------------------", \
+ "----------"),
+
+ for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
+ reverse = True):
+ try:
+ print "%-40s %10d\n" % (syscall_name(id), val),
+ except TypeError:
+ pass
+ syscalls.clear()
+ time.sleep(interval)
diff --git a/smartt-perf/scripts/python/syscall-counts-by-pid.py b/smartt-perf/scripts/python/syscall-counts-by-pid.py
new file mode 100644
index 0000000..c64d1c5
--- /dev/null
+++ b/smartt-perf/scripts/python/syscall-counts-by-pid.py
@@ -0,0 +1,69 @@
+# system call counts, by pid
+# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# Displays system-wide system call totals, broken down by syscall.
+# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
+
+import os, sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import syscall_name
+
+usage = "perf script -s syscall-counts-by-pid.py [comm]\n";
+
+for_comm = None
+for_pid = None
+
+if len(sys.argv) > 2:
+ sys.exit(usage)
+
+if len(sys.argv) > 1:
+ try:
+ for_pid = int(sys.argv[1])
+ except:
+ for_comm = sys.argv[1]
+
+syscalls = autodict()
+
+def trace_begin():
+ print "Press control+C to stop and show the summary"
+
+def trace_end():
+ print_syscall_totals()
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+
+ if (for_comm and common_comm != for_comm) or \
+ (for_pid and common_pid != for_pid ):
+ return
+ try:
+ syscalls[common_comm][common_pid][id] += 1
+ except TypeError:
+ syscalls[common_comm][common_pid][id] = 1
+
+def print_syscall_totals():
+ if for_comm is not None:
+ print "\nsyscall events for %s:\n\n" % (for_comm),
+ else:
+ print "\nsyscall events by comm/pid:\n\n",
+
+ print "%-40s %10s\n" % ("comm [pid]/syscalls", "count"),
+ print "%-40s %10s\n" % ("----------------------------------------", \
+ "----------"),
+
+ comm_keys = syscalls.keys()
+ for comm in comm_keys:
+ pid_keys = syscalls[comm].keys()
+ for pid in pid_keys:
+ print "\n%s [%d]\n" % (comm, pid),
+ id_keys = syscalls[comm][pid].keys()
+ for id, val in sorted(syscalls[comm][pid].iteritems(), \
+ key = lambda(k, v): (v, k), reverse = True):
+ print " %-38s %10d\n" % (syscall_name(id), val),
diff --git a/smartt-perf/scripts/python/syscall-counts.py b/smartt-perf/scripts/python/syscall-counts.py
new file mode 100644
index 0000000..b435d3f
--- /dev/null
+++ b/smartt-perf/scripts/python/syscall-counts.py
@@ -0,0 +1,59 @@
+# system call counts
+# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# Displays system-wide system call totals, broken down by syscall.
+# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import syscall_name
+
+usage = "perf script -s syscall-counts.py [comm]\n";
+
+for_comm = None
+
+if len(sys.argv) > 2:
+ sys.exit(usage)
+
+if len(sys.argv) > 1:
+ for_comm = sys.argv[1]
+
+syscalls = autodict()
+
+def trace_begin():
+ print "Press control+C to stop and show the summary"
+
+def trace_end():
+ print_syscall_totals()
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+ if for_comm is not None:
+ if common_comm != for_comm:
+ return
+ try:
+ syscalls[id] += 1
+ except TypeError:
+ syscalls[id] = 1
+
+def print_syscall_totals():
+ if for_comm is not None:
+ print "\nsyscall events for %s:\n\n" % (for_comm),
+ else:
+ print "\nsyscall events:\n\n",
+
+ print "%-40s %10s\n" % ("event", "count"),
+ print "%-40s %10s\n" % ("----------------------------------------", \
+ "-----------"),
+
+ for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
+ reverse = True):
+ print "%-40s %10d\n" % (syscall_name(id), val),
diff --git a/smartt-perf/util/PERF-VERSION-GEN b/smartt-perf/util/PERF-VERSION-GEN
new file mode 100755
index 0000000..26d4d3f
--- /dev/null
+++ b/smartt-perf/util/PERF-VERSION-GEN
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+if [ $# -eq 1 ] ; then
+ OUTPUT=$1
+fi
+
+GVF=${OUTPUT}PERF-VERSION-FILE
+
+LF='
+'
+
+# First check if there is a .git to get the version from git describe
+# otherwise try to get the version from the kernel makefile
+if test -d ../../.git -o -f ../../.git &&
+ VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
+ case "$VN" in
+ *$LF*) (exit 1) ;;
+ v[0-9]*)
+ git update-index -q --refresh
+ test -z "$(git diff-index --name-only HEAD --)" ||
+ VN="$VN-dirty" ;;
+ esac
+then
+ VN=$(echo "$VN" | sed -e 's/-/./g');
+else
+ eval $(grep '^VERSION[[:space:]]*=' ../../Makefile|tr -d ' ')
+ eval $(grep '^PATCHLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ')
+ eval $(grep '^SUBLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ')
+ eval $(grep '^EXTRAVERSION[[:space:]]*=' ../../Makefile|tr -d ' ')
+
+ VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}"
+fi
+
+VN=$(expr "$VN" : v*'\(.*\)')
+
+if test -r $GVF
+then
+ VC=$(sed -e 's/^PERF_VERSION = //' <$GVF)
+else
+ VC=unset
+fi
+test "$VN" = "$VC" || {
+ echo >&2 "PERF_VERSION = $VN"
+ echo "PERF_VERSION = $VN" >$GVF
+}
+
+
diff --git a/smartt-perf/util/abspath.c b/smartt-perf/util/abspath.c
new file mode 100644
index 0000000..0e76aff
--- /dev/null
+++ b/smartt-perf/util/abspath.c
@@ -0,0 +1,37 @@
+#include "cache.h"
+
+static const char *get_pwd_cwd(void)
+{
+ static char cwd[PATH_MAX + 1];
+ char *pwd;
+ struct stat cwd_stat, pwd_stat;
+ if (getcwd(cwd, PATH_MAX) == NULL)
+ return NULL;
+ pwd = getenv("PWD");
+ if (pwd && strcmp(pwd, cwd)) {
+ stat(cwd, &cwd_stat);
+ if (!stat(pwd, &pwd_stat) &&
+ pwd_stat.st_dev == cwd_stat.st_dev &&
+ pwd_stat.st_ino == cwd_stat.st_ino) {
+ strlcpy(cwd, pwd, PATH_MAX);
+ }
+ }
+ return cwd;
+}
+
+const char *make_nonrelative_path(const char *path)
+{
+ static char buf[PATH_MAX + 1];
+
+ if (is_absolute_path(path)) {
+ if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
+ die("Too long path: %.*s", 60, path);
+ } else {
+ const char *cwd = get_pwd_cwd();
+ if (!cwd)
+ die("Cannot determine the current working directory");
+ if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
+ die("Too long path: %.*s", 60, path);
+ }
+ return buf;
+}
diff --git a/smartt-perf/util/alias.c b/smartt-perf/util/alias.c
new file mode 100644
index 0000000..b8144e8
--- /dev/null
+++ b/smartt-perf/util/alias.c
@@ -0,0 +1,77 @@
+#include "cache.h"
+
+static const char *alias_key;
+static char *alias_val;
+
+static int alias_lookup_cb(const char *k, const char *v, void *cb __used)
+{
+ if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
+ if (!v)
+ return config_error_nonbool(k);
+ alias_val = strdup(v);
+ return 0;
+ }
+ return 0;
+}
+
+char *alias_lookup(const char *alias)
+{
+ alias_key = alias;
+ alias_val = NULL;
+ perf_config(alias_lookup_cb, NULL);
+ return alias_val;
+}
+
+int split_cmdline(char *cmdline, const char ***argv)
+{
+ int src, dst, count = 0, size = 16;
+ char quoted = 0;
+
+ *argv = malloc(sizeof(char*) * size);
+
+ /* split alias_string */
+ (*argv)[count++] = cmdline;
+ for (src = dst = 0; cmdline[src];) {
+ char c = cmdline[src];
+ if (!quoted && isspace(c)) {
+ cmdline[dst++] = 0;
+ while (cmdline[++src]
+ && isspace(cmdline[src]))
+ ; /* skip */
+ if (count >= size) {
+ size += 16;
+ *argv = realloc(*argv, sizeof(char*) * size);
+ }
+ (*argv)[count++] = cmdline + dst;
+ } else if (!quoted && (c == '\'' || c == '"')) {
+ quoted = c;
+ src++;
+ } else if (c == quoted) {
+ quoted = 0;
+ src++;
+ } else {
+ if (c == '\\' && quoted != '\'') {
+ src++;
+ c = cmdline[src];
+ if (!c) {
+ free(*argv);
+ *argv = NULL;
+ return error("cmdline ends with \\");
+ }
+ }
+ cmdline[dst++] = c;
+ src++;
+ }
+ }
+
+ cmdline[dst] = 0;
+
+ if (quoted) {
+ free(*argv);
+ *argv = NULL;
+ return error("unclosed quote");
+ }
+
+ return count;
+}
+
diff --git a/smartt-perf/util/bitmap.c b/smartt-perf/util/bitmap.c
new file mode 100644
index 0000000..5e230ac
--- /dev/null
+++ b/smartt-perf/util/bitmap.c
@@ -0,0 +1,21 @@
+/*
+ * From lib/bitmap.c
+ * Helper functions for bitmap.h.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include <linux/bitmap.h>
+
+int __bitmap_weight(const unsigned long *bitmap, int bits)
+{
+ int k, w = 0, lim = bits/BITS_PER_LONG;
+
+ for (k = 0; k < lim; k++)
+ w += hweight_long(bitmap[k]);
+
+ if (bits % BITS_PER_LONG)
+ w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
+
+ return w;
+}
diff --git a/smartt-perf/util/build-id.c b/smartt-perf/util/build-id.c
new file mode 100644
index 0000000..deffb8c
--- /dev/null
+++ b/smartt-perf/util/build-id.c
@@ -0,0 +1,80 @@
+/*
+ * build-id.c
+ *
+ * build-id support
+ *
+ * Copyright (C) 2009, 2010 Red Hat Inc.
+ * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+#include "util.h"
+#include <stdio.h>
+#include "build-id.h"
+#include "event.h"
+#include "symbol.h"
+#include <linux/kernel.h>
+#include "debug.h"
+
+static int build_id__mark_dso_hit(event_t *event,
+ struct sample_data *sample __used,
+ struct perf_session *session)
+{
+ struct addr_location al;
+ u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ struct thread *thread = perf_session__findnew(session, event->ip.pid);
+
+ if (thread == NULL) {
+ pr_err("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
+ event->ip.pid, event->ip.ip, &al);
+
+ if (al.map != NULL)
+ al.map->dso->hit = 1;
+
+ return 0;
+}
+
+static int event__exit_del_thread(event_t *self, struct sample_data *sample __used,
+ struct perf_session *session)
+{
+ struct thread *thread = perf_session__findnew(session, self->fork.tid);
+
+ dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
+ self->fork.ppid, self->fork.ptid);
+
+ if (thread) {
+ rb_erase(&thread->rb_node, &session->threads);
+ session->last_match = NULL;
+ thread__delete(thread);
+ }
+
+ return 0;
+}
+
+struct perf_event_ops build_id__mark_dso_hit_ops = {
+ .sample = build_id__mark_dso_hit,
+ .mmap = event__process_mmap,
+ .fork = event__process_task,
+ .exit = event__exit_del_thread,
+};
+
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
+{
+ char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+
+ if (!self->has_build_id)
+ return NULL;
+
+ build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex);
+ if (bf == NULL) {
+ if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,
+ build_id_hex, build_id_hex + 2) < 0)
+ return NULL;
+ } else
+ snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
+ build_id_hex, build_id_hex + 2);
+ return bf;
+}
diff --git a/smartt-perf/util/build-id.h b/smartt-perf/util/build-id.h
new file mode 100644
index 0000000..5dafb00
--- /dev/null
+++ b/smartt-perf/util/build-id.h
@@ -0,0 +1,10 @@
+#ifndef PERF_BUILD_ID_H_
+#define PERF_BUILD_ID_H_ 1
+
+#include "session.h"
+
+extern struct perf_event_ops build_id__mark_dso_hit_ops;
+
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
+
+#endif
diff --git a/smartt-perf/util/cache.h b/smartt-perf/util/cache.h
new file mode 100644
index 0000000..a772979
--- /dev/null
+++ b/smartt-perf/util/cache.h
@@ -0,0 +1,89 @@
+#ifndef __PERF_CACHE_H
+#define __PERF_CACHE_H
+
+#include <stdbool.h>
+#include "util.h"
+#include "strbuf.h"
+#include "../perf.h"
+
+#define CMD_EXEC_PATH "--exec-path"
+#define CMD_PERF_DIR "--perf-dir="
+#define CMD_WORK_TREE "--work-tree="
+#define CMD_DEBUGFS_DIR "--debugfs-dir="
+
+#define PERF_DIR_ENVIRONMENT "PERF_DIR"
+#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
+#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
+#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
+#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
+
+typedef int (*config_fn_t)(const char *, const char *, void *);
+extern int perf_default_config(const char *, const char *, void *);
+extern int perf_config(config_fn_t fn, void *);
+extern int perf_config_int(const char *, const char *);
+extern int perf_config_bool(const char *, const char *);
+extern int config_error_nonbool(const char *);
+extern const char *perf_config_dirname(const char *, const char *);
+
+/* pager.c */
+extern void setup_pager(void);
+extern const char *pager_program;
+extern int pager_in_use(void);
+extern int pager_use_color;
+
+extern int use_browser;
+
+#ifdef NO_NEWT_SUPPORT
+static inline void setup_browser(void)
+{
+ setup_pager();
+}
+static inline void exit_browser(bool wait_for_ok __used) {}
+#else
+void setup_browser(void);
+void exit_browser(bool wait_for_ok);
+#endif
+
+char *alias_lookup(const char *alias);
+int split_cmdline(char *cmdline, const char ***argv);
+
+#define alloc_nr(x) (((x)+16)*3/2)
+
+/*
+ * Realloc the buffer pointed at by variable 'x' so that it can hold
+ * at least 'nr' entries; the number of entries currently allocated
+ * is 'alloc', using the standard growing factor alloc_nr() macro.
+ *
+ * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
+ */
+#define ALLOC_GROW(x, nr, alloc) \
+ do { \
+ if ((nr) > alloc) { \
+ if (alloc_nr(alloc) < (nr)) \
+ alloc = (nr); \
+ else \
+ alloc = alloc_nr(alloc); \
+ x = xrealloc((x), alloc * sizeof(*(x))); \
+ } \
+ } while(0)
+
+
+static inline int is_absolute_path(const char *path)
+{
+ return path[0] == '/';
+}
+
+const char *make_nonrelative_path(const char *path);
+char *strip_path_suffix(const char *path, const char *suffix);
+
+extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+
+extern char *perf_pathdup(const char *fmt, ...)
+ __attribute__((format (printf, 1, 2)));
+
+#ifdef NO_STRLCPY
+extern size_t strlcpy(char *dest, const char *src, size_t size);
+#endif
+
+#endif /* __PERF_CACHE_H */
diff --git a/smartt-perf/util/callchain.c b/smartt-perf/util/callchain.c
new file mode 100644
index 0000000..e12d539
--- /dev/null
+++ b/smartt-perf/util/callchain.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com>
+ *
+ * Handle the callchains from the stream in an ad-hoc radix tree and then
+ * sort them in an rbtree.
+ *
+ * Using a radix for code path provides a fast retrieval and factorizes
+ * memory use. Also that lets us use the paths in a hierarchical graph view.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <math.h>
+
+#include "util.h"
+#include "callchain.h"
+
+bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
+{
+ unsigned int chain_size = event->header.size;
+ chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
+ return chain->nr * sizeof(u64) <= chain_size;
+}
+
+#define chain_for_each_child(child, parent) \
+ list_for_each_entry(child, &parent->children, brothers)
+
+#define chain_for_each_child_safe(child, next, parent) \
+ list_for_each_entry_safe(child, next, &parent->children, brothers)
+
+static void
+rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
+ enum chain_mode mode)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct callchain_node *rnode;
+ u64 chain_cumul = cumul_hits(chain);
+
+ while (*p) {
+ u64 rnode_cumul;
+
+ parent = *p;
+ rnode = rb_entry(parent, struct callchain_node, rb_node);
+ rnode_cumul = cumul_hits(rnode);
+
+ switch (mode) {
+ case CHAIN_FLAT:
+ if (rnode->hit < chain->hit)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ break;
+ case CHAIN_GRAPH_ABS: /* Falldown */
+ case CHAIN_GRAPH_REL:
+ if (rnode_cumul < chain_cumul)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ break;
+ case CHAIN_NONE:
+ default:
+ break;
+ }
+ }
+
+ rb_link_node(&chain->rb_node, parent, p);
+ rb_insert_color(&chain->rb_node, root);
+}
+
+static void
+__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
+ u64 min_hit)
+{
+ struct callchain_node *child;
+
+ chain_for_each_child(child, node)
+ __sort_chain_flat(rb_root, child, min_hit);
+
+ if (node->hit && node->hit >= min_hit)
+ rb_insert_callchain(rb_root, node, CHAIN_FLAT);
+}
+
+/*
+ * Once we get every callchains from the stream, we can now
+ * sort them by hit
+ */
+static void
+sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
+ u64 min_hit, struct callchain_param *param __used)
+{
+ __sort_chain_flat(rb_root, &root->node, min_hit);
+}
+
+static void __sort_chain_graph_abs(struct callchain_node *node,
+ u64 min_hit)
+{
+ struct callchain_node *child;
+
+ node->rb_root = RB_ROOT;
+
+ chain_for_each_child(child, node) {
+ __sort_chain_graph_abs(child, min_hit);
+ if (cumul_hits(child) >= min_hit)
+ rb_insert_callchain(&node->rb_root, child,
+ CHAIN_GRAPH_ABS);
+ }
+}
+
+static void
+sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
+ u64 min_hit, struct callchain_param *param __used)
+{
+ __sort_chain_graph_abs(&chain_root->node, min_hit);
+ rb_root->rb_node = chain_root->node.rb_root.rb_node;
+}
+
+static void __sort_chain_graph_rel(struct callchain_node *node,
+ double min_percent)
+{
+ struct callchain_node *child;
+ u64 min_hit;
+
+ node->rb_root = RB_ROOT;
+ min_hit = ceil(node->children_hit * min_percent);
+
+ chain_for_each_child(child, node) {
+ __sort_chain_graph_rel(child, min_percent);
+ if (cumul_hits(child) >= min_hit)
+ rb_insert_callchain(&node->rb_root, child,
+ CHAIN_GRAPH_REL);
+ }
+}
+
+static void
+sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
+ u64 min_hit __used, struct callchain_param *param)
+{
+ __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
+ rb_root->rb_node = chain_root->node.rb_root.rb_node;
+}
+
+int register_callchain_param(struct callchain_param *param)
+{
+ switch (param->mode) {
+ case CHAIN_GRAPH_ABS:
+ param->sort = sort_chain_graph_abs;
+ break;
+ case CHAIN_GRAPH_REL:
+ param->sort = sort_chain_graph_rel;
+ break;
+ case CHAIN_FLAT:
+ param->sort = sort_chain_flat;
+ break;
+ case CHAIN_NONE:
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Create a child for a parent. If inherit_children, then the new child
+ * will become the new parent of it's parent children
+ */
+static struct callchain_node *
+create_child(struct callchain_node *parent, bool inherit_children)
+{
+ struct callchain_node *new;
+
+ new = zalloc(sizeof(*new));
+ if (!new) {
+ perror("not enough memory to create child for code path tree");
+ return NULL;
+ }
+ new->parent = parent;
+ INIT_LIST_HEAD(&new->children);
+ INIT_LIST_HEAD(&new->val);
+
+ if (inherit_children) {
+ struct callchain_node *next;
+
+ list_splice(&parent->children, &new->children);
+ INIT_LIST_HEAD(&parent->children);
+
+ chain_for_each_child(next, new)
+ next->parent = new;
+ }
+ list_add_tail(&new->brothers, &parent->children);
+
+ return new;
+}
+
+
+struct resolved_ip {
+ u64 ip;
+ struct map_symbol ms;
+};
+
+struct resolved_chain {
+ u64 nr;
+ struct resolved_ip ips[0];
+};
+
+
+/*
+ * Fill the node with callchain values
+ */
+static void
+fill_node(struct callchain_node *node, struct resolved_chain *chain, int start)
+{
+ unsigned int i;
+
+ for (i = start; i < chain->nr; i++) {
+ struct callchain_list *call;
+
+ call = zalloc(sizeof(*call));
+ if (!call) {
+ perror("not enough memory for the code path tree");
+ return;
+ }
+ call->ip = chain->ips[i].ip;
+ call->ms = chain->ips[i].ms;
+ list_add_tail(&call->list, &node->val);
+ }
+ node->val_nr = chain->nr - start;
+ if (!node->val_nr)
+ pr_warning("Warning: empty node in callchain tree\n");
+}
+
+static void
+add_child(struct callchain_node *parent, struct resolved_chain *chain,
+ int start, u64 period)
+{
+ struct callchain_node *new;
+
+ new = create_child(parent, false);
+ fill_node(new, chain, start);
+
+ new->children_hit = 0;
+ new->hit = period;
+}
+
+/*
+ * Split the parent in two parts (a new child is created) and
+ * give a part of its callchain to the created child.
+ * Then create another child to host the given callchain of new branch
+ */
+static void
+split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
+ struct callchain_list *to_split, int idx_parents, int idx_local,
+ u64 period)
+{
+ struct callchain_node *new;
+ struct list_head *old_tail;
+ unsigned int idx_total = idx_parents + idx_local;
+
+ /* split */
+ new = create_child(parent, true);
+
+ /* split the callchain and move a part to the new child */
+ old_tail = parent->val.prev;
+ list_del_range(&to_split->list, old_tail);
+ new->val.next = &to_split->list;
+ new->val.prev = old_tail;
+ to_split->list.prev = &new->val;
+ old_tail->next = &new->val;
+
+ /* split the hits */
+ new->hit = parent->hit;
+ new->children_hit = parent->children_hit;
+ parent->children_hit = cumul_hits(new);
+ new->val_nr = parent->val_nr - idx_local;
+ parent->val_nr = idx_local;
+
+ /* create a new child for the new branch if any */
+ if (idx_total < chain->nr) {
+ parent->hit = 0;
+ add_child(parent, chain, idx_total, period);
+ parent->children_hit += period;
+ } else {
+ parent->hit = period;
+ }
+}
+
+static int
+append_chain(struct callchain_node *root, struct resolved_chain *chain,
+ unsigned int start, u64 period);
+
+static void
+append_chain_children(struct callchain_node *root, struct resolved_chain *chain,
+ unsigned int start, u64 period)
+{
+ struct callchain_node *rnode;
+
+ /* lookup in childrens */
+ chain_for_each_child(rnode, root) {
+ unsigned int ret = append_chain(rnode, chain, start, period);
+
+ if (!ret)
+ goto inc_children_hit;
+ }
+ /* nothing in children, add to the current node */
+ add_child(root, chain, start, period);
+
+inc_children_hit:
+ root->children_hit += period;
+}
+
+static int
+append_chain(struct callchain_node *root, struct resolved_chain *chain,
+ unsigned int start, u64 period)
+{
+ struct callchain_list *cnode;
+ unsigned int i = start;
+ bool found = false;
+
+ /*
+ * Lookup in the current node
+ * If we have a symbol, then compare the start to match
+ * anywhere inside a function.
+ */
+ list_for_each_entry(cnode, &root->val, list) {
+ struct symbol *sym;
+
+ if (i == chain->nr)
+ break;
+
+ sym = chain->ips[i].ms.sym;
+
+ if (cnode->ms.sym && sym) {
+ if (cnode->ms.sym->start != sym->start)
+ break;
+ } else if (cnode->ip != chain->ips[i].ip)
+ break;
+
+ if (!found)
+ found = true;
+ i++;
+ }
+
+ /* matches not, relay on the parent */
+ if (!found)
+ return -1;
+
+ /* we match only a part of the node. Split it and add the new chain */
+ if (i - start < root->val_nr) {
+ split_add_child(root, chain, cnode, start, i - start, period);
+ return 0;
+ }
+
+ /* we match 100% of the path, increment the hit */
+ if (i - start == root->val_nr && i == chain->nr) {
+ root->hit += period;
+ return 0;
+ }
+
+ /* We match the node and still have a part remaining */
+ append_chain_children(root, chain, i, period);
+
+ return 0;
+}
+
+static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
+ struct map_symbol *syms)
+{
+ int i, j = 0;
+
+ for (i = 0; i < (int)old->nr; i++) {
+ if (old->ips[i] >= PERF_CONTEXT_MAX)
+ continue;
+
+ new->ips[j].ip = old->ips[i];
+ new->ips[j].ms = syms[i];
+ j++;
+ }
+
+ new->nr = j;
+}
+
+
+int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
+ struct map_symbol *syms, u64 period)
+{
+ struct resolved_chain *filtered;
+
+ if (!chain->nr)
+ return 0;
+
+ filtered = zalloc(sizeof(*filtered) +
+ chain->nr * sizeof(struct resolved_ip));
+ if (!filtered)
+ return -ENOMEM;
+
+ filter_context(chain, filtered, syms);
+
+ if (!filtered->nr)
+ goto end;
+
+ append_chain_children(&root->node, filtered, 0, period);
+
+ if (filtered->nr > root->max_depth)
+ root->max_depth = filtered->nr;
+end:
+ free(filtered);
+
+ return 0;
+}
+
+static int
+merge_chain_branch(struct callchain_node *dst, struct callchain_node *src,
+ struct resolved_chain *chain)
+{
+ struct callchain_node *child, *next_child;
+ struct callchain_list *list, *next_list;
+ int old_pos = chain->nr;
+ int err = 0;
+
+ list_for_each_entry_safe(list, next_list, &src->val, list) {
+ chain->ips[chain->nr].ip = list->ip;
+ chain->ips[chain->nr].ms = list->ms;
+ chain->nr++;
+ list_del(&list->list);
+ free(list);
+ }
+
+ if (src->hit)
+ append_chain_children(dst, chain, 0, src->hit);
+
+ chain_for_each_child_safe(child, next_child, src) {
+ err = merge_chain_branch(dst, child, chain);
+ if (err)
+ break;
+
+ list_del(&child->brothers);
+ free(child);
+ }
+
+ chain->nr = old_pos;
+
+ return err;
+}
+
+int callchain_merge(struct callchain_root *dst, struct callchain_root *src)
+{
+ struct resolved_chain *chain;
+ int err;
+
+ chain = malloc(sizeof(*chain) +
+ src->max_depth * sizeof(struct resolved_ip));
+ if (!chain)
+ return -ENOMEM;
+
+ chain->nr = 0;
+
+ err = merge_chain_branch(&dst->node, &src->node, chain);
+
+ free(chain);
+
+ return err;
+}
diff --git a/smartt-perf/util/callchain.h b/smartt-perf/util/callchain.h
new file mode 100644
index 0000000..c15fb8c
--- /dev/null
+++ b/smartt-perf/util/callchain.h
@@ -0,0 +1,75 @@
+#ifndef __PERF_CALLCHAIN_H
+#define __PERF_CALLCHAIN_H
+
+#include "../perf.h"
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include "event.h"
+#include "symbol.h"
+
+enum chain_mode {
+ CHAIN_NONE,
+ CHAIN_FLAT,
+ CHAIN_GRAPH_ABS,
+ CHAIN_GRAPH_REL
+};
+
+struct callchain_node {
+ struct callchain_node *parent;
+ struct list_head brothers;
+ struct list_head children;
+ struct list_head val;
+ struct rb_node rb_node; /* to sort nodes in an rbtree */
+ struct rb_root rb_root; /* sorted tree of children */
+ unsigned int val_nr;
+ u64 hit;
+ u64 children_hit;
+};
+
+struct callchain_root {
+ u64 max_depth;
+ struct callchain_node node;
+};
+
+struct callchain_param;
+
+typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
+ u64, struct callchain_param *);
+
+struct callchain_param {
+ enum chain_mode mode;
+ u32 print_limit;
+ double min_percent;
+ sort_chain_func_t sort;
+};
+
+struct callchain_list {
+ u64 ip;
+ struct map_symbol ms;
+ struct list_head list;
+};
+
+static inline void callchain_init(struct callchain_root *root)
+{
+ INIT_LIST_HEAD(&root->node.brothers);
+ INIT_LIST_HEAD(&root->node.children);
+ INIT_LIST_HEAD(&root->node.val);
+
+ root->node.parent = NULL;
+ root->node.hit = 0;
+ root->node.children_hit = 0;
+ root->max_depth = 0;
+}
+
+static inline u64 cumul_hits(struct callchain_node *node)
+{
+ return node->hit + node->children_hit;
+}
+
+int register_callchain_param(struct callchain_param *param);
+int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
+ struct map_symbol *syms, u64 period);
+int callchain_merge(struct callchain_root *dst, struct callchain_root *src);
+
+bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
+#endif /* __PERF_CALLCHAIN_H */
diff --git a/smartt-perf/util/color.c b/smartt-perf/util/color.c
new file mode 100644
index 0000000..e191eb9
--- /dev/null
+++ b/smartt-perf/util/color.c
@@ -0,0 +1,324 @@
+#include "cache.h"
+#include "color.h"
+
+int perf_use_color_default = -1;
+
+static int parse_color(const char *name, int len)
+{
+ static const char * const color_names[] = {
+ "normal", "black", "red", "green", "yellow",
+ "blue", "magenta", "cyan", "white"
+ };
+ char *end;
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
+ const char *str = color_names[i];
+ if (!strncasecmp(name, str, len) && !str[len])
+ return i - 1;
+ }
+ i = strtol(name, &end, 10);
+ if (end - name == len && i >= -1 && i <= 255)
+ return i;
+ return -2;
+}
+
+static int parse_attr(const char *name, int len)
+{
+ static const int attr_values[] = { 1, 2, 4, 5, 7 };
+ static const char * const attr_names[] = {
+ "bold", "dim", "ul", "blink", "reverse"
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
+ const char *str = attr_names[i];
+ if (!strncasecmp(name, str, len) && !str[len])
+ return attr_values[i];
+ }
+ return -1;
+}
+
+void color_parse(const char *value, const char *var, char *dst)
+{
+ color_parse_mem(value, strlen(value), var, dst);
+}
+
+void color_parse_mem(const char *value, int value_len, const char *var,
+ char *dst)
+{
+ const char *ptr = value;
+ int len = value_len;
+ int attr = -1;
+ int fg = -2;
+ int bg = -2;
+
+ if (!strncasecmp(value, "reset", len)) {
+ strcpy(dst, PERF_COLOR_RESET);
+ return;
+ }
+
+ /* [fg [bg]] [attr] */
+ while (len > 0) {
+ const char *word = ptr;
+ int val, wordlen = 0;
+
+ while (len > 0 && !isspace(word[wordlen])) {
+ wordlen++;
+ len--;
+ }
+
+ ptr = word + wordlen;
+ while (len > 0 && isspace(*ptr)) {
+ ptr++;
+ len--;
+ }
+
+ val = parse_color(word, wordlen);
+ if (val >= -1) {
+ if (fg == -2) {
+ fg = val;
+ continue;
+ }
+ if (bg == -2) {
+ bg = val;
+ continue;
+ }
+ goto bad;
+ }
+ val = parse_attr(word, wordlen);
+ if (val < 0 || attr != -1)
+ goto bad;
+ attr = val;
+ }
+
+ if (attr >= 0 || fg >= 0 || bg >= 0) {
+ int sep = 0;
+
+ *dst++ = '\033';
+ *dst++ = '[';
+ if (attr >= 0) {
+ *dst++ = '0' + attr;
+ sep++;
+ }
+ if (fg >= 0) {
+ if (sep++)
+ *dst++ = ';';
+ if (fg < 8) {
+ *dst++ = '3';
+ *dst++ = '0' + fg;
+ } else {
+ dst += sprintf(dst, "38;5;%d", fg);
+ }
+ }
+ if (bg >= 0) {
+ if (sep++)
+ *dst++ = ';';
+ if (bg < 8) {
+ *dst++ = '4';
+ *dst++ = '0' + bg;
+ } else {
+ dst += sprintf(dst, "48;5;%d", bg);
+ }
+ }
+ *dst++ = 'm';
+ }
+ *dst = 0;
+ return;
+bad:
+ die("bad color value '%.*s' for variable '%s'", value_len, value, var);
+}
+
+int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty)
+{
+ if (value) {
+ if (!strcasecmp(value, "never"))
+ return 0;
+ if (!strcasecmp(value, "always"))
+ return 1;
+ if (!strcasecmp(value, "auto"))
+ goto auto_color;
+ }
+
+ /* Missing or explicit false to turn off colorization */
+ if (!perf_config_bool(var, value))
+ return 0;
+
+ /* any normal truth value defaults to 'auto' */
+ auto_color:
+ if (stdout_is_tty < 0)
+ stdout_is_tty = isatty(1);
+ if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
+ char *term = getenv("TERM");
+ if (term && strcmp(term, "dumb"))
+ return 1;
+ }
+ return 0;
+}
+
+int perf_color_default_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "color.ui")) {
+ perf_use_color_default = perf_config_colorbool(var, value, -1);
+ return 0;
+ }
+
+ return perf_default_config(var, value, cb);
+}
+
+static int __color_vsnprintf(char *bf, size_t size, const char *color,
+ const char *fmt, va_list args, const char *trail)
+{
+ int r = 0;
+
+ /*
+ * Auto-detect:
+ */
+ if (perf_use_color_default < 0) {
+ if (isatty(1) || pager_in_use())
+ perf_use_color_default = 1;
+ else
+ perf_use_color_default = 0;
+ }
+
+ if (perf_use_color_default && *color)
+ r += snprintf(bf, size, "%s", color);
+ r += vsnprintf(bf + r, size - r, fmt, args);
+ if (perf_use_color_default && *color)
+ r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET);
+ if (trail)
+ r += snprintf(bf + r, size - r, "%s", trail);
+ return r;
+}
+
+static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,
+ va_list args, const char *trail)
+{
+ int r = 0;
+
+ /*
+ * Auto-detect:
+ */
+ if (perf_use_color_default < 0) {
+ if (isatty(1) || pager_in_use())
+ perf_use_color_default = 1;
+ else
+ perf_use_color_default = 0;
+ }
+
+ if (perf_use_color_default && *color)
+ r += fprintf(fp, "%s", color);
+ r += vfprintf(fp, fmt, args);
+ if (perf_use_color_default && *color)
+ r += fprintf(fp, "%s", PERF_COLOR_RESET);
+ if (trail)
+ r += fprintf(fp, "%s", trail);
+ return r;
+}
+
+int color_vsnprintf(char *bf, size_t size, const char *color,
+ const char *fmt, va_list args)
+{
+ return __color_vsnprintf(bf, size, color, fmt, args, NULL);
+}
+
+int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args)
+{
+ return __color_vfprintf(fp, color, fmt, args, NULL);
+}
+
+int color_snprintf(char *bf, size_t size, const char *color,
+ const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+ r = color_vsnprintf(bf, size, color, fmt, args);
+ va_end(args);
+ return r;
+}
+
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+ r = color_vfprintf(fp, color, fmt, args);
+ va_end(args);
+ return r;
+}
+
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+ va_start(args, fmt);
+ r = __color_vfprintf(fp, color, fmt, args, "\n");
+ va_end(args);
+ return r;
+}
+
+/*
+ * This function splits the buffer by newlines and colors the lines individually.
+ *
+ * Returns 0 on success.
+ */
+int color_fwrite_lines(FILE *fp, const char *color,
+ size_t count, const char *buf)
+{
+ if (!*color)
+ return fwrite(buf, count, 1, fp) != 1;
+
+ while (count) {
+ char *p = memchr(buf, '\n', count);
+
+ if (p != buf && (fputs(color, fp) < 0 ||
+ fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||
+ fputs(PERF_COLOR_RESET, fp) < 0))
+ return -1;
+ if (!p)
+ return 0;
+ if (fputc('\n', fp) < 0)
+ return -1;
+ count -= p + 1 - buf;
+ buf = p + 1;
+ }
+ return 0;
+}
+
+const char *get_percent_color(double percent)
+{
+ const char *color = PERF_COLOR_NORMAL;
+
+ /*
+ * We color high-overhead entries in red, mid-overhead
+ * entries in green - and keep the low overhead places
+ * normal:
+ */
+ if (percent >= MIN_RED)
+ color = PERF_COLOR_RED;
+ else {
+ if (percent > MIN_GREEN)
+ color = PERF_COLOR_GREEN;
+ }
+ return color;
+}
+
+int percent_color_fprintf(FILE *fp, const char *fmt, double percent)
+{
+ int r;
+ const char *color;
+
+ color = get_percent_color(percent);
+ r = color_fprintf(fp, color, fmt, percent);
+
+ return r;
+}
+
+int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent)
+{
+ const char *color = get_percent_color(percent);
+ return color_snprintf(bf, size, color, fmt, percent);
+}
diff --git a/smartt-perf/util/color.h b/smartt-perf/util/color.h
new file mode 100644
index 0000000..dea082b
--- /dev/null
+++ b/smartt-perf/util/color.h
@@ -0,0 +1,46 @@
+#ifndef __PERF_COLOR_H
+#define __PERF_COLOR_H
+
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+#define COLOR_MAXLEN 24
+
+#define PERF_COLOR_NORMAL ""
+#define PERF_COLOR_RESET "\033[m"
+#define PERF_COLOR_BOLD "\033[1m"
+#define PERF_COLOR_RED "\033[31m"
+#define PERF_COLOR_GREEN "\033[32m"
+#define PERF_COLOR_YELLOW "\033[33m"
+#define PERF_COLOR_BLUE "\033[34m"
+#define PERF_COLOR_MAGENTA "\033[35m"
+#define PERF_COLOR_CYAN "\033[36m"
+#define PERF_COLOR_BG_RED "\033[41m"
+
+#define MIN_GREEN 0.5
+#define MIN_RED 5.0
+
+/*
+ * This variable stores the value of color.ui
+ */
+extern int perf_use_color_default;
+
+
+/*
+ * Use this instead of perf_default_config if you need the value of color.ui.
+ */
+int perf_color_default_config(const char *var, const char *value, void *cb);
+
+int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty);
+void color_parse(const char *value, const char *var, char *dst);
+void color_parse_mem(const char *value, int len, const char *var, char *dst);
+int color_vsnprintf(char *bf, size_t size, const char *color,
+ const char *fmt, va_list args);
+int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args);
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
+int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...);
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
+int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
+int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent);
+int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
+const char *get_percent_color(double percent);
+
+#endif /* __PERF_COLOR_H */
diff --git a/smartt-perf/util/config.c b/smartt-perf/util/config.c
new file mode 100644
index 0000000..e02d78c
--- /dev/null
+++ b/smartt-perf/util/config.c
@@ -0,0 +1,492 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ * Copyright (C) Johannes Schindelin, 2005
+ *
+ */
+#include "util.h"
+#include "cache.h"
+#include "exec_cmd.h"
+
+#define MAXNAME (256)
+
+#define DEBUG_CACHE_DIR ".debug"
+
+
+char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
+
+static FILE *config_file;
+static const char *config_file_name;
+static int config_linenr;
+static int config_file_eof;
+
+static const char *config_exclusive_filename;
+
+static int get_next_char(void)
+{
+ int c;
+ FILE *f;
+
+ c = '\n';
+ if ((f = config_file) != NULL) {
+ c = fgetc(f);
+ if (c == '\r') {
+ /* DOS like systems */
+ c = fgetc(f);
+ if (c != '\n') {
+ ungetc(c, f);
+ c = '\r';
+ }
+ }
+ if (c == '\n')
+ config_linenr++;
+ if (c == EOF) {
+ config_file_eof = 1;
+ c = '\n';
+ }
+ }
+ return c;
+}
+
+static char *parse_value(void)
+{
+ static char value[1024];
+ int quote = 0, comment = 0, space = 0;
+ size_t len = 0;
+
+ for (;;) {
+ int c = get_next_char();
+
+ if (len >= sizeof(value) - 1)
+ return NULL;
+ if (c == '\n') {
+ if (quote)
+ return NULL;
+ value[len] = 0;
+ return value;
+ }
+ if (comment)
+ continue;
+ if (isspace(c) && !quote) {
+ space = 1;
+ continue;
+ }
+ if (!quote) {
+ if (c == ';' || c == '#') {
+ comment = 1;
+ continue;
+ }
+ }
+ if (space) {
+ if (len)
+ value[len++] = ' ';
+ space = 0;
+ }
+ if (c == '\\') {
+ c = get_next_char();
+ switch (c) {
+ case '\n':
+ continue;
+ case 't':
+ c = '\t';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ /* Some characters escape as themselves */
+ case '\\': case '"':
+ break;
+ /* Reject unknown escape sequences */
+ default:
+ return NULL;
+ }
+ value[len++] = c;
+ continue;
+ }
+ if (c == '"') {
+ quote = 1-quote;
+ continue;
+ }
+ value[len++] = c;
+ }
+}
+
+static inline int iskeychar(int c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
+{
+ int c;
+ char *value;
+
+ /* Get the full name */
+ for (;;) {
+ c = get_next_char();
+ if (config_file_eof)
+ break;
+ if (!iskeychar(c))
+ break;
+ name[len++] = c;
+ if (len >= MAXNAME)
+ return -1;
+ }
+ name[len] = 0;
+ while (c == ' ' || c == '\t')
+ c = get_next_char();
+
+ value = NULL;
+ if (c != '\n') {
+ if (c != '=')
+ return -1;
+ value = parse_value();
+ if (!value)
+ return -1;
+ }
+ return fn(name, value, data);
+}
+
+static int get_extended_base_var(char *name, int baselen, int c)
+{
+ do {
+ if (c == '\n')
+ return -1;
+ c = get_next_char();
+ } while (isspace(c));
+
+ /* We require the format to be '[base "extension"]' */
+ if (c != '"')
+ return -1;
+ name[baselen++] = '.';
+
+ for (;;) {
+ int ch = get_next_char();
+
+ if (ch == '\n')
+ return -1;
+ if (ch == '"')
+ break;
+ if (ch == '\\') {
+ ch = get_next_char();
+ if (ch == '\n')
+ return -1;
+ }
+ name[baselen++] = ch;
+ if (baselen > MAXNAME / 2)
+ return -1;
+ }
+
+ /* Final ']' */
+ if (get_next_char() != ']')
+ return -1;
+ return baselen;
+}
+
+static int get_base_var(char *name)
+{
+ int baselen = 0;
+
+ for (;;) {
+ int c = get_next_char();
+ if (config_file_eof)
+ return -1;
+ if (c == ']')
+ return baselen;
+ if (isspace(c))
+ return get_extended_base_var(name, baselen, c);
+ if (!iskeychar(c) && c != '.')
+ return -1;
+ if (baselen > MAXNAME / 2)
+ return -1;
+ name[baselen++] = tolower(c);
+ }
+}
+
+static int perf_parse_file(config_fn_t fn, void *data)
+{
+ int comment = 0;
+ int baselen = 0;
+ static char var[MAXNAME];
+
+ /* U+FEFF Byte Order Mark in UTF8 */
+ static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
+ const unsigned char *bomptr = utf8_bom;
+
+ for (;;) {
+ int c = get_next_char();
+ if (bomptr && *bomptr) {
+ /* We are at the file beginning; skip UTF8-encoded BOM
+ * if present. Sane editors won't put this in on their
+ * own, but e.g. Windows Notepad will do it happily. */
+ if ((unsigned char) c == *bomptr) {
+ bomptr++;
+ continue;
+ } else {
+ /* Do not tolerate partial BOM. */
+ if (bomptr != utf8_bom)
+ break;
+ /* No BOM at file beginning. Cool. */
+ bomptr = NULL;
+ }
+ }
+ if (c == '\n') {
+ if (config_file_eof)
+ return 0;
+ comment = 0;
+ continue;
+ }
+ if (comment || isspace(c))
+ continue;
+ if (c == '#' || c == ';') {
+ comment = 1;
+ continue;
+ }
+ if (c == '[') {
+ baselen = get_base_var(var);
+ if (baselen <= 0)
+ break;
+ var[baselen++] = '.';
+ var[baselen] = 0;
+ continue;
+ }
+ if (!isalpha(c))
+ break;
+ var[baselen] = tolower(c);
+ if (get_value(fn, data, var, baselen+1) < 0)
+ break;
+ }
+ die("bad config file line %d in %s", config_linenr, config_file_name);
+}
+
+static int parse_unit_factor(const char *end, unsigned long *val)
+{
+ if (!*end)
+ return 1;
+ else if (!strcasecmp(end, "k")) {
+ *val *= 1024;
+ return 1;
+ }
+ else if (!strcasecmp(end, "m")) {
+ *val *= 1024 * 1024;
+ return 1;
+ }
+ else if (!strcasecmp(end, "g")) {
+ *val *= 1024 * 1024 * 1024;
+ return 1;
+ }
+ return 0;
+}
+
+static int perf_parse_long(const char *value, long *ret)
+{
+ if (value && *value) {
+ char *end;
+ long val = strtol(value, &end, 0);
+ unsigned long factor = 1;
+ if (!parse_unit_factor(end, &factor))
+ return 0;
+ *ret = val * factor;
+ return 1;
+ }
+ return 0;
+}
+
+static void die_bad_config(const char *name)
+{
+ if (config_file_name)
+ die("bad config value for '%s' in %s", name, config_file_name);
+ die("bad config value for '%s'", name);
+}
+
+int perf_config_int(const char *name, const char *value)
+{
+ long ret = 0;
+ if (!perf_parse_long(value, &ret))
+ die_bad_config(name);
+ return ret;
+}
+
+static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
+{
+ *is_bool = 1;
+ if (!value)
+ return 1;
+ if (!*value)
+ return 0;
+ if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
+ return 1;
+ if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
+ return 0;
+ *is_bool = 0;
+ return perf_config_int(name, value);
+}
+
+int perf_config_bool(const char *name, const char *value)
+{
+ int discard;
+ return !!perf_config_bool_or_int(name, value, &discard);
+}
+
+const char *perf_config_dirname(const char *name, const char *value)
+{
+ if (!name)
+ return NULL;
+ return value;
+}
+
+static int perf_default_core_config(const char *var __used, const char *value __used)
+{
+ /* Add other config variables here and to Documentation/config.txt. */
+ return 0;
+}
+
+int perf_default_config(const char *var, const char *value, void *dummy __used)
+{
+ if (!prefixcmp(var, "core."))
+ return perf_default_core_config(var, value);
+
+ /* Add other config variables here and to Documentation/config.txt. */
+ return 0;
+}
+
+static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
+{
+ int ret;
+ FILE *f = fopen(filename, "r");
+
+ ret = -1;
+ if (f) {
+ config_file = f;
+ config_file_name = filename;
+ config_linenr = 1;
+ config_file_eof = 0;
+ ret = perf_parse_file(fn, data);
+ fclose(f);
+ config_file_name = NULL;
+ }
+ return ret;
+}
+
+static const char *perf_etc_perfconfig(void)
+{
+ static const char *system_wide;
+ if (!system_wide)
+ system_wide = system_path(ETC_PERFCONFIG);
+ return system_wide;
+}
+
+static int perf_env_bool(const char *k, int def)
+{
+ const char *v = getenv(k);
+ return v ? perf_config_bool(k, v) : def;
+}
+
+static int perf_config_system(void)
+{
+ return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
+}
+
+static int perf_config_global(void)
+{
+ return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
+}
+
+int perf_config(config_fn_t fn, void *data)
+{
+ int ret = 0, found = 0;
+ char *repo_config = NULL;
+ const char *home = NULL;
+
+ /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
+ if (config_exclusive_filename)
+ return perf_config_from_file(fn, config_exclusive_filename, data);
+ if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
+ ret += perf_config_from_file(fn, perf_etc_perfconfig(),
+ data);
+ found += 1;
+ }
+
+ home = getenv("HOME");
+ if (perf_config_global() && home) {
+ char *user_config = strdup(mkpath("%s/.perfconfig", home));
+ if (!access(user_config, R_OK)) {
+ ret += perf_config_from_file(fn, user_config, data);
+ found += 1;
+ }
+ free(user_config);
+ }
+
+ repo_config = perf_pathdup("config");
+ if (!access(repo_config, R_OK)) {
+ ret += perf_config_from_file(fn, repo_config, data);
+ found += 1;
+ }
+ free(repo_config);
+ if (found == 0)
+ return -1;
+ return ret;
+}
+
+/*
+ * Call this to report error for your variable that should not
+ * get a boolean value (i.e. "[my] var" means "true").
+ */
+int config_error_nonbool(const char *var)
+{
+ return error("Missing value for '%s'", var);
+}
+
+struct buildid_dir_config {
+ char *dir;
+};
+
+static int buildid_dir_command_config(const char *var, const char *value,
+ void *data)
+{
+ struct buildid_dir_config *c = data;
+ const char *v;
+
+ /* same dir for all commands */
+ if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) {
+ v = perf_config_dirname(var, value);
+ if (!v)
+ return -1;
+ strncpy(c->dir, v, MAXPATHLEN-1);
+ c->dir[MAXPATHLEN-1] = '\0';
+ }
+ return 0;
+}
+
+static void check_buildid_dir_config(void)
+{
+ struct buildid_dir_config c;
+ c.dir = buildid_dir;
+ perf_config(buildid_dir_command_config, &c);
+}
+
+void set_buildid_dir(void)
+{
+ buildid_dir[0] = '\0';
+
+ /* try config file */
+ check_buildid_dir_config();
+
+ /* default to $HOME/.debug */
+ if (buildid_dir[0] == '\0') {
+ char *v = getenv("HOME");
+ if (v) {
+ snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
+ v, DEBUG_CACHE_DIR);
+ } else {
+ strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
+ }
+ buildid_dir[MAXPATHLEN-1] = '\0';
+ }
+ /* for communicating with external commands */
+ setenv("PERF_BUILDID_DIR", buildid_dir, 1);
+}
diff --git a/smartt-perf/util/connect.c b/smartt-perf/util/connect.c
new file mode 100644
index 0000000..944a38b
--- /dev/null
+++ b/smartt-perf/util/connect.c
@@ -0,0 +1,54 @@
+/*
+* build-id.c
+*
+* build-id support
+*
+* Copyright (C) 20011, 2012 Linaro Inc.
+* Copyright (C) 2011, 2012 Sudip Jain <sudip.jain@linaro.org>
+*/
+
+#include "util.h"
+#include "connect.h"
+
+int sock_desc;
+struct sockaddr_in server;
+struct s_system_stat sys_stats;
+
+void SMARTTperf_Connect(void)
+{
+ struct hostent *hp;
+
+ sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+
+ server.sin_family = AF_INET;
+ server.sin_port = (4000);
+
+ hp = gethostbyname("127.0.0.1");
+ bcopy (hp->h_addr,&(server.sin_addr),hp->h_length);
+}
+
+void SMARTTperf_StatPid(pid_t *pid)
+{
+ sys_stats.pid = *pid;
+}
+
+void SMARTTperf_StatMetrics(double *avg, const char *event)
+{
+
+ if (strcmp(event, "cycles") == 0 )
+ sys_stats.cpu_cycles = *avg;
+ else if (strcmp(event, "instructions") == 0 )
+ sys_stats.instructions = *avg;
+ else if (strcmp(event, "bus-cycles") == 0 )
+ sys_stats.bus_cycles = *avg;
+ else if (strcmp(event, "cache-misses") == 0 )
+ sys_stats.cache_misses = *avg;
+ else if (strcmp(event, "branches") == 0 )
+ sys_stats.branches = *avg;
+}
+
+void SMARTTperf_SendData(void)
+{
+ sys_stats.time_stamp = time(NULL);
+ sendto(sock_desc, (void *)&sys_stats, sizeof(sys_stats),0,(struct sockaddr *) &server, sizeof(server));
+}
diff --git a/smartt-perf/util/connect.h b/smartt-perf/util/connect.h
new file mode 100644
index 0000000..ab4eed9
--- /dev/null
+++ b/smartt-perf/util/connect.h
@@ -0,0 +1,24 @@
+
+#ifndef __PERF_CONNECT_H
+#define __PERF_CONNECT_H
+
+typedef unsigned int t_pid;
+
+struct s_system_stat {
+ t_pid pid;
+ unsigned long int cpu_cycles;
+ unsigned long int instructions;
+ unsigned long int bus_cycles;
+ unsigned long int cache_misses;
+ unsigned long int branches;
+ unsigned long int time_stamp;
+};
+
+
+
+void SMARTTperf_Connect(void);
+void SMARTTperf_StatPid(pid_t *);
+void SMARTTperf_StatMetrics(double *,const char *);
+void SMARTTperf_SendData(void);
+
+#endif /* __PERF_CONNECT_H */
diff --git a/smartt-perf/util/cpumap.c b/smartt-perf/util/cpumap.c
new file mode 100644
index 0000000..3ccaa10
--- /dev/null
+++ b/smartt-perf/util/cpumap.c
@@ -0,0 +1,179 @@
+#include "util.h"
+#include "../perf.h"
+#include "cpumap.h"
+#include <assert.h>
+#include <stdio.h>
+
+static struct cpu_map *cpu_map__default_new(void)
+{
+ struct cpu_map *cpus;
+ int nr_cpus;
+
+ nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (nr_cpus < 0)
+ return NULL;
+
+ cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
+ if (cpus != NULL) {
+ int i;
+ for (i = 0; i < nr_cpus; ++i)
+ cpus->map[i] = i;
+
+ cpus->nr = nr_cpus;
+ }
+
+ return cpus;
+}
+
+static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
+{
+ size_t payload_size = nr_cpus * sizeof(int);
+ struct cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
+
+ if (cpus != NULL) {
+ cpus->nr = nr_cpus;
+ memcpy(cpus->map, tmp_cpus, payload_size);
+ }
+
+ return cpus;
+}
+
+static struct cpu_map *cpu_map__read_all_cpu_map(void)
+{
+ struct cpu_map *cpus = NULL;
+ FILE *onlnf;
+ int nr_cpus = 0;
+ int *tmp_cpus = NULL, *tmp;
+ int max_entries = 0;
+ int n, cpu, prev;
+ char sep;
+
+ onlnf = fopen("/sys/devices/system/cpu/online", "r");
+ if (!onlnf)
+ return cpu_map__default_new();
+
+ sep = 0;
+ prev = -1;
+ for (;;) {
+ n = fscanf(onlnf, "%u%c", &cpu, &sep);
+ if (n <= 0)
+ break;
+ if (prev >= 0) {
+ int new_max = nr_cpus + cpu - prev - 1;
+
+ if (new_max >= max_entries) {
+ max_entries = new_max + MAX_NR_CPUS / 2;
+ tmp = realloc(tmp_cpus, max_entries * sizeof(int));
+ if (tmp == NULL)
+ goto out_free_tmp;
+ tmp_cpus = tmp;
+ }
+
+ while (++prev < cpu)
+ tmp_cpus[nr_cpus++] = prev;
+ }
+ if (nr_cpus == max_entries) {
+ max_entries += MAX_NR_CPUS;
+ tmp = realloc(tmp_cpus, max_entries * sizeof(int));
+ if (tmp == NULL)
+ goto out_free_tmp;
+ tmp_cpus = tmp;
+ }
+
+ tmp_cpus[nr_cpus++] = cpu;
+ if (n == 2 && sep == '-')
+ prev = cpu;
+ else
+ prev = -1;
+ if (n == 1 || sep == '\n')
+ break;
+ }
+
+ if (nr_cpus > 0)
+ cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
+ else
+ cpus = cpu_map__default_new();
+out_free_tmp:
+ free(tmp_cpus);
+ fclose(onlnf);
+ return cpus;
+}
+
+struct cpu_map *cpu_map__new(const char *cpu_list)
+{
+ struct cpu_map *cpus = NULL;
+ unsigned long start_cpu, end_cpu = 0;
+ char *p = NULL;
+ int i, nr_cpus = 0;
+ int *tmp_cpus = NULL, *tmp;
+ int max_entries = 0;
+
+ if (!cpu_list)
+ return cpu_map__read_all_cpu_map();
+
+ if (!isdigit(*cpu_list))
+ goto out;
+
+ while (isdigit(*cpu_list)) {
+ p = NULL;
+ start_cpu = strtoul(cpu_list, &p, 0);
+ if (start_cpu >= INT_MAX
+ || (*p != '\0' && *p != ',' && *p != '-'))
+ goto invalid;
+
+ if (*p == '-') {
+ cpu_list = ++p;
+ p = NULL;
+ end_cpu = strtoul(cpu_list, &p, 0);
+
+ if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
+ goto invalid;
+
+ if (end_cpu < start_cpu)
+ goto invalid;
+ } else {
+ end_cpu = start_cpu;
+ }
+
+ for (; start_cpu <= end_cpu; start_cpu++) {
+ /* check for duplicates */
+ for (i = 0; i < nr_cpus; i++)
+ if (tmp_cpus[i] == (int)start_cpu)
+ goto invalid;
+
+ if (nr_cpus == max_entries) {
+ max_entries += MAX_NR_CPUS;
+ tmp = realloc(tmp_cpus, max_entries * sizeof(int));
+ if (tmp == NULL)
+ goto invalid;
+ tmp_cpus = tmp;
+ }
+ tmp_cpus[nr_cpus++] = (int)start_cpu;
+ }
+ if (*p)
+ ++p;
+
+ cpu_list = p;
+ }
+
+ if (nr_cpus > 0)
+ cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
+ else
+ cpus = cpu_map__default_new();
+invalid:
+ free(tmp_cpus);
+out:
+ return cpus;
+}
+
+struct cpu_map *cpu_map__dummy_new(void)
+{
+ struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
+
+ if (cpus != NULL) {
+ cpus->nr = 1;
+ cpus->map[0] = -1;
+ }
+
+ return cpus;
+}
diff --git a/smartt-perf/util/cpumap.h b/smartt-perf/util/cpumap.h
new file mode 100644
index 0000000..f7a4f42
--- /dev/null
+++ b/smartt-perf/util/cpumap.h
@@ -0,0 +1,13 @@
+#ifndef __PERF_CPUMAP_H
+#define __PERF_CPUMAP_H
+
+struct cpu_map {
+ int nr;
+ int map[];
+};
+
+struct cpu_map *cpu_map__new(const char *cpu_list);
+struct cpu_map *cpu_map__dummy_new(void);
+void *cpu_map__delete(struct cpu_map *map);
+
+#endif /* __PERF_CPUMAP_H */
diff --git a/smartt-perf/util/ctype.c b/smartt-perf/util/ctype.c
new file mode 100644
index 0000000..3507362
--- /dev/null
+++ b/smartt-perf/util/ctype.c
@@ -0,0 +1,39 @@
+/*
+ * Sane locale-independent, ASCII ctype.
+ *
+ * No surprises, and works with signed and unsigned chars.
+ */
+#include "cache.h"
+
+enum {
+ S = GIT_SPACE,
+ A = GIT_ALPHA,
+ D = GIT_DIGIT,
+ G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
+ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */
+ P = GIT_PRINT_EXTRA, /* printable - alpha - digit - glob - regex */
+
+ PS = GIT_SPACE | GIT_PRINT_EXTRA,
+};
+
+unsigned char sane_ctype[256] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
+ PS,P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
+ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
+ A, A, A, A, A, A, A, A, A, A, A, G, G, P, R, P, /* 80.. 95 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
+ A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */
+ /* Nothing in the 128.. range */
+};
+
+const char *graph_line =
+ "_____________________________________________________________________"
+ "_____________________________________________________________________";
+const char *graph_dotted_line =
+ "---------------------------------------------------------------------"
+ "---------------------------------------------------------------------"
+ "---------------------------------------------------------------------";
diff --git a/smartt-perf/util/debug.c b/smartt-perf/util/debug.c
new file mode 100644
index 0000000..01bbe8e
--- /dev/null
+++ b/smartt-perf/util/debug.c
@@ -0,0 +1,94 @@
+/* For general debugging purposes */
+
+#include "../perf.h"
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "cache.h"
+#include "color.h"
+#include "event.h"
+#include "debug.h"
+#include "util.h"
+
+int verbose;
+bool dump_trace = false, quiet = false;
+
+int eprintf(int level, const char *fmt, ...)
+{
+ va_list args;
+ int ret = 0;
+
+ if (verbose >= level) {
+ va_start(args, fmt);
+ if (use_browser > 0)
+ ret = ui_helpline__show_help(fmt, args);
+ else
+ ret = vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+
+ return ret;
+}
+
+int dump_printf(const char *fmt, ...)
+{
+ va_list args;
+ int ret = 0;
+
+ if (dump_trace) {
+ va_start(args, fmt);
+ ret = vprintf(fmt, args);
+ va_end(args);
+ }
+
+ return ret;
+}
+
+#ifdef NO_NEWT_SUPPORT
+void ui__warning(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+#endif
+
+void trace_event(event_t *event)
+{
+ unsigned char *raw_event = (void *)event;
+ const char *color = PERF_COLOR_BLUE;
+ int i, j;
+
+ if (!dump_trace)
+ return;
+
+ printf(".");
+ color_fprintf(stdout, color, "\n. ... raw event: size %d bytes\n",
+ event->header.size);
+
+ for (i = 0; i < event->header.size; i++) {
+ if ((i & 15) == 0) {
+ printf(".");
+ color_fprintf(stdout, color, " %04x: ", i);
+ }
+
+ color_fprintf(stdout, color, " %02x", raw_event[i]);
+
+ if (((i & 15) == 15) || i == event->header.size-1) {
+ color_fprintf(stdout, color, " ");
+ for (j = 0; j < 15-(i & 15); j++)
+ color_fprintf(stdout, color, " ");
+ for (j = i & ~15; j <= i; j++) {
+ color_fprintf(stdout, color, "%c",
+ isprint(raw_event[j]) ?
+ raw_event[j] : '.');
+ }
+ color_fprintf(stdout, color, "\n");
+ }
+ }
+ printf(".\n");
+}
diff --git a/smartt-perf/util/debug.h b/smartt-perf/util/debug.h
new file mode 100644
index 0000000..ca35fd6
--- /dev/null
+++ b/smartt-perf/util/debug.h
@@ -0,0 +1,40 @@
+/* For debugging general purposes */
+#ifndef __PERF_DEBUG_H
+#define __PERF_DEBUG_H
+
+#include <stdbool.h>
+#include "event.h"
+
+extern int verbose;
+extern bool quiet, dump_trace;
+
+int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void trace_event(event_t *event);
+
+struct ui_progress;
+
+#ifdef NO_NEWT_SUPPORT
+static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
+{
+ return 0;
+}
+
+static inline struct ui_progress *ui_progress__new(const char *title __used,
+ u64 total __used)
+{
+ return (struct ui_progress *)1;
+}
+
+static inline void ui_progress__update(struct ui_progress *self __used,
+ u64 curr __used) {}
+
+static inline void ui_progress__delete(struct ui_progress *self __used) {}
+#else
+extern char ui_helpline__last_msg[];
+int ui_helpline__show_help(const char *format, va_list ap);
+#include "ui/progress.h"
+#endif
+
+void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+#endif /* __PERF_DEBUG_H */
diff --git a/smartt-perf/util/debugfs.c b/smartt-perf/util/debugfs.c
new file mode 100644
index 0000000..a88fefc
--- /dev/null
+++ b/smartt-perf/util/debugfs.c
@@ -0,0 +1,240 @@
+#include "util.h"
+#include "debugfs.h"
+#include "cache.h"
+
+static int debugfs_premounted;
+static char debugfs_mountpoint[MAX_PATH+1];
+
+static const char *debugfs_known_mountpoints[] = {
+ "/sys/kernel/debug/",
+ "/debug/",
+ 0,
+};
+
+/* use this to force a umount */
+void debugfs_force_cleanup(void)
+{
+ debugfs_find_mountpoint();
+ debugfs_premounted = 0;
+ debugfs_umount();
+}
+
+/* construct a full path to a debugfs element */
+int debugfs_make_path(const char *element, char *buffer, int size)
+{
+ int len;
+
+ if (strlen(debugfs_mountpoint) == 0) {
+ buffer[0] = '\0';
+ return -1;
+ }
+
+ len = strlen(debugfs_mountpoint) + strlen(element) + 1;
+ if (len >= size)
+ return len+1;
+
+ snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element);
+ return 0;
+}
+
+static int debugfs_found;
+
+/* find the path to the mounted debugfs */
+const char *debugfs_find_mountpoint(void)
+{
+ const char **ptr;
+ char type[100];
+ FILE *fp;
+
+ if (debugfs_found)
+ return (const char *) debugfs_mountpoint;
+
+ ptr = debugfs_known_mountpoints;
+ while (*ptr) {
+ if (debugfs_valid_mountpoint(*ptr) == 0) {
+ debugfs_found = 1;
+ strcpy(debugfs_mountpoint, *ptr);
+ return debugfs_mountpoint;
+ }
+ ptr++;
+ }
+
+ /* give up and parse /proc/mounts */
+ fp = fopen("/proc/mounts", "r");
+ if (fp == NULL)
+ die("Can't open /proc/mounts for read");
+
+ while (fscanf(fp, "%*s %"
+ STR(MAX_PATH)
+ "s %99s %*s %*d %*d\n",
+ debugfs_mountpoint, type) == 2) {
+ if (strcmp(type, "debugfs") == 0)
+ break;
+ }
+ fclose(fp);
+
+ if (strcmp(type, "debugfs") != 0)
+ return NULL;
+
+ debugfs_found = 1;
+
+ return debugfs_mountpoint;
+}
+
+/* verify that a mountpoint is actually a debugfs instance */
+
+int debugfs_valid_mountpoint(const char *debugfs)
+{
+ struct statfs st_fs;
+
+ if (statfs(debugfs, &st_fs) < 0)
+ return -ENOENT;
+ else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
+ return -ENOENT;
+
+ return 0;
+}
+
+
+int debugfs_valid_entry(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st))
+ return -errno;
+
+ return 0;
+}
+
+/* mount the debugfs somewhere if it's not mounted */
+
+char *debugfs_mount(const char *mountpoint)
+{
+ /* see if it's already mounted */
+ if (debugfs_find_mountpoint()) {
+ debugfs_premounted = 1;
+ return debugfs_mountpoint;
+ }
+
+ /* if not mounted and no argument */
+ if (mountpoint == NULL) {
+ /* see if environment variable set */
+ mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT);
+ /* if no environment variable, use default */
+ if (mountpoint == NULL)
+ mountpoint = "/sys/kernel/debug";
+ }
+
+ if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0)
+ return NULL;
+
+ /* save the mountpoint */
+ strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
+ debugfs_found = 1;
+
+ return debugfs_mountpoint;
+}
+
+/* umount the debugfs */
+
+int debugfs_umount(void)
+{
+ char umountcmd[128];
+ int ret;
+
+ /* if it was already mounted, leave it */
+ if (debugfs_premounted)
+ return 0;
+
+ /* make sure it's a valid mount point */
+ ret = debugfs_valid_mountpoint(debugfs_mountpoint);
+ if (ret)
+ return ret;
+
+ snprintf(umountcmd, sizeof(umountcmd),
+ "/bin/umount %s", debugfs_mountpoint);
+ return system(umountcmd);
+}
+
+int debugfs_write(const char *entry, const char *value)
+{
+ char path[MAX_PATH+1];
+ int ret, count;
+ int fd;
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ ret = debugfs_valid_entry(path);
+ if (ret)
+ return ret;
+
+ /* get how many chars we're going to write */
+ count = strlen(value);
+
+ /* open the debugfs entry */
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -errno;
+
+ while (count > 0) {
+ /* write it */
+ ret = write(fd, value, count);
+ if (ret <= 0) {
+ if (ret == EAGAIN)
+ continue;
+ close(fd);
+ return -errno;
+ }
+ count -= ret;
+ }
+
+ /* close it */
+ close(fd);
+
+ /* return success */
+ return 0;
+}
+
+/*
+ * read a debugfs entry
+ * returns the number of chars read or a negative errno
+ */
+int debugfs_read(const char *entry, char *buffer, size_t size)
+{
+ char path[MAX_PATH+1];
+ int ret;
+ int fd;
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ ret = debugfs_valid_entry(path);
+ if (ret)
+ return ret;
+
+ /* open the debugfs entry */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ do {
+ /* read it */
+ ret = read(fd, buffer, size);
+ if (ret == 0) {
+ close(fd);
+ return EOF;
+ }
+ } while (ret < 0 && errno == EAGAIN);
+
+ /* close it */
+ close(fd);
+
+ /* make *sure* there's a null character at the end */
+ buffer[ret] = '\0';
+
+ /* return the number of chars read */
+ return ret;
+}
diff --git a/smartt-perf/util/debugfs.h b/smartt-perf/util/debugfs.h
new file mode 100644
index 0000000..83a0287
--- /dev/null
+++ b/smartt-perf/util/debugfs.h
@@ -0,0 +1,25 @@
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+
+#include <sys/mount.h>
+
+#ifndef MAX_PATH
+# define MAX_PATH 256
+#endif
+
+#ifndef STR
+# define _STR(x) #x
+# define STR(x) _STR(x)
+#endif
+
+extern const char *debugfs_find_mountpoint(void);
+extern int debugfs_valid_mountpoint(const char *debugfs);
+extern int debugfs_valid_entry(const char *path);
+extern char *debugfs_mount(const char *mountpoint);
+extern int debugfs_umount(void);
+extern int debugfs_write(const char *entry, const char *value);
+extern int debugfs_read(const char *entry, char *buffer, size_t size);
+extern void debugfs_force_cleanup(void);
+extern int debugfs_make_path(const char *element, char *buffer, int size);
+
+#endif /* __DEBUGFS_H__ */
diff --git a/smartt-perf/util/environment.c b/smartt-perf/util/environment.c
new file mode 100644
index 0000000..275b0ee
--- /dev/null
+++ b/smartt-perf/util/environment.c
@@ -0,0 +1,9 @@
+/*
+ * We put all the perf config variables in this same object
+ * file, so that programs can link against the config parser
+ * without having to link against all the rest of perf.
+ */
+#include "cache.h"
+
+const char *pager_program;
+int pager_use_color = 1;
diff --git a/smartt-perf/util/event.c b/smartt-perf/util/event.c
new file mode 100644
index 0000000..50d0a93
--- /dev/null
+++ b/smartt-perf/util/event.c
@@ -0,0 +1,961 @@
+#include <linux/types.h>
+#include "event.h"
+#include "debug.h"
+#include "session.h"
+#include "sort.h"
+#include "string.h"
+#include "strlist.h"
+#include "thread.h"
+
+static const char *event__name[] = {
+ [0] = "TOTAL",
+ [PERF_RECORD_MMAP] = "MMAP",
+ [PERF_RECORD_LOST] = "LOST",
+ [PERF_RECORD_COMM] = "COMM",
+ [PERF_RECORD_EXIT] = "EXIT",
+ [PERF_RECORD_THROTTLE] = "THROTTLE",
+ [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
+ [PERF_RECORD_FORK] = "FORK",
+ [PERF_RECORD_READ] = "READ",
+ [PERF_RECORD_SAMPLE] = "SAMPLE",
+ [PERF_RECORD_HEADER_ATTR] = "ATTR",
+ [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
+ [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
+ [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
+ [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND",
+};
+
+const char *event__get_event_name(unsigned int id)
+{
+ if (id >= ARRAY_SIZE(event__name))
+ return "INVALID";
+ if (!event__name[id])
+ return "UNKNOWN";
+ return event__name[id];
+}
+
+static struct sample_data synth_sample = {
+ .pid = -1,
+ .tid = -1,
+ .time = -1,
+ .stream_id = -1,
+ .cpu = -1,
+ .period = 1,
+};
+
+static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ char filename[PATH_MAX];
+ char bf[BUFSIZ];
+ FILE *fp;
+ size_t size = 0;
+ DIR *tasks;
+ struct dirent dirent, *next;
+ pid_t tgid = 0;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+out_race:
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return 0;
+ }
+
+ memset(&event->comm, 0, sizeof(event->comm));
+
+ while (!event->comm.comm[0] || !event->comm.pid) {
+ if (fgets(bf, sizeof(bf), fp) == NULL) {
+ pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
+ goto out;
+ }
+
+ if (memcmp(bf, "Name:", 5) == 0) {
+ char *name = bf + 5;
+ while (*name && isspace(*name))
+ ++name;
+ size = strlen(name) - 1;
+ memcpy(event->comm.comm, name, size++);
+ } else if (memcmp(bf, "Tgid:", 5) == 0) {
+ char *tgids = bf + 5;
+ while (*tgids && isspace(*tgids))
+ ++tgids;
+ tgid = event->comm.pid = atoi(tgids);
+ }
+ }
+
+ event->comm.header.type = PERF_RECORD_COMM;
+ size = ALIGN(size, sizeof(u64));
+ memset(event->comm.comm + size, 0, session->id_hdr_size);
+ event->comm.header.size = (sizeof(event->comm) -
+ (sizeof(event->comm.comm) - size) +
+ session->id_hdr_size);
+ if (!full) {
+ event->comm.tid = pid;
+
+ process(event, &synth_sample, session);
+ goto out;
+ }
+
+ snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
+
+ tasks = opendir(filename);
+ if (tasks == NULL)
+ goto out_race;
+
+ while (!readdir_r(tasks, &dirent, &next) && next) {
+ char *end;
+ pid = strtol(dirent.d_name, &end, 10);
+ if (*end)
+ continue;
+
+ event->comm.tid = pid;
+
+ process(event, &synth_sample, session);
+ }
+
+ closedir(tasks);
+out:
+ fclose(fp);
+
+ return tgid;
+}
+
+static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ char filename[PATH_MAX];
+ FILE *fp;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return -1;
+ }
+
+ event->header.type = PERF_RECORD_MMAP;
+ /*
+ * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
+ */
+ event->header.misc = PERF_RECORD_MISC_USER;
+
+ while (1) {
+ char bf[BUFSIZ], *pbf = bf;
+ int n;
+ size_t size;
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ break;
+
+ /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
+ n = hex2u64(pbf, &event->mmap.start);
+ if (n < 0)
+ continue;
+ pbf += n + 1;
+ n = hex2u64(pbf, &event->mmap.len);
+ if (n < 0)
+ continue;
+ pbf += n + 3;
+ if (*pbf == 'x') { /* vm_exec */
+ char *execname = strchr(bf, '/');
+
+ /* Catch VDSO */
+ if (execname == NULL)
+ execname = strstr(bf, "[vdso]");
+
+ if (execname == NULL)
+ continue;
+
+ pbf += 3;
+ n = hex2u64(pbf, &event->mmap.pgoff);
+
+ size = strlen(execname);
+ execname[size - 1] = '\0'; /* Remove \n */
+ memcpy(event->mmap.filename, execname, size);
+ size = ALIGN(size, sizeof(u64));
+ event->mmap.len -= event->mmap.start;
+ event->mmap.header.size = (sizeof(event->mmap) -
+ (sizeof(event->mmap.filename) - size));
+ memset(event->mmap.filename + size, 0, session->id_hdr_size);
+ event->mmap.header.size += session->id_hdr_size;
+ event->mmap.pid = tgid;
+ event->mmap.tid = pid;
+
+ process(event, &synth_sample, session);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+int event__synthesize_modules(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine)
+{
+ struct rb_node *nd;
+ struct map_groups *kmaps = &machine->kmaps;
+ event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size);
+
+ if (event == NULL) {
+ pr_debug("Not enough memory synthesizing mmap event "
+ "for kernel modules\n");
+ return -1;
+ }
+
+ event->header.type = PERF_RECORD_MMAP;
+
+ /*
+ * kernel uses 0 for user space maps, see kernel/perf_event.c
+ * __perf_event_mmap
+ */
+ if (machine__is_host(machine))
+ event->header.misc = PERF_RECORD_MISC_KERNEL;
+ else
+ event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
+
+ for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]);
+ nd; nd = rb_next(nd)) {
+ size_t size;
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+
+ if (pos->dso->kernel)
+ continue;
+
+ size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
+ event->mmap.header.type = PERF_RECORD_MMAP;
+ event->mmap.header.size = (sizeof(event->mmap) -
+ (sizeof(event->mmap.filename) - size));
+ memset(event->mmap.filename + size, 0, session->id_hdr_size);
+ event->mmap.header.size += session->id_hdr_size;
+ event->mmap.start = pos->start;
+ event->mmap.len = pos->end - pos->start;
+ event->mmap.pid = machine->pid;
+
+ memcpy(event->mmap.filename, pos->dso->long_name,
+ pos->dso->long_name_len + 1);
+ process(event, &synth_sample, session);
+ }
+
+ free(event);
+ return 0;
+}
+
+static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event,
+ pid_t pid, event__handler_t process,
+ struct perf_session *session)
+{
+ pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process,
+ session);
+ if (tgid == -1)
+ return -1;
+ return event__synthesize_mmap_events(mmap_event, pid, tgid,
+ process, session);
+}
+
+int event__synthesize_thread_map(struct thread_map *threads,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t *comm_event, *mmap_event;
+ int err = -1, thread;
+
+ comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size);
+ if (comm_event == NULL)
+ goto out;
+
+ mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size);
+ if (mmap_event == NULL)
+ goto out_free_comm;
+
+ err = 0;
+ for (thread = 0; thread < threads->nr; ++thread) {
+ if (__event__synthesize_thread(comm_event, mmap_event,
+ threads->map[thread],
+ process, session)) {
+ err = -1;
+ break;
+ }
+ }
+ free(mmap_event);
+out_free_comm:
+ free(comm_event);
+out:
+ return err;
+}
+
+int event__synthesize_threads(event__handler_t process,
+ struct perf_session *session)
+{
+ DIR *proc;
+ struct dirent dirent, *next;
+ event_t *comm_event, *mmap_event;
+ int err = -1;
+
+ comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size);
+ if (comm_event == NULL)
+ goto out;
+
+ mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size);
+ if (mmap_event == NULL)
+ goto out_free_comm;
+
+ proc = opendir("/proc");
+ if (proc == NULL)
+ goto out_free_mmap;
+
+ while (!readdir_r(proc, &dirent, &next) && next) {
+ char *end;
+ pid_t pid = strtol(dirent.d_name, &end, 10);
+
+ if (*end) /* only interested in proper numerical dirents */
+ continue;
+
+ __event__synthesize_thread(comm_event, mmap_event, pid,
+ process, session);
+ }
+
+ closedir(proc);
+ err = 0;
+out_free_mmap:
+ free(mmap_event);
+out_free_comm:
+ free(comm_event);
+out:
+ return err;
+}
+
+struct process_symbol_args {
+ const char *name;
+ u64 start;
+};
+
+static int find_symbol_cb(void *arg, const char *name, char type,
+ u64 start, u64 end __used)
+{
+ struct process_symbol_args *args = arg;
+
+ /*
+ * Must be a function or at least an alias, as in PARISC64, where "_text" is
+ * an 'A' to the same address as "_stext".
+ */
+ if (!(symbol_type__is_a(type, MAP__FUNCTION) ||
+ type == 'A') || strcmp(name, args->name))
+ return 0;
+
+ args->start = start;
+ return 1;
+}
+
+int event__synthesize_kernel_mmap(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine,
+ const char *symbol_name)
+{
+ size_t size;
+ const char *filename, *mmap_name;
+ char path[PATH_MAX];
+ char name_buff[PATH_MAX];
+ struct map *map;
+ int err;
+ /*
+ * We should get this from /sys/kernel/sections/.text, but till that is
+ * available use this, and after it is use this as a fallback for older
+ * kernels.
+ */
+ struct process_symbol_args args = { .name = symbol_name, };
+ event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size);
+
+ if (event == NULL) {
+ pr_debug("Not enough memory synthesizing mmap event "
+ "for kernel modules\n");
+ return -1;
+ }
+
+ mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff));
+ if (machine__is_host(machine)) {
+ /*
+ * kernel uses PERF_RECORD_MISC_USER for user space maps,
+ * see kernel/perf_event.c __perf_event_mmap
+ */
+ event->header.misc = PERF_RECORD_MISC_KERNEL;
+ filename = "/proc/kallsyms";
+ } else {
+ event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
+ if (machine__is_default_guest(machine))
+ filename = (char *) symbol_conf.default_guest_kallsyms;
+ else {
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
+ filename = path;
+ }
+ }
+
+ if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0)
+ return -ENOENT;
+
+ map = machine->vmlinux_maps[MAP__FUNCTION];
+ size = snprintf(event->mmap.filename, sizeof(event->mmap.filename),
+ "%s%s", mmap_name, symbol_name) + 1;
+ size = ALIGN(size, sizeof(u64));
+ event->mmap.header.type = PERF_RECORD_MMAP;
+ event->mmap.header.size = (sizeof(event->mmap) -
+ (sizeof(event->mmap.filename) - size) + session->id_hdr_size);
+ event->mmap.pgoff = args.start;
+ event->mmap.start = map->start;
+ event->mmap.len = map->end - event->mmap.start;
+ event->mmap.pid = machine->pid;
+
+ err = process(event, &synth_sample, session);
+ free(event);
+
+ return err;
+}
+
+static void thread__comm_adjust(struct thread *self, struct hists *hists)
+{
+ char *comm = self->comm;
+
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ (!symbol_conf.comm_list ||
+ strlist__has_entry(symbol_conf.comm_list, comm))) {
+ u16 slen = strlen(comm);
+
+ if (hists__new_col_len(hists, HISTC_COMM, slen))
+ hists__set_col_len(hists, HISTC_THREAD, slen + 6);
+ }
+}
+
+static int thread__set_comm_adjust(struct thread *self, const char *comm,
+ struct hists *hists)
+{
+ int ret = thread__set_comm(self, comm);
+
+ if (ret)
+ return ret;
+
+ thread__comm_adjust(self, hists);
+
+ return 0;
+}
+
+int event__process_comm(event_t *self, struct sample_data *sample __used,
+ struct perf_session *session)
+{
+ struct thread *thread = perf_session__findnew(session, self->comm.tid);
+
+ dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid);
+
+ if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm,
+ &session->hists)) {
+ dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int event__process_lost(event_t *self, struct sample_data *sample __used,
+ struct perf_session *session)
+{
+ dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
+ self->lost.id, self->lost.lost);
+ session->hists.stats.total_lost += self->lost.lost;
+ return 0;
+}
+
+static void event_set_kernel_mmap_len(struct map **maps, event_t *self)
+{
+ maps[MAP__FUNCTION]->start = self->mmap.start;
+ maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len;
+ /*
+ * Be a bit paranoid here, some perf.data file came with
+ * a zero sized synthesized MMAP event for the kernel.
+ */
+ if (maps[MAP__FUNCTION]->end == 0)
+ maps[MAP__FUNCTION]->end = ~0ULL;
+}
+
+static int event__process_kernel_mmap(event_t *self,
+ struct perf_session *session)
+{
+ struct map *map;
+ char kmmap_prefix[PATH_MAX];
+ struct machine *machine;
+ enum dso_kernel_type kernel_type;
+ bool is_kernel_mmap;
+
+ machine = perf_session__findnew_machine(session, self->mmap.pid);
+ if (!machine) {
+ pr_err("Can't find id %d's machine\n", self->mmap.pid);
+ goto out_problem;
+ }
+
+ machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
+ if (machine__is_host(machine))
+ kernel_type = DSO_TYPE_KERNEL;
+ else
+ kernel_type = DSO_TYPE_GUEST_KERNEL;
+
+ is_kernel_mmap = memcmp(self->mmap.filename,
+ kmmap_prefix,
+ strlen(kmmap_prefix)) == 0;
+ if (self->mmap.filename[0] == '/' ||
+ (!is_kernel_mmap && self->mmap.filename[0] == '[')) {
+
+ char short_module_name[1024];
+ char *name, *dot;
+
+ if (self->mmap.filename[0] == '/') {
+ name = strrchr(self->mmap.filename, '/');
+ if (name == NULL)
+ goto out_problem;
+
+ ++name; /* skip / */
+ dot = strrchr(name, '.');
+ if (dot == NULL)
+ goto out_problem;
+ snprintf(short_module_name, sizeof(short_module_name),
+ "[%.*s]", (int)(dot - name), name);
+ strxfrchar(short_module_name, '-', '_');
+ } else
+ strcpy(short_module_name, self->mmap.filename);
+
+ map = machine__new_module(machine, self->mmap.start,
+ self->mmap.filename);
+ if (map == NULL)
+ goto out_problem;
+
+ name = strdup(short_module_name);
+ if (name == NULL)
+ goto out_problem;
+
+ map->dso->short_name = name;
+ map->dso->sname_alloc = 1;
+ map->end = map->start + self->mmap.len;
+ } else if (is_kernel_mmap) {
+ const char *symbol_name = (self->mmap.filename +
+ strlen(kmmap_prefix));
+ /*
+ * Should be there already, from the build-id table in
+ * the header.
+ */
+ struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
+ kmmap_prefix);
+ if (kernel == NULL)
+ goto out_problem;
+
+ kernel->kernel = kernel_type;
+ if (__machine__create_kernel_maps(machine, kernel) < 0)
+ goto out_problem;
+
+ event_set_kernel_mmap_len(machine->vmlinux_maps, self);
+ perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
+ symbol_name,
+ self->mmap.pgoff);
+ if (machine__is_default_guest(machine)) {
+ /*
+ * preload dso of guest kernel and modules
+ */
+ dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
+ NULL);
+ }
+ }
+ return 0;
+out_problem:
+ return -1;
+}
+
+int event__process_mmap(event_t *self, struct sample_data *sample __used,
+ struct perf_session *session)
+{
+ struct machine *machine;
+ struct thread *thread;
+ struct map *map;
+ u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ int ret = 0;
+
+ dump_printf(" %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n",
+ self->mmap.pid, self->mmap.tid, self->mmap.start,
+ self->mmap.len, self->mmap.pgoff, self->mmap.filename);
+
+ if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
+ cpumode == PERF_RECORD_MISC_KERNEL) {
+ ret = event__process_kernel_mmap(self, session);
+ if (ret < 0)
+ goto out_problem;
+ return 0;
+ }
+
+ machine = perf_session__find_host_machine(session);
+ if (machine == NULL)
+ goto out_problem;
+ thread = perf_session__findnew(session, self->mmap.pid);
+ if (thread == NULL)
+ goto out_problem;
+ map = map__new(&machine->user_dsos, self->mmap.start,
+ self->mmap.len, self->mmap.pgoff,
+ self->mmap.pid, self->mmap.filename,
+ MAP__FUNCTION);
+ if (map == NULL)
+ goto out_problem;
+
+ thread__insert_map(thread, map);
+ return 0;
+
+out_problem:
+ dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
+ return 0;
+}
+
+int event__process_task(event_t *self, struct sample_data *sample __used,
+ struct perf_session *session)
+{
+ struct thread *thread = perf_session__findnew(session, self->fork.tid);
+ struct thread *parent = perf_session__findnew(session, self->fork.ptid);
+
+ dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
+ self->fork.ppid, self->fork.ptid);
+
+ if (self->header.type == PERF_RECORD_EXIT) {
+ perf_session__remove_thread(session, thread);
+ return 0;
+ }
+
+ if (thread == NULL || parent == NULL ||
+ thread__fork(thread, parent) < 0) {
+ dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int event__process(event_t *event, struct sample_data *sample,
+ struct perf_session *session)
+{
+ switch (event->header.type) {
+ case PERF_RECORD_COMM:
+ event__process_comm(event, sample, session);
+ break;
+ case PERF_RECORD_MMAP:
+ event__process_mmap(event, sample, session);
+ break;
+ case PERF_RECORD_FORK:
+ case PERF_RECORD_EXIT:
+ event__process_task(event, sample, session);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void thread__find_addr_map(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al)
+{
+ struct map_groups *mg = &self->mg;
+ struct machine *machine = NULL;
+
+ al->thread = self;
+ al->addr = addr;
+ al->cpumode = cpumode;
+ al->filtered = false;
+
+ if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
+ al->level = 'k';
+ machine = perf_session__find_host_machine(session);
+ if (machine == NULL) {
+ al->map = NULL;
+ return;
+ }
+ mg = &machine->kmaps;
+ } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
+ al->level = '.';
+ machine = perf_session__find_host_machine(session);
+ } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
+ al->level = 'g';
+ machine = perf_session__find_machine(session, pid);
+ if (machine == NULL) {
+ al->map = NULL;
+ return;
+ }
+ mg = &machine->kmaps;
+ } else {
+ /*
+ * 'u' means guest os user space.
+ * TODO: We don't support guest user space. Might support late.
+ */
+ if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest)
+ al->level = 'u';
+ else
+ al->level = 'H';
+ al->map = NULL;
+
+ if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||
+ cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
+ !perf_guest)
+ al->filtered = true;
+ if ((cpumode == PERF_RECORD_MISC_USER ||
+ cpumode == PERF_RECORD_MISC_KERNEL) &&
+ !perf_host)
+ al->filtered = true;
+
+ return;
+ }
+try_again:
+ al->map = map_groups__find(mg, type, al->addr);
+ if (al->map == NULL) {
+ /*
+ * If this is outside of all known maps, and is a negative
+ * address, try to look it up in the kernel dso, as it might be
+ * a vsyscall or vdso (which executes in user-mode).
+ *
+ * XXX This is nasty, we should have a symbol list in the
+ * "[vdso]" dso, but for now lets use the old trick of looking
+ * in the whole kernel symbol list.
+ */
+ if ((long long)al->addr < 0 &&
+ cpumode == PERF_RECORD_MISC_KERNEL &&
+ machine && mg != &machine->kmaps) {
+ mg = &machine->kmaps;
+ goto try_again;
+ }
+ } else
+ al->addr = al->map->map_ip(al->map, al->addr);
+}
+
+void thread__find_addr_location(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al,
+ symbol_filter_t filter)
+{
+ thread__find_addr_map(self, session, cpumode, type, pid, addr, al);
+ if (al->map != NULL)
+ al->sym = map__find_symbol(al->map, al->addr, filter);
+ else
+ al->sym = NULL;
+}
+
+static void dso__calc_col_width(struct dso *self, struct hists *hists)
+{
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ (!symbol_conf.dso_list ||
+ strlist__has_entry(symbol_conf.dso_list, self->name))) {
+ u16 slen = dso__name_len(self);
+ hists__new_col_len(hists, HISTC_DSO, slen);
+ }
+
+ self->slen_calculated = 1;
+}
+
+int event__preprocess_sample(const event_t *self, struct perf_session *session,
+ struct addr_location *al, struct sample_data *data,
+ symbol_filter_t filter)
+{
+ u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ struct thread *thread = perf_session__findnew(session, self->ip.pid);
+
+ if (thread == NULL)
+ return -1;
+
+ if (symbol_conf.comm_list &&
+ !strlist__has_entry(symbol_conf.comm_list, thread->comm))
+ goto out_filtered;
+
+ dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+ /*
+ * Have we already created the kernel maps for the host machine?
+ *
+ * This should have happened earlier, when we processed the kernel MMAP
+ * events, but for older perf.data files there was no such thing, so do
+ * it now.
+ */
+ if (cpumode == PERF_RECORD_MISC_KERNEL &&
+ session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL)
+ machine__create_kernel_maps(&session->host_machine);
+
+ thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
+ self->ip.pid, self->ip.ip, al);
+ dump_printf(" ...... dso: %s\n",
+ al->map ? al->map->dso->long_name :
+ al->level == 'H' ? "[hypervisor]" : "<not found>");
+ al->sym = NULL;
+ al->cpu = data->cpu;
+
+ if (al->map) {
+ if (symbol_conf.dso_list &&
+ (!al->map || !al->map->dso ||
+ !(strlist__has_entry(symbol_conf.dso_list,
+ al->map->dso->short_name) ||
+ (al->map->dso->short_name != al->map->dso->long_name &&
+ strlist__has_entry(symbol_conf.dso_list,
+ al->map->dso->long_name)))))
+ goto out_filtered;
+ /*
+ * We have to do this here as we may have a dso with no symbol
+ * hit that has a name longer than the ones with symbols
+ * sampled.
+ */
+ if (!sort_dso.elide && !al->map->dso->slen_calculated)
+ dso__calc_col_width(al->map->dso, &session->hists);
+
+ al->sym = map__find_symbol(al->map, al->addr, filter);
+ } else {
+ const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
+
+ if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width &&
+ !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ !symbol_conf.dso_list)
+ hists__set_col_len(&session->hists, HISTC_DSO,
+ unresolved_col_width);
+ }
+
+ if (symbol_conf.sym_list && al->sym &&
+ !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
+ goto out_filtered;
+
+ return 0;
+
+out_filtered:
+ al->filtered = true;
+ return 0;
+}
+
+static int event__parse_id_sample(const event_t *event,
+ struct perf_session *session,
+ struct sample_data *sample)
+{
+ const u64 *array;
+ u64 type;
+
+ sample->cpu = sample->pid = sample->tid = -1;
+ sample->stream_id = sample->id = sample->time = -1ULL;
+
+ if (!session->sample_id_all)
+ return 0;
+
+ array = event->sample.array;
+ array += ((event->header.size -
+ sizeof(event->header)) / sizeof(u64)) - 1;
+ type = session->sample_type;
+
+ if (type & PERF_SAMPLE_CPU) {
+ u32 *p = (u32 *)array;
+ sample->cpu = *p;
+ array--;
+ }
+
+ if (type & PERF_SAMPLE_STREAM_ID) {
+ sample->stream_id = *array;
+ array--;
+ }
+
+ if (type & PERF_SAMPLE_ID) {
+ sample->id = *array;
+ array--;
+ }
+
+ if (type & PERF_SAMPLE_TIME) {
+ sample->time = *array;
+ array--;
+ }
+
+ if (type & PERF_SAMPLE_TID) {
+ u32 *p = (u32 *)array;
+ sample->pid = p[0];
+ sample->tid = p[1];
+ }
+
+ return 0;
+}
+
+int event__parse_sample(const event_t *event, struct perf_session *session,
+ struct sample_data *data)
+{
+ const u64 *array;
+ u64 type;
+
+ if (event->header.type != PERF_RECORD_SAMPLE)
+ return event__parse_id_sample(event, session, data);
+
+ array = event->sample.array;
+ type = session->sample_type;
+
+ if (type & PERF_SAMPLE_IP) {
+ data->ip = event->ip.ip;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_TID) {
+ u32 *p = (u32 *)array;
+ data->pid = p[0];
+ data->tid = p[1];
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_TIME) {
+ data->time = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_ADDR) {
+ data->addr = *array;
+ array++;
+ }
+
+ data->id = -1ULL;
+ if (type & PERF_SAMPLE_ID) {
+ data->id = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_STREAM_ID) {
+ data->stream_id = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_CPU) {
+ u32 *p = (u32 *)array;
+ data->cpu = *p;
+ array++;
+ } else
+ data->cpu = -1;
+
+ if (type & PERF_SAMPLE_PERIOD) {
+ data->period = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_READ) {
+ pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
+ return -1;
+ }
+
+ if (type & PERF_SAMPLE_CALLCHAIN) {
+ data->callchain = (struct ip_callchain *)array;
+ array += 1 + data->callchain->nr;
+ }
+
+ if (type & PERF_SAMPLE_RAW) {
+ u32 *p = (u32 *)array;
+ data->raw_size = *p;
+ p++;
+ data->raw_data = p;
+ }
+
+ return 0;
+}
diff --git a/smartt-perf/util/event.h b/smartt-perf/util/event.h
new file mode 100644
index 0000000..cc7b52f
--- /dev/null
+++ b/smartt-perf/util/event.h
@@ -0,0 +1,179 @@
+#ifndef __PERF_RECORD_H
+#define __PERF_RECORD_H
+
+#include <limits.h>
+
+#include "../perf.h"
+#include "map.h"
+
+/*
+ * PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
+ */
+struct ip_event {
+ struct perf_event_header header;
+ u64 ip;
+ u32 pid, tid;
+ unsigned char __more_data[];
+};
+
+struct mmap_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
+ char filename[PATH_MAX];
+};
+
+struct comm_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ char comm[16];
+};
+
+struct fork_event {
+ struct perf_event_header header;
+ u32 pid, ppid;
+ u32 tid, ptid;
+ u64 time;
+};
+
+struct lost_event {
+ struct perf_event_header header;
+ u64 id;
+ u64 lost;
+};
+
+/*
+ * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ */
+struct read_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 value;
+ u64 time_enabled;
+ u64 time_running;
+ u64 id;
+};
+
+struct sample_event {
+ struct perf_event_header header;
+ u64 array[];
+};
+
+struct sample_data {
+ u64 ip;
+ u32 pid, tid;
+ u64 time;
+ u64 addr;
+ u64 id;
+ u64 stream_id;
+ u64 period;
+ u32 cpu;
+ u32 raw_size;
+ void *raw_data;
+ struct ip_callchain *callchain;
+};
+
+#define BUILD_ID_SIZE 20
+
+struct build_id_event {
+ struct perf_event_header header;
+ pid_t pid;
+ u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+ char filename[];
+};
+
+enum perf_user_event_type { /* above any possible kernel type */
+ PERF_RECORD_USER_TYPE_START = 64,
+ PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_EVENT_TYPE = 65,
+ PERF_RECORD_HEADER_TRACING_DATA = 66,
+ PERF_RECORD_HEADER_BUILD_ID = 67,
+ PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_HEADER_MAX
+};
+
+struct attr_event {
+ struct perf_event_header header;
+ struct perf_event_attr attr;
+ u64 id[];
+};
+
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+ u64 event_id;
+ char name[MAX_EVENT_NAME];
+};
+
+struct event_type_event {
+ struct perf_event_header header;
+ struct perf_trace_event_type event_type;
+};
+
+struct tracing_data_event {
+ struct perf_event_header header;
+ u32 size;
+};
+
+typedef union event_union {
+ struct perf_event_header header;
+ struct ip_event ip;
+ struct mmap_event mmap;
+ struct comm_event comm;
+ struct fork_event fork;
+ struct lost_event lost;
+ struct read_event read;
+ struct sample_event sample;
+ struct attr_event attr;
+ struct event_type_event event_type;
+ struct tracing_data_event tracing_data;
+ struct build_id_event build_id;
+} event_t;
+
+void event__print_totals(void);
+
+struct perf_session;
+struct thread_map;
+
+typedef int (*event__handler_synth_t)(event_t *event,
+ struct perf_session *session);
+typedef int (*event__handler_t)(event_t *event, struct sample_data *sample,
+ struct perf_session *session);
+
+int event__synthesize_thread_map(struct thread_map *threads,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_threads(event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_kernel_mmap(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine,
+ const char *symbol_name);
+
+int event__synthesize_modules(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine);
+
+int event__process_comm(event_t *self, struct sample_data *sample,
+ struct perf_session *session);
+int event__process_lost(event_t *self, struct sample_data *sample,
+ struct perf_session *session);
+int event__process_mmap(event_t *self, struct sample_data *sample,
+ struct perf_session *session);
+int event__process_task(event_t *self, struct sample_data *sample,
+ struct perf_session *session);
+int event__process(event_t *event, struct sample_data *sample,
+ struct perf_session *session);
+
+struct addr_location;
+int event__preprocess_sample(const event_t *self, struct perf_session *session,
+ struct addr_location *al, struct sample_data *data,
+ symbol_filter_t filter);
+int event__parse_sample(const event_t *event, struct perf_session *session,
+ struct sample_data *sample);
+
+const char *event__get_event_name(unsigned int id);
+
+#endif /* __PERF_RECORD_H */
diff --git a/smartt-perf/util/evsel.c b/smartt-perf/util/evsel.c
new file mode 100644
index 0000000..d8575d3
--- /dev/null
+++ b/smartt-perf/util/evsel.c
@@ -0,0 +1,201 @@
+#include "evsel.h"
+#include "../perf.h"
+#include "util.h"
+#include "cpumap.h"
+#include "thread.h"
+
+#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
+
+struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
+{
+ struct perf_evsel *evsel = zalloc(sizeof(*evsel));
+
+ if (evsel != NULL) {
+ evsel->idx = idx;
+ evsel->attr = *attr;
+ INIT_LIST_HEAD(&evsel->node);
+ }
+
+ return evsel;
+}
+
+int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
+{
+ evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
+ return evsel->fd != NULL ? 0 : -ENOMEM;
+}
+
+int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
+{
+ evsel->counts = zalloc((sizeof(*evsel->counts) +
+ (ncpus * sizeof(struct perf_counts_values))));
+ return evsel->counts != NULL ? 0 : -ENOMEM;
+}
+
+void perf_evsel__free_fd(struct perf_evsel *evsel)
+{
+ xyarray__delete(evsel->fd);
+ evsel->fd = NULL;
+}
+
+void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
+{
+ int cpu, thread;
+
+ for (cpu = 0; cpu < ncpus; cpu++)
+ for (thread = 0; thread < nthreads; ++thread) {
+ close(FD(evsel, cpu, thread));
+ FD(evsel, cpu, thread) = -1;
+ }
+}
+
+void perf_evsel__delete(struct perf_evsel *evsel)
+{
+ assert(list_empty(&evsel->node));
+ xyarray__delete(evsel->fd);
+ free(evsel);
+}
+
+int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
+ int cpu, int thread, bool scale)
+{
+ struct perf_counts_values count;
+ size_t nv = scale ? 3 : 1;
+
+ if (FD(evsel, cpu, thread) < 0)
+ return -EINVAL;
+
+ if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0)
+ return -ENOMEM;
+
+ if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
+ return -errno;
+
+ if (scale) {
+ if (count.run == 0)
+ count.val = 0;
+ else if (count.run < count.ena)
+ count.val = (u64)((double)count.val * count.ena / count.run + 0.5);
+ } else
+ count.ena = count.run = 0;
+
+ evsel->counts->cpu[cpu] = count;
+ return 0;
+}
+
+int __perf_evsel__read(struct perf_evsel *evsel,
+ int ncpus, int nthreads, bool scale)
+{
+ size_t nv = scale ? 3 : 1;
+ int cpu, thread;
+ struct perf_counts_values *aggr = &evsel->counts->aggr, count;
+
+ aggr->val = aggr->ena = aggr->run = 0;
+
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ for (thread = 0; thread < nthreads; thread++) {
+ if (FD(evsel, cpu, thread) < 0)
+ continue;
+
+ if (readn(FD(evsel, cpu, thread),
+ &count, nv * sizeof(u64)) < 0)
+ return -errno;
+
+ aggr->val += count.val;
+ if (scale) {
+ aggr->ena += count.ena;
+ aggr->run += count.run;
+ }
+ }
+ }
+
+ evsel->counts->scaled = 0;
+ if (scale) {
+ if (aggr->run == 0) {
+ evsel->counts->scaled = -1;
+ aggr->val = 0;
+ return 0;
+ }
+
+ if (aggr->run < aggr->ena) {
+ evsel->counts->scaled = 1;
+ aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5);
+ }
+ } else
+ aggr->ena = aggr->run = 0;
+
+ return 0;
+}
+
+static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
+ struct thread_map *threads)
+{
+ int cpu, thread;
+
+ if (evsel->fd == NULL &&
+ perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
+ return -1;
+
+ for (cpu = 0; cpu < cpus->nr; cpu++) {
+ for (thread = 0; thread < threads->nr; thread++) {
+ FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
+ threads->map[thread],
+ cpus->map[cpu], -1, 0);
+ if (FD(evsel, cpu, thread) < 0)
+ goto out_close;
+ }
+ }
+
+ return 0;
+
+out_close:
+ do {
+ while (--thread >= 0) {
+ close(FD(evsel, cpu, thread));
+ FD(evsel, cpu, thread) = -1;
+ }
+ thread = threads->nr;
+ } while (--cpu >= 0);
+ return -1;
+}
+
+static struct {
+ struct cpu_map map;
+ int cpus[1];
+} empty_cpu_map = {
+ .map.nr = 1,
+ .cpus = { -1, },
+};
+
+static struct {
+ struct thread_map map;
+ int threads[1];
+} empty_thread_map = {
+ .map.nr = 1,
+ .threads = { -1, },
+};
+
+int perf_evsel__open(struct perf_evsel *evsel,
+ struct cpu_map *cpus, struct thread_map *threads)
+{
+
+ if (cpus == NULL) {
+ /* Work around old compiler warnings about strict aliasing */
+ cpus = &empty_cpu_map.map;
+ }
+
+ if (threads == NULL)
+ threads = &empty_thread_map.map;
+
+ return __perf_evsel__open(evsel, cpus, threads);
+}
+
+int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus)
+{
+ return __perf_evsel__open(evsel, cpus, &empty_thread_map.map);
+}
+
+int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads)
+{
+ return __perf_evsel__open(evsel, &empty_cpu_map.map, threads);
+}
diff --git a/smartt-perf/util/evsel.h b/smartt-perf/util/evsel.h
new file mode 100644
index 0000000..1693b7f
--- /dev/null
+++ b/smartt-perf/util/evsel.h
@@ -0,0 +1,115 @@
+#ifndef __PERF_EVSEL_H
+#define __PERF_EVSEL_H 1
+
+#include <linux/list.h>
+#include <stdbool.h>
+#include "linux/perf_event.h"
+#include "types.h"
+#include "xyarray.h"
+
+struct perf_counts_values {
+ union {
+ struct {
+ u64 val;
+ u64 ena;
+ u64 run;
+ };
+ u64 values[3];
+ };
+};
+
+struct perf_counts {
+ s8 scaled;
+ struct perf_counts_values aggr;
+ struct perf_counts_values cpu[];
+};
+
+struct perf_evsel {
+ struct list_head node;
+ struct perf_event_attr attr;
+ char *filter;
+ struct xyarray *fd;
+ struct perf_counts *counts;
+ int idx;
+ void *priv;
+};
+
+struct cpu_map;
+struct thread_map;
+
+struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx);
+void perf_evsel__delete(struct perf_evsel *evsel);
+
+int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
+int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
+void perf_evsel__free_fd(struct perf_evsel *evsel);
+void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
+
+int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus);
+int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads);
+int perf_evsel__open(struct perf_evsel *evsel,
+ struct cpu_map *cpus, struct thread_map *threads);
+
+#define perf_evsel__match(evsel, t, c) \
+ (evsel->attr.type == PERF_TYPE_##t && \
+ evsel->attr.config == PERF_COUNT_##c)
+
+int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
+ int cpu, int thread, bool scale);
+
+/**
+ * perf_evsel__read_on_cpu - Read out the results on a CPU and thread
+ *
+ * @evsel - event selector to read value
+ * @cpu - CPU of interest
+ * @thread - thread of interest
+ */
+static inline int perf_evsel__read_on_cpu(struct perf_evsel *evsel,
+ int cpu, int thread)
+{
+ return __perf_evsel__read_on_cpu(evsel, cpu, thread, false);
+}
+
+/**
+ * perf_evsel__read_on_cpu_scaled - Read out the results on a CPU and thread, scaled
+ *
+ * @evsel - event selector to read value
+ * @cpu - CPU of interest
+ * @thread - thread of interest
+ */
+static inline int perf_evsel__read_on_cpu_scaled(struct perf_evsel *evsel,
+ int cpu, int thread)
+{
+ return __perf_evsel__read_on_cpu(evsel, cpu, thread, true);
+}
+
+int __perf_evsel__read(struct perf_evsel *evsel, int ncpus, int nthreads,
+ bool scale);
+
+/**
+ * perf_evsel__read - Read the aggregate results on all CPUs
+ *
+ * @evsel - event selector to read value
+ * @ncpus - Number of cpus affected, from zero
+ * @nthreads - Number of threads affected, from zero
+ */
+static inline int perf_evsel__read(struct perf_evsel *evsel,
+ int ncpus, int nthreads)
+{
+ return __perf_evsel__read(evsel, ncpus, nthreads, false);
+}
+
+/**
+ * perf_evsel__read_scaled - Read the aggregate results on all CPUs, scaled
+ *
+ * @evsel - event selector to read value
+ * @ncpus - Number of cpus affected, from zero
+ * @nthreads - Number of threads affected, from zero
+ */
+static inline int perf_evsel__read_scaled(struct perf_evsel *evsel,
+ int ncpus, int nthreads)
+{
+ return __perf_evsel__read(evsel, ncpus, nthreads, true);
+}
+
+#endif /* __PERF_EVSEL_H */
diff --git a/smartt-perf/util/exec_cmd.c b/smartt-perf/util/exec_cmd.c
new file mode 100644
index 0000000..67eeff5
--- /dev/null
+++ b/smartt-perf/util/exec_cmd.c
@@ -0,0 +1,167 @@
+#include "cache.h"
+#include "exec_cmd.h"
+#include "quote.h"
+
+#include <string.h>
+
+#define MAX_ARGS 32
+
+static const char *argv_exec_path;
+static const char *argv0_path;
+
+const char *system_path(const char *path)
+{
+#ifdef RUNTIME_PREFIX
+ static const char *prefix;
+#else
+ static const char *prefix = PREFIX;
+#endif
+ struct strbuf d = STRBUF_INIT;
+
+ if (is_absolute_path(path))
+ return path;
+
+#ifdef RUNTIME_PREFIX
+ assert(argv0_path);
+ assert(is_absolute_path(argv0_path));
+
+ if (!prefix &&
+ !(prefix = strip_path_suffix(argv0_path, PERF_EXEC_PATH)) &&
+ !(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
+ !(prefix = strip_path_suffix(argv0_path, "perf"))) {
+ prefix = PREFIX;
+ fprintf(stderr, "RUNTIME_PREFIX requested, "
+ "but prefix computation failed. "
+ "Using static fallback '%s'.\n", prefix);
+ }
+#endif
+
+ strbuf_addf(&d, "%s/%s", prefix, path);
+ path = strbuf_detach(&d, NULL);
+ return path;
+}
+
+const char *perf_extract_argv0_path(const char *argv0)
+{
+ const char *slash;
+
+ if (!argv0 || !*argv0)
+ return NULL;
+ slash = argv0 + strlen(argv0);
+
+ while (argv0 <= slash && !is_dir_sep(*slash))
+ slash--;
+
+ if (slash >= argv0) {
+ argv0_path = strndup(argv0, slash - argv0);
+ return argv0_path ? slash + 1 : NULL;
+ }
+
+ return argv0;
+}
+
+void perf_set_argv_exec_path(const char *exec_path)
+{
+ argv_exec_path = exec_path;
+ /*
+ * Propagate this setting to external programs.
+ */
+ setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
+}
+
+
+/* Returns the highest-priority, location to look for perf programs. */
+const char *perf_exec_path(void)
+{
+ const char *env;
+
+ if (argv_exec_path)
+ return argv_exec_path;
+
+ env = getenv(EXEC_PATH_ENVIRONMENT);
+ if (env && *env) {
+ return env;
+ }
+
+ return system_path(PERF_EXEC_PATH);
+}
+
+static void add_path(struct strbuf *out, const char *path)
+{
+ if (path && *path) {
+ if (is_absolute_path(path))
+ strbuf_addstr(out, path);
+ else
+ strbuf_addstr(out, make_nonrelative_path(path));
+
+ strbuf_addch(out, PATH_SEP);
+ }
+}
+
+void setup_path(void)
+{
+ const char *old_path = getenv("PATH");
+ struct strbuf new_path = STRBUF_INIT;
+
+ add_path(&new_path, perf_exec_path());
+ add_path(&new_path, argv0_path);
+
+ if (old_path)
+ strbuf_addstr(&new_path, old_path);
+ else
+ strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
+
+ setenv("PATH", new_path.buf, 1);
+
+ strbuf_release(&new_path);
+}
+
+static const char **prepare_perf_cmd(const char **argv)
+{
+ int argc;
+ const char **nargv;
+
+ for (argc = 0; argv[argc]; argc++)
+ ; /* just counting */
+ nargv = malloc(sizeof(*nargv) * (argc + 2));
+
+ nargv[0] = "perf";
+ for (argc = 0; argv[argc]; argc++)
+ nargv[argc + 1] = argv[argc];
+ nargv[argc + 1] = NULL;
+ return nargv;
+}
+
+int execv_perf_cmd(const char **argv) {
+ const char **nargv = prepare_perf_cmd(argv);
+
+ /* execvp() can only ever return if it fails */
+ execvp("perf", (char **)nargv);
+
+ free(nargv);
+ return -1;
+}
+
+
+int execl_perf_cmd(const char *cmd,...)
+{
+ int argc;
+ const char *argv[MAX_ARGS + 1];
+ const char *arg;
+ va_list param;
+
+ va_start(param, cmd);
+ argv[0] = cmd;
+ argc = 1;
+ while (argc < MAX_ARGS) {
+ arg = argv[argc++] = va_arg(param, char *);
+ if (!arg)
+ break;
+ }
+ va_end(param);
+ if (MAX_ARGS <= argc)
+ return error("too many args to run %s", cmd);
+
+ argv[argc] = NULL;
+ return execv_perf_cmd(argv);
+}
diff --git a/smartt-perf/util/exec_cmd.h b/smartt-perf/util/exec_cmd.h
new file mode 100644
index 0000000..bc4b915
--- /dev/null
+++ b/smartt-perf/util/exec_cmd.h
@@ -0,0 +1,12 @@
+#ifndef __PERF_EXEC_CMD_H
+#define __PERF_EXEC_CMD_H
+
+extern void perf_set_argv_exec_path(const char *exec_path);
+extern const char *perf_extract_argv0_path(const char *path);
+extern const char *perf_exec_path(void);
+extern void setup_path(void);
+extern int execv_perf_cmd(const char **argv); /* NULL terminated */
+extern int execl_perf_cmd(const char *cmd, ...);
+extern const char *system_path(const char *path);
+
+#endif /* __PERF_EXEC_CMD_H */
diff --git a/smartt-perf/util/generate-cmdlist.sh b/smartt-perf/util/generate-cmdlist.sh
new file mode 100755
index 0000000..f06f6fd
--- /dev/null
+++ b/smartt-perf/util/generate-cmdlist.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "/* Automatically generated by $0 */
+struct cmdname_help
+{
+ char name[16];
+ char help[80];
+};
+
+static struct cmdname_help common_cmds[] = {"
+
+sed -n -e 's/^perf-\([^ ]*\)[ ].* common.*/\1/p' command-list.txt |
+sort |
+while read cmd
+do
+ sed -n '
+ /^NAME/,/perf-'"$cmd"'/H
+ ${
+ x
+ s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
+ p
+ }' "Documentation/perf-$cmd.txt"
+done
+echo "};"
diff --git a/smartt-perf/util/header.c b/smartt-perf/util/header.c
new file mode 100644
index 0000000..0866bcd
--- /dev/null
+++ b/smartt-perf/util/header.c
@@ -0,0 +1,1237 @@
+#define _FILE_OFFSET_BITS 64
+
+#include <sys/types.h>
+#include <byteswap.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+
+#include "util.h"
+#include "header.h"
+#include "../perf.h"
+#include "trace-event.h"
+#include "session.h"
+#include "symbol.h"
+#include "debug.h"
+
+static bool no_buildid_cache = false;
+
+/*
+ * Create new perf.data header attribute:
+ */
+struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
+{
+ struct perf_header_attr *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ self->attr = *attr;
+ self->ids = 0;
+ self->size = 1;
+ self->id = malloc(sizeof(u64));
+ if (self->id == NULL) {
+ free(self);
+ self = NULL;
+ }
+ }
+
+ return self;
+}
+
+void perf_header_attr__delete(struct perf_header_attr *self)
+{
+ free(self->id);
+ free(self);
+}
+
+int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
+{
+ int pos = self->ids;
+
+ self->ids++;
+ if (self->ids > self->size) {
+ int nsize = self->size * 2;
+ u64 *nid = realloc(self->id, nsize * sizeof(u64));
+
+ if (nid == NULL)
+ return -1;
+
+ self->size = nsize;
+ self->id = nid;
+ }
+ self->id[pos] = id;
+ return 0;
+}
+
+int perf_header__init(struct perf_header *self)
+{
+ self->size = 1;
+ self->attr = malloc(sizeof(void *));
+ return self->attr == NULL ? -ENOMEM : 0;
+}
+
+void perf_header__exit(struct perf_header *self)
+{
+ int i;
+ for (i = 0; i < self->attrs; ++i)
+ perf_header_attr__delete(self->attr[i]);
+ free(self->attr);
+}
+
+int perf_header__add_attr(struct perf_header *self,
+ struct perf_header_attr *attr)
+{
+ if (self->frozen)
+ return -1;
+
+ if (self->attrs == self->size) {
+ int nsize = self->size * 2;
+ struct perf_header_attr **nattr;
+
+ nattr = realloc(self->attr, nsize * sizeof(void *));
+ if (nattr == NULL)
+ return -1;
+
+ self->size = nsize;
+ self->attr = nattr;
+ }
+
+ self->attr[self->attrs++] = attr;
+ return 0;
+}
+
+static int event_count;
+static struct perf_trace_event_type *events;
+
+int perf_header__push_event(u64 id, const char *name)
+{
+ if (strlen(name) > MAX_EVENT_NAME)
+ pr_warning("Event %s will be truncated\n", name);
+
+ if (!events) {
+ events = malloc(sizeof(struct perf_trace_event_type));
+ if (events == NULL)
+ return -ENOMEM;
+ } else {
+ struct perf_trace_event_type *nevents;
+
+ nevents = realloc(events, (event_count + 1) * sizeof(*events));
+ if (nevents == NULL)
+ return -ENOMEM;
+ events = nevents;
+ }
+ memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
+ events[event_count].event_id = id;
+ strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
+ event_count++;
+ return 0;
+}
+
+char *perf_header__find_event(u64 id)
+{
+ int i;
+ for (i = 0 ; i < event_count; i++) {
+ if (events[i].event_id == id)
+ return events[i].name;
+ }
+ return NULL;
+}
+
+static const char *__perf_magic = "PERFFILE";
+
+#define PERF_MAGIC (*(u64 *)__perf_magic)
+
+struct perf_file_attr {
+ struct perf_event_attr attr;
+ struct perf_file_section ids;
+};
+
+void perf_header__set_feat(struct perf_header *self, int feat)
+{
+ set_bit(feat, self->adds_features);
+}
+
+void perf_header__clear_feat(struct perf_header *self, int feat)
+{
+ clear_bit(feat, self->adds_features);
+}
+
+bool perf_header__has_feat(const struct perf_header *self, int feat)
+{
+ return test_bit(feat, self->adds_features);
+}
+
+static int do_write(int fd, const void *buf, size_t size)
+{
+ while (size) {
+ int ret = write(fd, buf, size);
+
+ if (ret < 0)
+ return -errno;
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return 0;
+}
+
+#define NAME_ALIGN 64
+
+static int write_padded(int fd, const void *bf, size_t count,
+ size_t count_aligned)
+{
+ static const char zero_buf[NAME_ALIGN];
+ int err = do_write(fd, bf, count);
+
+ if (!err)
+ err = do_write(fd, zero_buf, count_aligned - count);
+
+ return err;
+}
+
+#define dsos__for_each_with_build_id(pos, head) \
+ list_for_each_entry(pos, head, node) \
+ if (!pos->has_build_id) \
+ continue; \
+ else
+
+static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
+ u16 misc, int fd)
+{
+ struct dso *pos;
+
+ dsos__for_each_with_build_id(pos, head) {
+ int err;
+ struct build_id_event b;
+ size_t len;
+
+ if (!pos->hit)
+ continue;
+ len = pos->long_name_len + 1;
+ len = ALIGN(len, NAME_ALIGN);
+ memset(&b, 0, sizeof(b));
+ memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
+ b.pid = pid;
+ b.header.misc = misc;
+ b.header.size = sizeof(b) + len;
+ err = do_write(fd, &b, sizeof(b));
+ if (err < 0)
+ return err;
+ err = write_padded(fd, pos->long_name,
+ pos->long_name_len + 1, len);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int machine__write_buildid_table(struct machine *self, int fd)
+{
+ int err;
+ u16 kmisc = PERF_RECORD_MISC_KERNEL,
+ umisc = PERF_RECORD_MISC_USER;
+
+ if (!machine__is_host(self)) {
+ kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
+ umisc = PERF_RECORD_MISC_GUEST_USER;
+ }
+
+ err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid,
+ kmisc, fd);
+ if (err == 0)
+ err = __dsos__write_buildid_table(&self->user_dsos,
+ self->pid, umisc, fd);
+ return err;
+}
+
+static int dsos__write_buildid_table(struct perf_header *header, int fd)
+{
+ struct perf_session *session = container_of(header,
+ struct perf_session, header);
+ struct rb_node *nd;
+ int err = machine__write_buildid_table(&session->host_machine, fd);
+
+ if (err)
+ return err;
+
+ for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ err = machine__write_buildid_table(pos, fd);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+ const char *name, bool is_kallsyms)
+{
+ const size_t size = PATH_MAX;
+ char *realname, *filename = malloc(size),
+ *linkname = malloc(size), *targetname;
+ int len, err = -1;
+
+ if (is_kallsyms)
+ realname = (char *)name;
+ else
+ realname = realpath(name, NULL);
+
+ if (realname == NULL || filename == NULL || linkname == NULL)
+ goto out_free;
+
+ len = snprintf(filename, size, "%s%s%s",
+ debugdir, is_kallsyms ? "/" : "", realname);
+ if (mkdir_p(filename, 0755))
+ goto out_free;
+
+ snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id);
+
+ if (access(filename, F_OK)) {
+ if (is_kallsyms) {
+ if (copyfile("/proc/kallsyms", filename))
+ goto out_free;
+ } else if (link(realname, filename) && copyfile(name, filename))
+ goto out_free;
+ }
+
+ len = snprintf(linkname, size, "%s/.build-id/%.2s",
+ debugdir, sbuild_id);
+
+ if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
+ goto out_free;
+
+ snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
+ targetname = filename + strlen(debugdir) - 5;
+ memcpy(targetname, "../..", 5);
+
+ if (symlink(targetname, linkname) == 0)
+ err = 0;
+out_free:
+ if (!is_kallsyms)
+ free(realname);
+ free(filename);
+ free(linkname);
+ return err;
+}
+
+static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
+ const char *name, const char *debugdir,
+ bool is_kallsyms)
+{
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(build_id, build_id_size, sbuild_id);
+
+ return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
+}
+
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
+{
+ const size_t size = PATH_MAX;
+ char *filename = malloc(size),
+ *linkname = malloc(size);
+ int err = -1;
+
+ if (filename == NULL || linkname == NULL)
+ goto out_free;
+
+ snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+ debugdir, sbuild_id, sbuild_id + 2);
+
+ if (access(linkname, F_OK))
+ goto out_free;
+
+ if (readlink(linkname, filename, size) < 0)
+ goto out_free;
+
+ if (unlink(linkname))
+ goto out_free;
+
+ /*
+ * Since the link is relative, we must make it absolute:
+ */
+ snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+ debugdir, sbuild_id, filename);
+
+ if (unlink(linkname))
+ goto out_free;
+
+ err = 0;
+out_free:
+ free(filename);
+ free(linkname);
+ return err;
+}
+
+static int dso__cache_build_id(struct dso *self, const char *debugdir)
+{
+ bool is_kallsyms = self->kernel && self->long_name[0] != '/';
+
+ return build_id_cache__add_b(self->build_id, sizeof(self->build_id),
+ self->long_name, debugdir, is_kallsyms);
+}
+
+static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
+{
+ struct dso *pos;
+ int err = 0;
+
+ dsos__for_each_with_build_id(pos, head)
+ if (dso__cache_build_id(pos, debugdir))
+ err = -1;
+
+ return err;
+}
+
+static int machine__cache_build_ids(struct machine *self, const char *debugdir)
+{
+ int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir);
+ ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir);
+ return ret;
+}
+
+static int perf_session__cache_build_ids(struct perf_session *self)
+{
+ struct rb_node *nd;
+ int ret;
+ char debugdir[PATH_MAX];
+
+ snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
+
+ if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
+ return -1;
+
+ ret = machine__cache_build_ids(&self->host_machine, debugdir);
+
+ for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= machine__cache_build_ids(pos, debugdir);
+ }
+ return ret ? -1 : 0;
+}
+
+static bool machine__read_build_ids(struct machine *self, bool with_hits)
+{
+ bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits);
+ ret |= __dsos__read_build_ids(&self->user_dsos, with_hits);
+ return ret;
+}
+
+static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits)
+{
+ struct rb_node *nd;
+ bool ret = machine__read_build_ids(&self->host_machine, with_hits);
+
+ for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= machine__read_build_ids(pos, with_hits);
+ }
+
+ return ret;
+}
+
+static int perf_header__adds_write(struct perf_header *self, int fd)
+{
+ int nr_sections;
+ struct perf_session *session;
+ struct perf_file_section *feat_sec;
+ int sec_size;
+ u64 sec_start;
+ int idx = 0, err;
+
+ session = container_of(self, struct perf_session, header);
+
+ if (perf_header__has_feat(self, HEADER_BUILD_ID &&
+ !perf_session__read_build_ids(session, true)))
+ perf_header__clear_feat(self, HEADER_BUILD_ID);
+
+ nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
+ if (!nr_sections)
+ return 0;
+
+ feat_sec = calloc(sizeof(*feat_sec), nr_sections);
+ if (feat_sec == NULL)
+ return -ENOMEM;
+
+ sec_size = sizeof(*feat_sec) * nr_sections;
+
+ sec_start = self->data_offset + self->data_size;
+ lseek(fd, sec_start + sec_size, SEEK_SET);
+
+ if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
+ struct perf_file_section *trace_sec;
+
+ trace_sec = &feat_sec[idx++];
+
+ /* Write trace info */
+ trace_sec->offset = lseek(fd, 0, SEEK_CUR);
+ read_tracing_data(fd, &evsel_list);
+ trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
+ }
+
+ if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
+ struct perf_file_section *buildid_sec;
+
+ buildid_sec = &feat_sec[idx++];
+
+ /* Write build-ids */
+ buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
+ err = dsos__write_buildid_table(self, fd);
+ if (err < 0) {
+ pr_debug("failed to write buildid table\n");
+ goto out_free;
+ }
+ buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
+ buildid_sec->offset;
+ if (!no_buildid_cache)
+ perf_session__cache_build_ids(session);
+ }
+
+ lseek(fd, sec_start, SEEK_SET);
+ err = do_write(fd, feat_sec, sec_size);
+ if (err < 0)
+ pr_debug("failed to write feature section\n");
+out_free:
+ free(feat_sec);
+ return err;
+}
+
+int perf_header__write_pipe(int fd)
+{
+ struct perf_pipe_file_header f_header;
+ int err;
+
+ f_header = (struct perf_pipe_file_header){
+ .magic = PERF_MAGIC,
+ .size = sizeof(f_header),
+ };
+
+ err = do_write(fd, &f_header, sizeof(f_header));
+ if (err < 0) {
+ pr_debug("failed to write perf pipe header\n");
+ return err;
+ }
+
+ return 0;
+}
+
+int perf_header__write(struct perf_header *self, int fd, bool at_exit)
+{
+ struct perf_file_header f_header;
+ struct perf_file_attr f_attr;
+ struct perf_header_attr *attr;
+ int i, err;
+
+ lseek(fd, sizeof(f_header), SEEK_SET);
+
+ for (i = 0; i < self->attrs; i++) {
+ attr = self->attr[i];
+
+ attr->id_offset = lseek(fd, 0, SEEK_CUR);
+ err = do_write(fd, attr->id, attr->ids * sizeof(u64));
+ if (err < 0) {
+ pr_debug("failed to write perf header\n");
+ return err;
+ }
+ }
+
+
+ self->attr_offset = lseek(fd, 0, SEEK_CUR);
+
+ for (i = 0; i < self->attrs; i++) {
+ attr = self->attr[i];
+
+ f_attr = (struct perf_file_attr){
+ .attr = attr->attr,
+ .ids = {
+ .offset = attr->id_offset,
+ .size = attr->ids * sizeof(u64),
+ }
+ };
+ err = do_write(fd, &f_attr, sizeof(f_attr));
+ if (err < 0) {
+ pr_debug("failed to write perf header attribute\n");
+ return err;
+ }
+ }
+
+ self->event_offset = lseek(fd, 0, SEEK_CUR);
+ self->event_size = event_count * sizeof(struct perf_trace_event_type);
+ if (events) {
+ err = do_write(fd, events, self->event_size);
+ if (err < 0) {
+ pr_debug("failed to write perf header events\n");
+ return err;
+ }
+ }
+
+ self->data_offset = lseek(fd, 0, SEEK_CUR);
+
+ if (at_exit) {
+ err = perf_header__adds_write(self, fd);
+ if (err < 0)
+ return err;
+ }
+
+ f_header = (struct perf_file_header){
+ .magic = PERF_MAGIC,
+ .size = sizeof(f_header),
+ .attr_size = sizeof(f_attr),
+ .attrs = {
+ .offset = self->attr_offset,
+ .size = self->attrs * sizeof(f_attr),
+ },
+ .data = {
+ .offset = self->data_offset,
+ .size = self->data_size,
+ },
+ .event_types = {
+ .offset = self->event_offset,
+ .size = self->event_size,
+ },
+ };
+
+ memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
+
+ lseek(fd, 0, SEEK_SET);
+ err = do_write(fd, &f_header, sizeof(f_header));
+ if (err < 0) {
+ pr_debug("failed to write perf header\n");
+ return err;
+ }
+ lseek(fd, self->data_offset + self->data_size, SEEK_SET);
+
+ self->frozen = 1;
+ return 0;
+}
+
+static int perf_header__getbuffer64(struct perf_header *self,
+ int fd, void *buf, size_t size)
+{
+ if (readn(fd, buf, size) <= 0)
+ return -1;
+
+ if (self->needs_swap)
+ mem_bswap_64(buf, size);
+
+ return 0;
+}
+
+int perf_header__process_sections(struct perf_header *self, int fd,
+ int (*process)(struct perf_file_section *self,
+ struct perf_header *ph,
+ int feat, int fd))
+{
+ struct perf_file_section *feat_sec;
+ int nr_sections;
+ int sec_size;
+ int idx = 0;
+ int err = -1, feat = 1;
+
+ nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
+ if (!nr_sections)
+ return 0;
+
+ feat_sec = calloc(sizeof(*feat_sec), nr_sections);
+ if (!feat_sec)
+ return -1;
+
+ sec_size = sizeof(*feat_sec) * nr_sections;
+
+ lseek(fd, self->data_offset + self->data_size, SEEK_SET);
+
+ if (perf_header__getbuffer64(self, fd, feat_sec, sec_size))
+ goto out_free;
+
+ err = 0;
+ while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
+ if (perf_header__has_feat(self, feat)) {
+ struct perf_file_section *sec = &feat_sec[idx++];
+
+ err = process(sec, self, feat, fd);
+ if (err < 0)
+ break;
+ }
+ ++feat;
+ }
+out_free:
+ free(feat_sec);
+ return err;
+}
+
+int perf_file_header__read(struct perf_file_header *self,
+ struct perf_header *ph, int fd)
+{
+ lseek(fd, 0, SEEK_SET);
+
+ if (readn(fd, self, sizeof(*self)) <= 0 ||
+ memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
+ return -1;
+
+ if (self->attr_size != sizeof(struct perf_file_attr)) {
+ u64 attr_size = bswap_64(self->attr_size);
+
+ if (attr_size != sizeof(struct perf_file_attr))
+ return -1;
+
+ mem_bswap_64(self, offsetof(struct perf_file_header,
+ adds_features));
+ ph->needs_swap = true;
+ }
+
+ if (self->size != sizeof(*self)) {
+ /* Support the previous format */
+ if (self->size == offsetof(typeof(*self), adds_features))
+ bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
+ else
+ return -1;
+ }
+
+ memcpy(&ph->adds_features, &self->adds_features,
+ sizeof(ph->adds_features));
+ /*
+ * FIXME: hack that assumes that if we need swap the perf.data file
+ * may be coming from an arch with a different word-size, ergo different
+ * DEFINE_BITMAP format, investigate more later, but for now its mostly
+ * safe to assume that we have a build-id section. Trace files probably
+ * have several other issues in this realm anyway...
+ */
+ if (ph->needs_swap) {
+ memset(&ph->adds_features, 0, sizeof(ph->adds_features));
+ perf_header__set_feat(ph, HEADER_BUILD_ID);
+ }
+
+ ph->event_offset = self->event_types.offset;
+ ph->event_size = self->event_types.size;
+ ph->data_offset = self->data.offset;
+ ph->data_size = self->data.size;
+ return 0;
+}
+
+static int __event_process_build_id(struct build_id_event *bev,
+ char *filename,
+ struct perf_session *session)
+{
+ int err = -1;
+ struct list_head *head;
+ struct machine *machine;
+ u16 misc;
+ struct dso *dso;
+ enum dso_kernel_type dso_type;
+
+ machine = perf_session__findnew_machine(session, bev->pid);
+ if (!machine)
+ goto out;
+
+ misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+
+ switch (misc) {
+ case PERF_RECORD_MISC_KERNEL:
+ dso_type = DSO_TYPE_KERNEL;
+ head = &machine->kernel_dsos;
+ break;
+ case PERF_RECORD_MISC_GUEST_KERNEL:
+ dso_type = DSO_TYPE_GUEST_KERNEL;
+ head = &machine->kernel_dsos;
+ break;
+ case PERF_RECORD_MISC_USER:
+ case PERF_RECORD_MISC_GUEST_USER:
+ dso_type = DSO_TYPE_USER;
+ head = &machine->user_dsos;
+ break;
+ default:
+ goto out;
+ }
+
+ dso = __dsos__findnew(head, filename);
+ if (dso != NULL) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ dso__set_build_id(dso, &bev->build_id);
+
+ if (filename[0] == '[')
+ dso->kernel = dso_type;
+
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id),
+ sbuild_id);
+ pr_debug("build id event received for %s: %s\n",
+ dso->long_name, sbuild_id);
+ }
+
+ err = 0;
+out:
+ return err;
+}
+
+static int perf_header__read_build_ids(struct perf_header *self,
+ int input, u64 offset, u64 size)
+{
+ struct perf_session *session = container_of(self,
+ struct perf_session, header);
+ struct build_id_event bev;
+ char filename[PATH_MAX];
+ u64 limit = offset + size;
+ int err = -1;
+
+ while (offset < limit) {
+ ssize_t len;
+
+ if (read(input, &bev, sizeof(bev)) != sizeof(bev))
+ goto out;
+
+ if (self->needs_swap)
+ perf_event_header__bswap(&bev.header);
+
+ len = bev.header.size - sizeof(bev);
+ if (read(input, filename, len) != len)
+ goto out;
+
+ __event_process_build_id(&bev, filename, session);
+
+ offset += bev.header.size;
+ }
+ err = 0;
+out:
+ return err;
+}
+
+static int perf_file_section__process(struct perf_file_section *self,
+ struct perf_header *ph,
+ int feat, int fd)
+{
+ if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) {
+ pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
+ "%d, continuing...\n", self->offset, feat);
+ return 0;
+ }
+
+ switch (feat) {
+ case HEADER_TRACE_INFO:
+ trace_report(fd, false);
+ break;
+
+ case HEADER_BUILD_ID:
+ if (perf_header__read_build_ids(ph, fd, self->offset, self->size))
+ pr_debug("Failed to read buildids, continuing...\n");
+ break;
+ default:
+ pr_debug("unknown feature %d, continuing...\n", feat);
+ }
+
+ return 0;
+}
+
+static int perf_file_header__read_pipe(struct perf_pipe_file_header *self,
+ struct perf_header *ph, int fd,
+ bool repipe)
+{
+ if (readn(fd, self, sizeof(*self)) <= 0 ||
+ memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
+ return -1;
+
+ if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0)
+ return -1;
+
+ if (self->size != sizeof(*self)) {
+ u64 size = bswap_64(self->size);
+
+ if (size != sizeof(*self))
+ return -1;
+
+ ph->needs_swap = true;
+ }
+
+ return 0;
+}
+
+static int perf_header__read_pipe(struct perf_session *session, int fd)
+{
+ struct perf_header *self = &session->header;
+ struct perf_pipe_file_header f_header;
+
+ if (perf_file_header__read_pipe(&f_header, self, fd,
+ session->repipe) < 0) {
+ pr_debug("incompatible file format\n");
+ return -EINVAL;
+ }
+
+ session->fd = fd;
+
+ return 0;
+}
+
+int perf_header__read(struct perf_session *session, int fd)
+{
+ struct perf_header *self = &session->header;
+ struct perf_file_header f_header;
+ struct perf_file_attr f_attr;
+ u64 f_id;
+ int nr_attrs, nr_ids, i, j;
+
+ if (session->fd_pipe)
+ return perf_header__read_pipe(session, fd);
+
+ if (perf_file_header__read(&f_header, self, fd) < 0) {
+ pr_debug("incompatible file format\n");
+ return -EINVAL;
+ }
+
+ nr_attrs = f_header.attrs.size / sizeof(f_attr);
+ lseek(fd, f_header.attrs.offset, SEEK_SET);
+
+ for (i = 0; i < nr_attrs; i++) {
+ struct perf_header_attr *attr;
+ off_t tmp;
+
+ if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr)))
+ goto out_errno;
+
+ tmp = lseek(fd, 0, SEEK_CUR);
+
+ attr = perf_header_attr__new(&f_attr.attr);
+ if (attr == NULL)
+ return -ENOMEM;
+
+ nr_ids = f_attr.ids.size / sizeof(u64);
+ lseek(fd, f_attr.ids.offset, SEEK_SET);
+
+ for (j = 0; j < nr_ids; j++) {
+ if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id)))
+ goto out_errno;
+
+ if (perf_header_attr__add_id(attr, f_id) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+ }
+ if (perf_header__add_attr(self, attr) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+
+ lseek(fd, tmp, SEEK_SET);
+ }
+
+ if (f_header.event_types.size) {
+ lseek(fd, f_header.event_types.offset, SEEK_SET);
+ events = malloc(f_header.event_types.size);
+ if (events == NULL)
+ return -ENOMEM;
+ if (perf_header__getbuffer64(self, fd, events,
+ f_header.event_types.size))
+ goto out_errno;
+ event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
+ }
+
+ perf_header__process_sections(self, fd, perf_file_section__process);
+
+ lseek(fd, self->data_offset, SEEK_SET);
+
+ self->frozen = 1;
+ return 0;
+out_errno:
+ return -errno;
+}
+
+u64 perf_header__sample_type(struct perf_header *header)
+{
+ u64 type = 0;
+ int i;
+
+ for (i = 0; i < header->attrs; i++) {
+ struct perf_header_attr *attr = header->attr[i];
+
+ if (!type)
+ type = attr->attr.sample_type;
+ else if (type != attr->attr.sample_type)
+ die("non matching sample_type");
+ }
+
+ return type;
+}
+
+bool perf_header__sample_id_all(const struct perf_header *header)
+{
+ bool value = false, first = true;
+ int i;
+
+ for (i = 0; i < header->attrs; i++) {
+ struct perf_header_attr *attr = header->attr[i];
+
+ if (first) {
+ value = attr->attr.sample_id_all;
+ first = false;
+ } else if (value != attr->attr.sample_id_all)
+ die("non matching sample_id_all");
+ }
+
+ return value;
+}
+
+struct perf_event_attr *
+perf_header__find_attr(u64 id, struct perf_header *header)
+{
+ int i;
+
+ /*
+ * We set id to -1 if the data file doesn't contain sample
+ * ids. This can happen when the data file contains one type
+ * of event and in that case, the header can still store the
+ * event attribute information. Check for this and avoid
+ * walking through the entire list of ids which may be large.
+ */
+ if (id == -1ULL) {
+ if (header->attrs > 0)
+ return &header->attr[0]->attr;
+ return NULL;
+ }
+
+ for (i = 0; i < header->attrs; i++) {
+ struct perf_header_attr *attr = header->attr[i];
+ int j;
+
+ for (j = 0; j < attr->ids; j++) {
+ if (attr->id[j] == id)
+ return &attr->attr;
+ }
+ }
+
+ return NULL;
+}
+
+int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t *ev;
+ size_t size;
+ int err;
+
+ size = sizeof(struct perf_event_attr);
+ size = ALIGN(size, sizeof(u64));
+ size += sizeof(struct perf_event_header);
+ size += ids * sizeof(u64);
+
+ ev = malloc(size);
+
+ if (ev == NULL)
+ return -ENOMEM;
+
+ ev->attr.attr = *attr;
+ memcpy(ev->attr.id, id, ids * sizeof(u64));
+
+ ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
+ ev->attr.header.size = size;
+
+ err = process(ev, NULL, session);
+
+ free(ev);
+
+ return err;
+}
+
+int event__synthesize_attrs(struct perf_header *self, event__handler_t process,
+ struct perf_session *session)
+{
+ struct perf_header_attr *attr;
+ int i, err = 0;
+
+ for (i = 0; i < self->attrs; i++) {
+ attr = self->attr[i];
+
+ err = event__synthesize_attr(&attr->attr, attr->ids, attr->id,
+ process, session);
+ if (err) {
+ pr_debug("failed to create perf header attribute\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+int event__process_attr(event_t *self, struct perf_session *session)
+{
+ struct perf_header_attr *attr;
+ unsigned int i, ids, n_ids;
+
+ attr = perf_header_attr__new(&self->attr.attr);
+ if (attr == NULL)
+ return -ENOMEM;
+
+ ids = self->header.size;
+ ids -= (void *)&self->attr.id - (void *)self;
+ n_ids = ids / sizeof(u64);
+
+ for (i = 0; i < n_ids; i++) {
+ if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+ }
+
+ if (perf_header__add_attr(&session->header, attr) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+
+ perf_session__update_sample_type(session);
+
+ return 0;
+}
+
+int event__synthesize_event_type(u64 event_id, char *name,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t ev;
+ size_t size = 0;
+ int err = 0;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.event_type.event_type.event_id = event_id;
+ memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME);
+ strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
+
+ ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
+ size = strlen(name);
+ size = ALIGN(size, sizeof(u64));
+ ev.event_type.header.size = sizeof(ev.event_type) -
+ (sizeof(ev.event_type.event_type.name) - size);
+
+ err = process(&ev, NULL, session);
+
+ return err;
+}
+
+int event__synthesize_event_types(event__handler_t process,
+ struct perf_session *session)
+{
+ struct perf_trace_event_type *type;
+ int i, err = 0;
+
+ for (i = 0; i < event_count; i++) {
+ type = &events[i];
+
+ err = event__synthesize_event_type(type->event_id, type->name,
+ process, session);
+ if (err) {
+ pr_debug("failed to create perf header event type\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+int event__process_event_type(event_t *self,
+ struct perf_session *session __unused)
+{
+ if (perf_header__push_event(self->event_type.event_type.event_id,
+ self->event_type.event_type.name) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
+ event__handler_t process,
+ struct perf_session *session __unused)
+{
+ event_t ev;
+ ssize_t size = 0, aligned_size = 0, padding;
+ int err = 0;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
+ size = read_tracing_data_size(fd, pattrs);
+ if (size <= 0)
+ return size;
+ aligned_size = ALIGN(size, sizeof(u64));
+ padding = aligned_size - size;
+ ev.tracing_data.header.size = sizeof(ev.tracing_data);
+ ev.tracing_data.size = aligned_size;
+
+ process(&ev, NULL, session);
+
+ err = read_tracing_data(fd, pattrs);
+ write_padded(fd, NULL, 0, padding);
+
+ return aligned_size;
+}
+
+int event__process_tracing_data(event_t *self,
+ struct perf_session *session)
+{
+ ssize_t size_read, padding, size = self->tracing_data.size;
+ off_t offset = lseek(session->fd, 0, SEEK_CUR);
+ char buf[BUFSIZ];
+
+ /* setup for reading amidst mmap */
+ lseek(session->fd, offset + sizeof(struct tracing_data_event),
+ SEEK_SET);
+
+ size_read = trace_report(session->fd, session->repipe);
+
+ padding = ALIGN(size_read, sizeof(u64)) - size_read;
+
+ if (read(session->fd, buf, padding) < 0)
+ die("reading input file");
+ if (session->repipe) {
+ int retw = write(STDOUT_FILENO, buf, padding);
+ if (retw <= 0 || retw != padding)
+ die("repiping tracing data padding");
+ }
+
+ if (size_read + padding != size)
+ die("tracing data size mismatch");
+
+ return size_read + padding;
+}
+
+int event__synthesize_build_id(struct dso *pos, u16 misc,
+ event__handler_t process,
+ struct machine *machine,
+ struct perf_session *session)
+{
+ event_t ev;
+ size_t len;
+ int err = 0;
+
+ if (!pos->hit)
+ return err;
+
+ memset(&ev, 0, sizeof(ev));
+
+ len = pos->long_name_len + 1;
+ len = ALIGN(len, NAME_ALIGN);
+ memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
+ ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
+ ev.build_id.header.misc = misc;
+ ev.build_id.pid = machine->pid;
+ ev.build_id.header.size = sizeof(ev.build_id) + len;
+ memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
+
+ err = process(&ev, NULL, session);
+
+ return err;
+}
+
+int event__process_build_id(event_t *self,
+ struct perf_session *session)
+{
+ __event_process_build_id(&self->build_id,
+ self->build_id.filename,
+ session);
+ return 0;
+}
+
+void disable_buildid_cache(void)
+{
+ no_buildid_cache = true;
+}
diff --git a/smartt-perf/util/header.h b/smartt-perf/util/header.h
new file mode 100644
index 0000000..7cc53af
--- /dev/null
+++ b/smartt-perf/util/header.h
@@ -0,0 +1,128 @@
+#ifndef __PERF_HEADER_H
+#define __PERF_HEADER_H
+
+#include "linux/perf_event.h"
+#include <sys/types.h>
+#include <stdbool.h>
+#include "types.h"
+#include "event.h"
+
+#include <linux/bitmap.h>
+
+struct perf_header_attr {
+ struct perf_event_attr attr;
+ int ids, size;
+ u64 *id;
+ off_t id_offset;
+};
+
+enum {
+ HEADER_TRACE_INFO = 1,
+ HEADER_BUILD_ID,
+ HEADER_LAST_FEATURE,
+};
+
+#define HEADER_FEAT_BITS 256
+
+struct perf_file_section {
+ u64 offset;
+ u64 size;
+};
+
+struct perf_file_header {
+ u64 magic;
+ u64 size;
+ u64 attr_size;
+ struct perf_file_section attrs;
+ struct perf_file_section data;
+ struct perf_file_section event_types;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
+struct perf_pipe_file_header {
+ u64 magic;
+ u64 size;
+};
+
+struct perf_header;
+
+int perf_file_header__read(struct perf_file_header *self,
+ struct perf_header *ph, int fd);
+
+struct perf_header {
+ int frozen;
+ int attrs, size;
+ bool needs_swap;
+ struct perf_header_attr **attr;
+ s64 attr_offset;
+ u64 data_offset;
+ u64 data_size;
+ u64 event_offset;
+ u64 event_size;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
+int perf_header__init(struct perf_header *self);
+void perf_header__exit(struct perf_header *self);
+
+int perf_header__read(struct perf_session *session, int fd);
+int perf_header__write(struct perf_header *self, int fd, bool at_exit);
+int perf_header__write_pipe(int fd);
+
+int perf_header__add_attr(struct perf_header *self,
+ struct perf_header_attr *attr);
+
+int perf_header__push_event(u64 id, const char *name);
+char *perf_header__find_event(u64 id);
+
+struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr);
+void perf_header_attr__delete(struct perf_header_attr *self);
+
+int perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
+
+u64 perf_header__sample_type(struct perf_header *header);
+bool perf_header__sample_id_all(const struct perf_header *header);
+struct perf_event_attr *
+perf_header__find_attr(u64 id, struct perf_header *header);
+void perf_header__set_feat(struct perf_header *self, int feat);
+void perf_header__clear_feat(struct perf_header *self, int feat);
+bool perf_header__has_feat(const struct perf_header *self, int feat);
+
+int perf_header__process_sections(struct perf_header *self, int fd,
+ int (*process)(struct perf_file_section *self,
+ struct perf_header *ph,
+ int feat, int fd));
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+ const char *name, bool is_kallsyms);
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
+
+int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_attrs(struct perf_header *self,
+ event__handler_t process,
+ struct perf_session *session);
+int event__process_attr(event_t *self, struct perf_session *session);
+
+int event__synthesize_event_type(u64 event_id, char *name,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_event_types(event__handler_t process,
+ struct perf_session *session);
+int event__process_event_type(event_t *self,
+ struct perf_session *session);
+
+int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
+ event__handler_t process,
+ struct perf_session *session);
+int event__process_tracing_data(event_t *self,
+ struct perf_session *session);
+
+int event__synthesize_build_id(struct dso *pos, u16 misc,
+ event__handler_t process,
+ struct machine *machine,
+ struct perf_session *session);
+int event__process_build_id(event_t *self, struct perf_session *session);
+
+#endif /* __PERF_HEADER_H */
diff --git a/smartt-perf/util/help.c b/smartt-perf/util/help.c
new file mode 100644
index 0000000..6f2975a
--- /dev/null
+++ b/smartt-perf/util/help.c
@@ -0,0 +1,338 @@
+#include "cache.h"
+#include "../builtin.h"
+#include "exec_cmd.h"
+#include "levenshtein.h"
+#include "help.h"
+
+void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
+{
+ struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
+
+ ent->len = len;
+ memcpy(ent->name, name, len);
+ ent->name[len] = 0;
+
+ ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
+ cmds->names[cmds->cnt++] = ent;
+}
+
+static void clean_cmdnames(struct cmdnames *cmds)
+{
+ unsigned int i;
+
+ for (i = 0; i < cmds->cnt; ++i)
+ free(cmds->names[i]);
+ free(cmds->names);
+ cmds->cnt = 0;
+ cmds->alloc = 0;
+}
+
+static int cmdname_compare(const void *a_, const void *b_)
+{
+ struct cmdname *a = *(struct cmdname **)a_;
+ struct cmdname *b = *(struct cmdname **)b_;
+ return strcmp(a->name, b->name);
+}
+
+static void uniq(struct cmdnames *cmds)
+{
+ unsigned int i, j;
+
+ if (!cmds->cnt)
+ return;
+
+ for (i = j = 1; i < cmds->cnt; i++)
+ if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
+ cmds->names[j++] = cmds->names[i];
+
+ cmds->cnt = j;
+}
+
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
+{
+ size_t ci, cj, ei;
+ int cmp;
+
+ ci = cj = ei = 0;
+ while (ci < cmds->cnt && ei < excludes->cnt) {
+ cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
+ if (cmp < 0)
+ cmds->names[cj++] = cmds->names[ci++];
+ else if (cmp == 0)
+ ci++, ei++;
+ else if (cmp > 0)
+ ei++;
+ }
+
+ while (ci < cmds->cnt)
+ cmds->names[cj++] = cmds->names[ci++];
+
+ cmds->cnt = cj;
+}
+
+static void pretty_print_string_list(struct cmdnames *cmds, int longest)
+{
+ int cols = 1, rows;
+ int space = longest + 1; /* min 1 SP between words */
+ struct winsize win;
+ int max_cols;
+ int i, j;
+
+ get_term_dimensions(&win);
+ max_cols = win.ws_col - 1; /* don't print *on* the edge */
+
+ if (space < max_cols)
+ cols = max_cols / space;
+ rows = (cmds->cnt + cols - 1) / cols;
+
+ for (i = 0; i < rows; i++) {
+ printf(" ");
+
+ for (j = 0; j < cols; j++) {
+ unsigned int n = j * rows + i;
+ unsigned int size = space;
+
+ if (n >= cmds->cnt)
+ break;
+ if (j == cols-1 || n + rows >= cmds->cnt)
+ size = 1;
+ printf("%-*s", size, cmds->names[n]->name);
+ }
+ putchar('\n');
+ }
+}
+
+static int is_executable(const char *name)
+{
+ struct stat st;
+
+ if (stat(name, &st) || /* stat, not lstat */
+ !S_ISREG(st.st_mode))
+ return 0;
+
+ return st.st_mode & S_IXUSR;
+}
+
+static void list_commands_in_dir(struct cmdnames *cmds,
+ const char *path,
+ const char *prefix)
+{
+ int prefix_len;
+ DIR *dir = opendir(path);
+ struct dirent *de;
+ struct strbuf buf = STRBUF_INIT;
+ int len;
+
+ if (!dir)
+ return;
+ if (!prefix)
+ prefix = "perf-";
+ prefix_len = strlen(prefix);
+
+ strbuf_addf(&buf, "%s/", path);
+ len = buf.len;
+
+ while ((de = readdir(dir)) != NULL) {
+ int entlen;
+
+ if (prefixcmp(de->d_name, prefix))
+ continue;
+
+ strbuf_setlen(&buf, len);
+ strbuf_addstr(&buf, de->d_name);
+ if (!is_executable(buf.buf))
+ continue;
+
+ entlen = strlen(de->d_name) - prefix_len;
+ if (has_extension(de->d_name, ".exe"))
+ entlen -= 4;
+
+ add_cmdname(cmds, de->d_name + prefix_len, entlen);
+ }
+ closedir(dir);
+ strbuf_release(&buf);
+}
+
+void load_command_list(const char *prefix,
+ struct cmdnames *main_cmds,
+ struct cmdnames *other_cmds)
+{
+ const char *env_path = getenv("PATH");
+ const char *exec_path = perf_exec_path();
+
+ if (exec_path) {
+ list_commands_in_dir(main_cmds, exec_path, prefix);
+ qsort(main_cmds->names, main_cmds->cnt,
+ sizeof(*main_cmds->names), cmdname_compare);
+ uniq(main_cmds);
+ }
+
+ if (env_path) {
+ char *paths, *path, *colon;
+ path = paths = strdup(env_path);
+ while (1) {
+ if ((colon = strchr(path, PATH_SEP)))
+ *colon = 0;
+ if (!exec_path || strcmp(path, exec_path))
+ list_commands_in_dir(other_cmds, path, prefix);
+
+ if (!colon)
+ break;
+ path = colon + 1;
+ }
+ free(paths);
+
+ qsort(other_cmds->names, other_cmds->cnt,
+ sizeof(*other_cmds->names), cmdname_compare);
+ uniq(other_cmds);
+ }
+ exclude_cmds(other_cmds, main_cmds);
+}
+
+void list_commands(const char *title, struct cmdnames *main_cmds,
+ struct cmdnames *other_cmds)
+{
+ unsigned int i, longest = 0;
+
+ for (i = 0; i < main_cmds->cnt; i++)
+ if (longest < main_cmds->names[i]->len)
+ longest = main_cmds->names[i]->len;
+ for (i = 0; i < other_cmds->cnt; i++)
+ if (longest < other_cmds->names[i]->len)
+ longest = other_cmds->names[i]->len;
+
+ if (main_cmds->cnt) {
+ const char *exec_path = perf_exec_path();
+ printf("available %s in '%s'\n", title, exec_path);
+ printf("----------------");
+ mput_char('-', strlen(title) + strlen(exec_path));
+ putchar('\n');
+ pretty_print_string_list(main_cmds, longest);
+ putchar('\n');
+ }
+
+ if (other_cmds->cnt) {
+ printf("%s available from elsewhere on your $PATH\n", title);
+ printf("---------------------------------------");
+ mput_char('-', strlen(title));
+ putchar('\n');
+ pretty_print_string_list(other_cmds, longest);
+ putchar('\n');
+ }
+}
+
+int is_in_cmdlist(struct cmdnames *c, const char *s)
+{
+ unsigned int i;
+
+ for (i = 0; i < c->cnt; i++)
+ if (!strcmp(s, c->names[i]->name))
+ return 1;
+ return 0;
+}
+
+static int autocorrect;
+static struct cmdnames aliases;
+
+static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "help.autocorrect"))
+ autocorrect = perf_config_int(var,value);
+ /* Also use aliases for command lookup */
+ if (!prefixcmp(var, "alias."))
+ add_cmdname(&aliases, var + 6, strlen(var + 6));
+
+ return perf_default_config(var, value, cb);
+}
+
+static int levenshtein_compare(const void *p1, const void *p2)
+{
+ const struct cmdname *const *c1 = p1, *const *c2 = p2;
+ const char *s1 = (*c1)->name, *s2 = (*c2)->name;
+ int l1 = (*c1)->len;
+ int l2 = (*c2)->len;
+ return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
+}
+
+static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
+{
+ unsigned int i;
+
+ ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
+
+ for (i = 0; i < old->cnt; i++)
+ cmds->names[cmds->cnt++] = old->names[i];
+ free(old->names);
+ old->cnt = 0;
+ old->names = NULL;
+}
+
+const char *help_unknown_cmd(const char *cmd)
+{
+ unsigned int i, n = 0, best_similarity = 0;
+ struct cmdnames main_cmds, other_cmds;
+
+ memset(&main_cmds, 0, sizeof(main_cmds));
+ memset(&other_cmds, 0, sizeof(main_cmds));
+ memset(&aliases, 0, sizeof(aliases));
+
+ perf_config(perf_unknown_cmd_config, NULL);
+
+ load_command_list("perf-", &main_cmds, &other_cmds);
+
+ add_cmd_list(&main_cmds, &aliases);
+ add_cmd_list(&main_cmds, &other_cmds);
+ qsort(main_cmds.names, main_cmds.cnt,
+ sizeof(main_cmds.names), cmdname_compare);
+ uniq(&main_cmds);
+
+ if (main_cmds.cnt) {
+ /* This reuses cmdname->len for similarity index */
+ for (i = 0; i < main_cmds.cnt; ++i)
+ main_cmds.names[i]->len =
+ levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
+
+ qsort(main_cmds.names, main_cmds.cnt,
+ sizeof(*main_cmds.names), levenshtein_compare);
+
+ best_similarity = main_cmds.names[0]->len;
+ n = 1;
+ while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
+ ++n;
+ }
+
+ if (autocorrect && n == 1) {
+ const char *assumed = main_cmds.names[0]->name;
+
+ main_cmds.names[0] = NULL;
+ clean_cmdnames(&main_cmds);
+ fprintf(stderr, "WARNING: You called a perf program named '%s', "
+ "which does not exist.\n"
+ "Continuing under the assumption that you meant '%s'\n",
+ cmd, assumed);
+ if (autocorrect > 0) {
+ fprintf(stderr, "in %0.1f seconds automatically...\n",
+ (float)autocorrect/10.0);
+ poll(NULL, 0, autocorrect * 100);
+ }
+ return assumed;
+ }
+
+ fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
+
+ if (main_cmds.cnt && best_similarity < 6) {
+ fprintf(stderr, "\nDid you mean %s?\n",
+ n < 2 ? "this": "one of these");
+
+ for (i = 0; i < n; i++)
+ fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
+ }
+
+ exit(1);
+}
+
+int cmd_version(int argc __used, const char **argv __used, const char *prefix __used)
+{
+ printf("perf version %s\n", perf_version_string);
+ return 0;
+}
diff --git a/smartt-perf/util/help.h b/smartt-perf/util/help.h
new file mode 100644
index 0000000..7f5c6de
--- /dev/null
+++ b/smartt-perf/util/help.h
@@ -0,0 +1,29 @@
+#ifndef __PERF_HELP_H
+#define __PERF_HELP_H
+
+struct cmdnames {
+ size_t alloc;
+ size_t cnt;
+ struct cmdname {
+ size_t len; /* also used for similarity index in help.c */
+ char name[FLEX_ARRAY];
+ } **names;
+};
+
+static inline void mput_char(char c, unsigned int num)
+{
+ while(num--)
+ putchar(c);
+}
+
+void load_command_list(const char *prefix,
+ struct cmdnames *main_cmds,
+ struct cmdnames *other_cmds);
+void add_cmdname(struct cmdnames *cmds, const char *name, size_t len);
+/* Here we require that excludes is a sorted list. */
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+int is_in_cmdlist(struct cmdnames *c, const char *s);
+void list_commands(const char *title, struct cmdnames *main_cmds,
+ struct cmdnames *other_cmds);
+
+#endif /* __PERF_HELP_H */
diff --git a/smartt-perf/util/hist.c b/smartt-perf/util/hist.c
new file mode 100644
index 0000000..df51560
--- /dev/null
+++ b/smartt-perf/util/hist.c
@@ -0,0 +1,1193 @@
+#include "util.h"
+#include "build-id.h"
+#include "hist.h"
+#include "session.h"
+#include "sort.h"
+#include <math.h>
+
+enum hist_filter {
+ HIST_FILTER__DSO,
+ HIST_FILTER__THREAD,
+ HIST_FILTER__PARENT,
+};
+
+struct callchain_param callchain_param = {
+ .mode = CHAIN_GRAPH_REL,
+ .min_percent = 0.5
+};
+
+u16 hists__col_len(struct hists *self, enum hist_column col)
+{
+ return self->col_len[col];
+}
+
+void hists__set_col_len(struct hists *self, enum hist_column col, u16 len)
+{
+ self->col_len[col] = len;
+}
+
+bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len)
+{
+ if (len > hists__col_len(self, col)) {
+ hists__set_col_len(self, col, len);
+ return true;
+ }
+ return false;
+}
+
+static void hists__reset_col_len(struct hists *self)
+{
+ enum hist_column col;
+
+ for (col = 0; col < HISTC_NR_COLS; ++col)
+ hists__set_col_len(self, col, 0);
+}
+
+static void hists__calc_col_len(struct hists *self, struct hist_entry *h)
+{
+ u16 len;
+
+ if (h->ms.sym)
+ hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen);
+
+ len = thread__comm_len(h->thread);
+ if (hists__new_col_len(self, HISTC_COMM, len))
+ hists__set_col_len(self, HISTC_THREAD, len + 6);
+
+ if (h->ms.map) {
+ len = dso__name_len(h->ms.map->dso);
+ hists__new_col_len(self, HISTC_DSO, len);
+ }
+}
+
+static void hist_entry__add_cpumode_period(struct hist_entry *self,
+ unsigned int cpumode, u64 period)
+{
+ switch (cpumode) {
+ case PERF_RECORD_MISC_KERNEL:
+ self->period_sys += period;
+ break;
+ case PERF_RECORD_MISC_USER:
+ self->period_us += period;
+ break;
+ case PERF_RECORD_MISC_GUEST_KERNEL:
+ self->period_guest_sys += period;
+ break;
+ case PERF_RECORD_MISC_GUEST_USER:
+ self->period_guest_us += period;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * histogram, sorted on item, collects periods
+ */
+
+static struct hist_entry *hist_entry__new(struct hist_entry *template)
+{
+ size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
+ struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
+
+ if (self != NULL) {
+ *self = *template;
+ self->nr_events = 1;
+ if (self->ms.map)
+ self->ms.map->referenced = true;
+ if (symbol_conf.use_callchain)
+ callchain_init(self->callchain);
+ }
+
+ return self;
+}
+
+static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h)
+{
+ if (!h->filtered) {
+ hists__calc_col_len(self, h);
+ ++self->nr_entries;
+ }
+}
+
+static u8 symbol__parent_filter(const struct symbol *parent)
+{
+ if (symbol_conf.exclude_other && parent == NULL)
+ return 1 << HIST_FILTER__PARENT;
+ return 0;
+}
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent, u64 period)
+{
+ struct rb_node **p = &self->entries.rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *he;
+ struct hist_entry entry = {
+ .thread = al->thread,
+ .ms = {
+ .map = al->map,
+ .sym = al->sym,
+ },
+ .cpu = al->cpu,
+ .ip = al->addr,
+ .level = al->level,
+ .period = period,
+ .parent = sym_parent,
+ .filtered = symbol__parent_filter(sym_parent),
+ };
+ int cmp;
+
+ while (*p != NULL) {
+ parent = *p;
+ he = rb_entry(parent, struct hist_entry, rb_node);
+
+ cmp = hist_entry__cmp(&entry, he);
+
+ if (!cmp) {
+ he->period += period;
+ ++he->nr_events;
+ goto out;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ he = hist_entry__new(&entry);
+ if (!he)
+ return NULL;
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, &self->entries);
+ hists__inc_nr_entries(self, he);
+out:
+ hist_entry__add_cpumode_period(he, al->cpumode, period);
+ return he;
+}
+
+int64_t
+hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct sort_entry *se;
+ int64_t cmp = 0;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ cmp = se->se_cmp(left, right);
+ if (cmp)
+ break;
+ }
+
+ return cmp;
+}
+
+int64_t
+hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
+{
+ struct sort_entry *se;
+ int64_t cmp = 0;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ int64_t (*f)(struct hist_entry *, struct hist_entry *);
+
+ f = se->se_collapse ?: se->se_cmp;
+
+ cmp = f(left, right);
+ if (cmp)
+ break;
+ }
+
+ return cmp;
+}
+
+void hist_entry__free(struct hist_entry *he)
+{
+ free(he);
+}
+
+/*
+ * collapse the histogram
+ */
+
+static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+ int64_t cmp;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+
+ cmp = hist_entry__collapse(iter, he);
+
+ if (!cmp) {
+ iter->period += he->period;
+ if (symbol_conf.use_callchain)
+ callchain_merge(iter->callchain, he->callchain);
+ hist_entry__free(he);
+ return false;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, root);
+ return true;
+}
+
+void hists__collapse_resort(struct hists *self)
+{
+ struct rb_root tmp;
+ struct rb_node *next;
+ struct hist_entry *n;
+
+ if (!sort__need_collapse)
+ return;
+
+ tmp = RB_ROOT;
+ next = rb_first(&self->entries);
+ self->nr_entries = 0;
+ hists__reset_col_len(self);
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ next = rb_next(&n->rb_node);
+
+ rb_erase(&n->rb_node, &self->entries);
+ if (collapse__insert_entry(&tmp, n))
+ hists__inc_nr_entries(self, n);
+ }
+
+ self->entries = tmp;
+}
+
+/*
+ * reverse the map, sort on period.
+ */
+
+static void __hists__insert_output_entry(struct rb_root *entries,
+ struct hist_entry *he,
+ u64 min_callchain_hits)
+{
+ struct rb_node **p = &entries->rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+
+ if (symbol_conf.use_callchain)
+ callchain_param.sort(&he->sorted_chain, he->callchain,
+ min_callchain_hits, &callchain_param);
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+
+ if (he->period > iter->period)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, entries);
+}
+
+void hists__output_resort(struct hists *self)
+{
+ struct rb_root tmp;
+ struct rb_node *next;
+ struct hist_entry *n;
+ u64 min_callchain_hits;
+
+ min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100);
+
+ tmp = RB_ROOT;
+ next = rb_first(&self->entries);
+
+ self->nr_entries = 0;
+ hists__reset_col_len(self);
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ next = rb_next(&n->rb_node);
+
+ rb_erase(&n->rb_node, &self->entries);
+ __hists__insert_output_entry(&tmp, n, min_callchain_hits);
+ hists__inc_nr_entries(self, n);
+ }
+
+ self->entries = tmp;
+}
+
+static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
+{
+ int i;
+ int ret = fprintf(fp, " ");
+
+ for (i = 0; i < left_margin; i++)
+ ret += fprintf(fp, " ");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
+ int left_margin)
+{
+ int i;
+ size_t ret = callchain__fprintf_left_margin(fp, left_margin);
+
+ for (i = 0; i < depth; i++)
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "| ");
+ else
+ ret += fprintf(fp, " ");
+
+ ret += fprintf(fp, "\n");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
+ int depth, int depth_mask, int period,
+ u64 total_samples, u64 hits,
+ int left_margin)
+{
+ int i;
+ size_t ret = 0;
+
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ for (i = 0; i < depth; i++) {
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "|");
+ else
+ ret += fprintf(fp, " ");
+ if (!period && i == depth - 1) {
+ double percent;
+
+ percent = hits * 100.0 / total_samples;
+ ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
+ } else
+ ret += fprintf(fp, "%s", " ");
+ }
+ if (chain->ms.sym)
+ ret += fprintf(fp, "%s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
+
+ return ret;
+}
+
+static struct symbol *rem_sq_bracket;
+static struct callchain_list rem_hits;
+
+static void init_rem_hits(void)
+{
+ rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ if (!rem_sq_bracket) {
+ fprintf(stderr, "Not enough memory to display remaining hits\n");
+ return;
+ }
+
+ strcpy(rem_sq_bracket->name, "[...]");
+ rem_hits.ms.sym = rem_sq_bracket;
+}
+
+static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+ u64 total_samples, int depth,
+ int depth_mask, int left_margin)
+{
+ struct rb_node *node, *next;
+ struct callchain_node *child;
+ struct callchain_list *chain;
+ int new_depth_mask = depth_mask;
+ u64 new_total;
+ u64 remaining;
+ size_t ret = 0;
+ int i;
+ uint entries_printed = 0;
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = self->children_hit;
+ else
+ new_total = total_samples;
+
+ remaining = new_total;
+
+ node = rb_first(&self->rb_root);
+ while (node) {
+ u64 cumul;
+
+ child = rb_entry(node, struct callchain_node, rb_node);
+ cumul = cumul_hits(child);
+ remaining -= cumul;
+
+ /*
+ * The depth mask manages the output of pipes that show
+ * the depth. We don't want to keep the pipes of the current
+ * level for the last child of this depth.
+ * Except if we have remaining filtered hits. They will
+ * supersede the last child
+ */
+ next = rb_next(node);
+ if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ /*
+ * But we keep the older depth mask for the line separator
+ * to keep the level link until we reach the last child
+ */
+ ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
+ left_margin);
+ i = 0;
+ list_for_each_entry(chain, &child->val, list) {
+ ret += ipchain__fprintf_graph(fp, chain, depth,
+ new_depth_mask, i++,
+ new_total,
+ cumul,
+ left_margin);
+ }
+ ret += __callchain__fprintf_graph(fp, child, new_total,
+ depth + 1,
+ new_depth_mask | (1 << depth),
+ left_margin);
+ node = next;
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ }
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL &&
+ remaining && remaining != new_total) {
+
+ if (!rem_sq_bracket)
+ return ret;
+
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
+ new_depth_mask, 0, new_total,
+ remaining, left_margin);
+ }
+
+ return ret;
+}
+
+static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+ u64 total_samples, int left_margin)
+{
+ struct callchain_list *chain;
+ bool printed = false;
+ int i = 0;
+ int ret = 0;
+ u32 entries_printed = 0;
+
+ list_for_each_entry(chain, &self->val, list) {
+ if (!i++ && sort__first_dimension == SORT_SYM)
+ continue;
+
+ if (!printed) {
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "|\n");
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "---");
+
+ left_margin += 3;
+ printed = true;
+ } else
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+
+ if (chain->ms.sym)
+ ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ }
+
+ ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
+
+ return ret;
+}
+
+static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
+ u64 total_samples)
+{
+ struct callchain_list *chain;
+ size_t ret = 0;
+
+ if (!self)
+ return 0;
+
+ ret += callchain__fprintf_flat(fp, self->parent, total_samples);
+
+
+ list_for_each_entry(chain, &self->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ if (chain->ms.sym)
+ ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, " %p\n",
+ (void *)(long)chain->ip);
+ }
+
+ return ret;
+}
+
+static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
+ u64 total_samples, int left_margin)
+{
+ struct rb_node *rb_node;
+ struct callchain_node *chain;
+ size_t ret = 0;
+ u32 entries_printed = 0;
+
+ rb_node = rb_first(&self->sorted_chain);
+ while (rb_node) {
+ double percent;
+
+ chain = rb_entry(rb_node, struct callchain_node, rb_node);
+ percent = chain->hit * 100.0 / total_samples;
+ switch (callchain_param.mode) {
+ case CHAIN_FLAT:
+ ret += percent_color_fprintf(fp, " %6.2f%%\n",
+ percent);
+ ret += callchain__fprintf_flat(fp, chain, total_samples);
+ break;
+ case CHAIN_GRAPH_ABS: /* Falldown */
+ case CHAIN_GRAPH_REL:
+ ret += callchain__fprintf_graph(fp, chain, total_samples,
+ left_margin);
+ case CHAIN_NONE:
+ default:
+ break;
+ }
+ ret += fprintf(fp, "\n");
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ rb_node = rb_next(rb_node);
+ }
+
+ return ret;
+}
+
+int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
+ struct hists *hists, struct hists *pair_hists,
+ bool show_displacement, long displacement,
+ bool color, u64 session_total)
+{
+ struct sort_entry *se;
+ u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
+ u64 nr_events;
+ const char *sep = symbol_conf.field_sep;
+ int ret;
+
+ if (symbol_conf.exclude_other && !self->parent)
+ return 0;
+
+ if (pair_hists) {
+ period = self->pair ? self->pair->period : 0;
+ nr_events = self->pair ? self->pair->nr_events : 0;
+ total = pair_hists->stats.total_period;
+ period_sys = self->pair ? self->pair->period_sys : 0;
+ period_us = self->pair ? self->pair->period_us : 0;
+ period_guest_sys = self->pair ? self->pair->period_guest_sys : 0;
+ period_guest_us = self->pair ? self->pair->period_guest_us : 0;
+ } else {
+ period = self->period;
+ nr_events = self->nr_events;
+ total = session_total;
+ period_sys = self->period_sys;
+ period_us = self->period_us;
+ period_guest_sys = self->period_guest_sys;
+ period_guest_us = self->period_guest_us;
+ }
+
+ if (total) {
+ if (color)
+ ret = percent_color_snprintf(s, size,
+ sep ? "%.2f" : " %6.2f%%",
+ (period * 100.0) / total);
+ else
+ ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
+ (period * 100.0) / total);
+ if (symbol_conf.show_cpu_utilization) {
+ ret += percent_color_snprintf(s + ret, size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_sys * 100.0) / total);
+ ret += percent_color_snprintf(s + ret, size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_us * 100.0) / total);
+ if (perf_guest) {
+ ret += percent_color_snprintf(s + ret,
+ size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_guest_sys * 100.0) /
+ total);
+ ret += percent_color_snprintf(s + ret,
+ size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_guest_us * 100.0) /
+ total);
+ }
+ }
+ } else
+ ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
+
+ if (symbol_conf.show_nr_samples) {
+ if (sep)
+ ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
+ else
+ ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
+ }
+
+ if (pair_hists) {
+ char bf[32];
+ double old_percent = 0, new_percent = 0, diff;
+
+ if (total > 0)
+ old_percent = (period * 100.0) / total;
+ if (session_total > 0)
+ new_percent = (self->period * 100.0) / session_total;
+
+ diff = new_percent - old_percent;
+
+ if (fabs(diff) >= 0.01)
+ snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
+ else
+ snprintf(bf, sizeof(bf), " ");
+
+ if (sep)
+ ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+ else
+ ret += snprintf(s + ret, size - ret, "%11.11s", bf);
+
+ if (show_displacement) {
+ if (displacement)
+ snprintf(bf, sizeof(bf), "%+4ld", displacement);
+ else
+ snprintf(bf, sizeof(bf), " ");
+
+ if (sep)
+ ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+ else
+ ret += snprintf(s + ret, size - ret, "%6.6s", bf);
+ }
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
+ ret += se->se_snprintf(self, s + ret, size - ret,
+ hists__col_len(hists, se->se_width_idx));
+ }
+
+ return ret;
+}
+
+int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
+ struct hists *pair_hists, bool show_displacement,
+ long displacement, FILE *fp, u64 session_total)
+{
+ char bf[512];
+ hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists,
+ show_displacement, displacement,
+ true, session_total);
+ return fprintf(fp, "%s\n", bf);
+}
+
+static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
+ struct hists *hists, FILE *fp,
+ u64 session_total)
+{
+ int left_margin = 0;
+
+ if (sort__first_dimension == SORT_COMM) {
+ struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
+ typeof(*se), list);
+ left_margin = hists__col_len(hists, se->se_width_idx);
+ left_margin -= thread__comm_len(self->thread);
+ }
+
+ return hist_entry_callchain__fprintf(fp, self, session_total,
+ left_margin);
+}
+
+size_t hists__fprintf(struct hists *self, struct hists *pair,
+ bool show_displacement, FILE *fp)
+{
+ struct sort_entry *se;
+ struct rb_node *nd;
+ size_t ret = 0;
+ unsigned long position = 1;
+ long displacement = 0;
+ unsigned int width;
+ const char *sep = symbol_conf.field_sep;
+ const char *col_width = symbol_conf.col_width_list_str;
+
+ init_rem_hits();
+
+ fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
+
+ if (symbol_conf.show_nr_samples) {
+ if (sep)
+ fprintf(fp, "%cSamples", *sep);
+ else
+ fputs(" Samples ", fp);
+ }
+
+ if (symbol_conf.show_cpu_utilization) {
+ if (sep) {
+ ret += fprintf(fp, "%csys", *sep);
+ ret += fprintf(fp, "%cus", *sep);
+ if (perf_guest) {
+ ret += fprintf(fp, "%cguest sys", *sep);
+ ret += fprintf(fp, "%cguest us", *sep);
+ }
+ } else {
+ ret += fprintf(fp, " sys ");
+ ret += fprintf(fp, " us ");
+ if (perf_guest) {
+ ret += fprintf(fp, " guest sys ");
+ ret += fprintf(fp, " guest us ");
+ }
+ }
+ }
+
+ if (pair) {
+ if (sep)
+ ret += fprintf(fp, "%cDelta", *sep);
+ else
+ ret += fprintf(fp, " Delta ");
+
+ if (show_displacement) {
+ if (sep)
+ ret += fprintf(fp, "%cDisplacement", *sep);
+ else
+ ret += fprintf(fp, " Displ");
+ }
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+ if (sep) {
+ fprintf(fp, "%c%s", *sep, se->se_header);
+ continue;
+ }
+ width = strlen(se->se_header);
+ if (symbol_conf.col_width_list_str) {
+ if (col_width) {
+ hists__set_col_len(self, se->se_width_idx,
+ atoi(col_width));
+ col_width = strchr(col_width, ',');
+ if (col_width)
+ ++col_width;
+ }
+ }
+ if (!hists__new_col_len(self, se->se_width_idx, width))
+ width = hists__col_len(self, se->se_width_idx);
+ fprintf(fp, " %*s", width, se->se_header);
+ }
+ fprintf(fp, "\n");
+
+ if (sep)
+ goto print_entries;
+
+ fprintf(fp, "# ........");
+ if (symbol_conf.show_nr_samples)
+ fprintf(fp, " ..........");
+ if (pair) {
+ fprintf(fp, " ..........");
+ if (show_displacement)
+ fprintf(fp, " .....");
+ }
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ unsigned int i;
+
+ if (se->elide)
+ continue;
+
+ fprintf(fp, " ");
+ width = hists__col_len(self, se->se_width_idx);
+ if (width == 0)
+ width = strlen(se->se_header);
+ for (i = 0; i < width; i++)
+ fprintf(fp, ".");
+ }
+
+ fprintf(fp, "\n#\n");
+
+print_entries:
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (show_displacement) {
+ if (h->pair != NULL)
+ displacement = ((long)h->pair->position -
+ (long)position);
+ else
+ displacement = 0;
+ ++position;
+ }
+ ret += hist_entry__fprintf(h, self, pair, show_displacement,
+ displacement, fp, self->stats.total_period);
+
+ if (symbol_conf.use_callchain)
+ ret += hist_entry__fprintf_callchain(h, self, fp,
+ self->stats.total_period);
+ if (h->ms.map == NULL && verbose > 1) {
+ __map_groups__fprintf_maps(&h->thread->mg,
+ MAP__FUNCTION, verbose, fp);
+ fprintf(fp, "%.10s end\n", graph_dotted_line);
+ }
+ }
+
+ free(rem_sq_bracket);
+
+ return ret;
+}
+
+/*
+ * See hists__fprintf to match the column widths
+ */
+unsigned int hists__sort_list_width(struct hists *self)
+{
+ struct sort_entry *se;
+ int ret = 9; /* total % */
+
+ if (symbol_conf.show_cpu_utilization) {
+ ret += 7; /* count_sys % */
+ ret += 6; /* count_us % */
+ if (perf_guest) {
+ ret += 13; /* count_guest_sys % */
+ ret += 12; /* count_guest_us % */
+ }
+ }
+
+ if (symbol_conf.show_nr_samples)
+ ret += 11;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list)
+ if (!se->elide)
+ ret += 2 + hists__col_len(self, se->se_width_idx);
+
+ if (verbose) /* Addr + origin */
+ ret += 3 + BITS_PER_LONG / 4;
+
+ return ret;
+}
+
+static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h,
+ enum hist_filter filter)
+{
+ h->filtered &= ~(1 << filter);
+ if (h->filtered)
+ return;
+
+ ++self->nr_entries;
+ if (h->ms.unfolded)
+ self->nr_entries += h->nr_rows;
+ h->row_offset = 0;
+ self->stats.total_period += h->period;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
+
+ hists__calc_col_len(self, h);
+}
+
+void hists__filter_by_dso(struct hists *self, const struct dso *dso)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = self->stats.total_period = 0;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+ hists__reset_col_len(self);
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (symbol_conf.exclude_other && !h->parent)
+ continue;
+
+ if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) {
+ h->filtered |= (1 << HIST_FILTER__DSO);
+ continue;
+ }
+
+ hists__remove_entry_filter(self, h, HIST_FILTER__DSO);
+ }
+}
+
+void hists__filter_by_thread(struct hists *self, const struct thread *thread)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = self->stats.total_period = 0;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+ hists__reset_col_len(self);
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (thread != NULL && h->thread != thread) {
+ h->filtered |= (1 << HIST_FILTER__THREAD);
+ continue;
+ }
+
+ hists__remove_entry_filter(self, h, HIST_FILTER__THREAD);
+ }
+}
+
+static int symbol__alloc_hist(struct symbol *self)
+{
+ struct sym_priv *priv = symbol__priv(self);
+ const int size = (sizeof(*priv->hist) +
+ (self->end - self->start) * sizeof(u64));
+
+ priv->hist = zalloc(size);
+ return priv->hist == NULL ? -1 : 0;
+}
+
+int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
+{
+ unsigned int sym_size, offset;
+ struct symbol *sym = self->ms.sym;
+ struct sym_priv *priv;
+ struct sym_hist *h;
+
+ if (!sym || !self->ms.map)
+ return 0;
+
+ priv = symbol__priv(sym);
+ if (priv->hist == NULL && symbol__alloc_hist(sym) < 0)
+ return -ENOMEM;
+
+ sym_size = sym->end - sym->start;
+ offset = ip - sym->start;
+
+ pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip));
+
+ if (offset >= sym_size)
+ return 0;
+
+ h = priv->hist;
+ h->sum++;
+ h->ip[offset]++;
+
+ pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64
+ "] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name,
+ ip, ip - self->ms.sym->start, h->ip[offset]);
+ return 0;
+}
+
+static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
+{
+ struct objdump_line *self = malloc(sizeof(*self) + privsize);
+
+ if (self != NULL) {
+ self->offset = offset;
+ self->line = line;
+ }
+
+ return self;
+}
+
+void objdump_line__free(struct objdump_line *self)
+{
+ free(self->line);
+ free(self);
+}
+
+static void objdump__add_line(struct list_head *head, struct objdump_line *line)
+{
+ list_add_tail(&line->node, head);
+}
+
+struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
+ struct objdump_line *pos)
+{
+ list_for_each_entry_continue(pos, head, node)
+ if (pos->offset >= 0)
+ return pos;
+
+ return NULL;
+}
+
+static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
+ struct list_head *head, size_t privsize)
+{
+ struct symbol *sym = self->ms.sym;
+ struct objdump_line *objdump_line;
+ char *line = NULL, *tmp, *tmp2, *c;
+ size_t line_len;
+ s64 line_ip, offset = -1;
+
+ if (getline(&line, &line_len, file) < 0)
+ return -1;
+
+ if (!line)
+ return -1;
+
+ while (line_len != 0 && isspace(line[line_len - 1]))
+ line[--line_len] = '\0';
+
+ c = strchr(line, '\n');
+ if (c)
+ *c = 0;
+
+ line_ip = -1;
+
+ /*
+ * Strip leading spaces:
+ */
+ tmp = line;
+ while (*tmp) {
+ if (*tmp != ' ')
+ break;
+ tmp++;
+ }
+
+ if (*tmp) {
+ /*
+ * Parse hexa addresses followed by ':'
+ */
+ line_ip = strtoull(tmp, &tmp2, 16);
+ if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
+ line_ip = -1;
+ }
+
+ if (line_ip != -1) {
+ u64 start = map__rip_2objdump(self->ms.map, sym->start),
+ end = map__rip_2objdump(self->ms.map, sym->end);
+
+ offset = line_ip - start;
+ if (offset < 0 || (u64)line_ip > end)
+ offset = -1;
+ }
+
+ objdump_line = objdump_line__new(offset, line, privsize);
+ if (objdump_line == NULL) {
+ free(line);
+ return -1;
+ }
+ objdump__add_line(head, objdump_line);
+
+ return 0;
+}
+
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
+ size_t privsize)
+{
+ struct symbol *sym = self->ms.sym;
+ struct map *map = self->ms.map;
+ struct dso *dso = map->dso;
+ char *filename = dso__build_id_filename(dso, NULL, 0);
+ bool free_filename = true;
+ char command[PATH_MAX * 2];
+ FILE *file;
+ int err = 0;
+ u64 len;
+ char symfs_filename[PATH_MAX];
+
+ if (filename) {
+ snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+ symbol_conf.symfs, filename);
+ }
+
+ if (filename == NULL) {
+ if (dso->has_build_id) {
+ pr_err("Can't annotate %s: not enough memory\n",
+ sym->name);
+ return -ENOMEM;
+ }
+ goto fallback;
+ } else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
+ strstr(command, "[kernel.kallsyms]") ||
+ access(symfs_filename, R_OK)) {
+ free(filename);
+fallback:
+ /*
+ * If we don't have build-ids or the build-id file isn't in the
+ * cache, or is just a kallsyms file, well, lets hope that this
+ * DSO is the same as when 'perf record' ran.
+ */
+ filename = dso->long_name;
+ snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+ symbol_conf.symfs, filename);
+ free_filename = false;
+ }
+
+ if (dso->origin == DSO__ORIG_KERNEL) {
+ if (dso->annotate_warned)
+ goto out_free_filename;
+ err = -ENOENT;
+ dso->annotate_warned = 1;
+ pr_err("Can't annotate %s: No vmlinux file was found in the "
+ "path\n", sym->name);
+ goto out_free_filename;
+ }
+
+ pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
+ filename, sym->name, map->unmap_ip(map, sym->start),
+ map->unmap_ip(map, sym->end));
+
+ len = sym->end - sym->start;
+
+ pr_debug("annotating [%p] %30s : [%p] %30s\n",
+ dso, dso->long_name, sym, sym->name);
+
+ snprintf(command, sizeof(command),
+ "objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand",
+ map__rip_2objdump(map, sym->start),
+ map__rip_2objdump(map, sym->end),
+ symfs_filename, filename);
+
+ pr_debug("Executing: %s\n", command);
+
+ file = popen(command, "r");
+ if (!file)
+ goto out_free_filename;
+
+ while (!feof(file))
+ if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
+ break;
+
+ pclose(file);
+out_free_filename:
+ if (free_filename)
+ free(filename);
+ return err;
+}
+
+void hists__inc_nr_events(struct hists *self, u32 type)
+{
+ ++self->stats.nr_events[0];
+ ++self->stats.nr_events[type];
+}
+
+size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
+{
+ int i;
+ size_t ret = 0;
+
+ for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
+ const char *name = event__get_event_name(i);
+
+ if (!strcmp(name, "UNKNOWN"))
+ continue;
+
+ ret += fprintf(fp, "%16s events: %10d\n", name,
+ self->stats.nr_events[i]);
+ }
+
+ return ret;
+}
diff --git a/smartt-perf/util/hist.h b/smartt-perf/util/hist.h
new file mode 100644
index 0000000..ee78985
--- /dev/null
+++ b/smartt-perf/util/hist.h
@@ -0,0 +1,150 @@
+#ifndef __PERF_HIST_H
+#define __PERF_HIST_H
+
+#include <linux/types.h>
+#include "callchain.h"
+
+extern struct callchain_param callchain_param;
+
+struct hist_entry;
+struct addr_location;
+struct symbol;
+struct rb_root;
+
+struct objdump_line {
+ struct list_head node;
+ s64 offset;
+ char *line;
+};
+
+void objdump_line__free(struct objdump_line *self);
+struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
+ struct objdump_line *pos);
+
+struct sym_hist {
+ u64 sum;
+ u64 ip[0];
+};
+
+struct sym_ext {
+ struct rb_node node;
+ double percent;
+ char *path;
+};
+
+struct sym_priv {
+ struct sym_hist *hist;
+ struct sym_ext *ext;
+};
+
+/*
+ * The kernel collects the number of events it couldn't send in a stretch and
+ * when possible sends this number in a PERF_RECORD_LOST event. The number of
+ * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
+ * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
+ * the sum of all struct lost_event.lost fields reported.
+ *
+ * The total_period is needed because by default auto-freq is used, so
+ * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
+ * the total number of low level events, it is necessary to to sum all struct
+ * sample_event.period and stash the result in total_period.
+ */
+struct events_stats {
+ u64 total_period;
+ u64 total_lost;
+ u64 total_invalid_chains;
+ u32 nr_events[PERF_RECORD_HEADER_MAX];
+ u32 nr_unknown_events;
+ u32 nr_invalid_chains;
+};
+
+enum hist_column {
+ HISTC_SYMBOL,
+ HISTC_DSO,
+ HISTC_THREAD,
+ HISTC_COMM,
+ HISTC_PARENT,
+ HISTC_CPU,
+ HISTC_NR_COLS, /* Last entry */
+};
+
+struct hists {
+ struct rb_node rb_node;
+ struct rb_root entries;
+ u64 nr_entries;
+ struct events_stats stats;
+ u64 config;
+ u64 event_stream;
+ u32 type;
+ u16 col_len[HISTC_NR_COLS];
+};
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *parent, u64 period);
+extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
+int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
+ struct hists *pair_hists, bool show_displacement,
+ long displacement, FILE *fp, u64 total);
+int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
+ struct hists *hists, struct hists *pair_hists,
+ bool show_displacement, long displacement,
+ bool color, u64 total);
+void hist_entry__free(struct hist_entry *);
+
+void hists__output_resort(struct hists *self);
+void hists__collapse_resort(struct hists *self);
+
+void hists__inc_nr_events(struct hists *self, u32 type);
+size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
+
+size_t hists__fprintf(struct hists *self, struct hists *pair,
+ bool show_displacement, FILE *fp);
+
+int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
+ size_t privsize);
+
+void hists__filter_by_dso(struct hists *self, const struct dso *dso);
+void hists__filter_by_thread(struct hists *self, const struct thread *thread);
+
+u16 hists__col_len(struct hists *self, enum hist_column col);
+void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
+bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
+
+#ifdef NO_NEWT_SUPPORT
+static inline int hists__browse(struct hists *self __used,
+ const char *helpline __used,
+ const char *ev_name __used)
+{
+ return 0;
+}
+
+static inline int hists__tui_browse_tree(struct rb_root *self __used,
+ const char *help __used)
+{
+ return 0;
+}
+
+static inline int hist_entry__tui_annotate(struct hist_entry *self __used)
+{
+ return 0;
+}
+#define KEY_LEFT -1
+#define KEY_RIGHT -2
+#else
+#include <newt.h>
+int hists__browse(struct hists *self, const char *helpline,
+ const char *ev_name);
+int hist_entry__tui_annotate(struct hist_entry *self);
+
+#define KEY_LEFT NEWT_KEY_LEFT
+#define KEY_RIGHT NEWT_KEY_RIGHT
+
+int hists__tui_browse_tree(struct rb_root *self, const char *help);
+#endif
+
+unsigned int hists__sort_list_width(struct hists *self);
+
+#endif /* __PERF_HIST_H */
diff --git a/smartt-perf/util/hweight.c b/smartt-perf/util/hweight.c
new file mode 100644
index 0000000..5c1d0d0
--- /dev/null
+++ b/smartt-perf/util/hweight.c
@@ -0,0 +1,31 @@
+#include <linux/bitops.h>
+
+/**
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+unsigned int hweight32(unsigned int w)
+{
+ unsigned int res = w - ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res + (res >> 4)) & 0x0F0F0F0F;
+ res = res + (res >> 8);
+ return (res + (res >> 16)) & 0x000000FF;
+}
+
+unsigned long hweight64(__u64 w)
+{
+#if BITS_PER_LONG == 32
+ return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w);
+#elif BITS_PER_LONG == 64
+ __u64 res = w - ((w >> 1) & 0x5555555555555555ul);
+ res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul);
+ res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful;
+ res = res + (res >> 8);
+ res = res + (res >> 16);
+ return (res + (res >> 32)) & 0x00000000000000FFul;
+#endif
+}
diff --git a/smartt-perf/util/include/arch/arm/include/asm/unistd.h b/smartt-perf/util/include/arch/arm/include/asm/unistd.h
new file mode 100644
index 0000000..c891eb7
--- /dev/null
+++ b/smartt-perf/util/include/arch/arm/include/asm/unistd.h
@@ -0,0 +1,479 @@
+/*
+ * arch/arm/include/asm/unistd.h
+ *
+ * Copyright (C) 2001-2005 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Please forward _all_ changes to this file to rmk@arm.linux.org.uk,
+ * no matter what the change is. Thanks!
+ */
+#ifndef __ASM_ARM_UNISTD_H
+#define __ASM_ARM_UNISTD_H
+
+#define __NR_OABI_SYSCALL_BASE 0x900000
+
+#if defined(__thumb__) || defined(__ARM_EABI__)
+#define __NR_SYSCALL_BASE 0
+#else
+#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE
+#endif
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
+#define __NR_exit (__NR_SYSCALL_BASE+ 1)
+#define __NR_fork (__NR_SYSCALL_BASE+ 2)
+#define __NR_read (__NR_SYSCALL_BASE+ 3)
+#define __NR_write (__NR_SYSCALL_BASE+ 4)
+#define __NR_open (__NR_SYSCALL_BASE+ 5)
+#define __NR_close (__NR_SYSCALL_BASE+ 6)
+ /* 7 was sys_waitpid */
+#define __NR_creat (__NR_SYSCALL_BASE+ 8)
+#define __NR_link (__NR_SYSCALL_BASE+ 9)
+#define __NR_unlink (__NR_SYSCALL_BASE+ 10)
+#define __NR_execve (__NR_SYSCALL_BASE+ 11)
+#define __NR_chdir (__NR_SYSCALL_BASE+ 12)
+#define __NR_time (__NR_SYSCALL_BASE+ 13)
+#define __NR_mknod (__NR_SYSCALL_BASE+ 14)
+#define __NR_chmod (__NR_SYSCALL_BASE+ 15)
+#define __NR_lchown (__NR_SYSCALL_BASE+ 16)
+ /* 17 was sys_break */
+ /* 18 was sys_stat */
+#define __NR_lseek (__NR_SYSCALL_BASE+ 19)
+#define __NR_getpid (__NR_SYSCALL_BASE+ 20)
+#define __NR_mount (__NR_SYSCALL_BASE+ 21)
+#define __NR_umount (__NR_SYSCALL_BASE+ 22)
+#define __NR_setuid (__NR_SYSCALL_BASE+ 23)
+#define __NR_getuid (__NR_SYSCALL_BASE+ 24)
+#define __NR_stime (__NR_SYSCALL_BASE+ 25)
+#define __NR_ptrace (__NR_SYSCALL_BASE+ 26)
+#define __NR_alarm (__NR_SYSCALL_BASE+ 27)
+ /* 28 was sys_fstat */
+#define __NR_pause (__NR_SYSCALL_BASE+ 29)
+#define __NR_utime (__NR_SYSCALL_BASE+ 30)
+ /* 31 was sys_stty */
+ /* 32 was sys_gtty */
+#define __NR_access (__NR_SYSCALL_BASE+ 33)
+#define __NR_nice (__NR_SYSCALL_BASE+ 34)
+ /* 35 was sys_ftime */
+#define __NR_sync (__NR_SYSCALL_BASE+ 36)
+#define __NR_kill (__NR_SYSCALL_BASE+ 37)
+#define __NR_rename (__NR_SYSCALL_BASE+ 38)
+#define __NR_mkdir (__NR_SYSCALL_BASE+ 39)
+#define __NR_rmdir (__NR_SYSCALL_BASE+ 40)
+#define __NR_dup (__NR_SYSCALL_BASE+ 41)
+#define __NR_pipe (__NR_SYSCALL_BASE+ 42)
+#define __NR_times (__NR_SYSCALL_BASE+ 43)
+ /* 44 was sys_prof */
+#define __NR_brk (__NR_SYSCALL_BASE+ 45)
+#define __NR_setgid (__NR_SYSCALL_BASE+ 46)
+#define __NR_getgid (__NR_SYSCALL_BASE+ 47)
+ /* 48 was sys_signal */
+#define __NR_geteuid (__NR_SYSCALL_BASE+ 49)
+#define __NR_getegid (__NR_SYSCALL_BASE+ 50)
+#define __NR_acct (__NR_SYSCALL_BASE+ 51)
+#define __NR_umount2 (__NR_SYSCALL_BASE+ 52)
+ /* 53 was sys_lock */
+#define __NR_ioctl (__NR_SYSCALL_BASE+ 54)
+#define __NR_fcntl (__NR_SYSCALL_BASE+ 55)
+ /* 56 was sys_mpx */
+#define __NR_setpgid (__NR_SYSCALL_BASE+ 57)
+ /* 58 was sys_ulimit */
+ /* 59 was sys_olduname */
+#define __NR_umask (__NR_SYSCALL_BASE+ 60)
+#define __NR_chroot (__NR_SYSCALL_BASE+ 61)
+#define __NR_ustat (__NR_SYSCALL_BASE+ 62)
+#define __NR_dup2 (__NR_SYSCALL_BASE+ 63)
+#define __NR_getppid (__NR_SYSCALL_BASE+ 64)
+#define __NR_getpgrp (__NR_SYSCALL_BASE+ 65)
+#define __NR_setsid (__NR_SYSCALL_BASE+ 66)
+#define __NR_sigaction (__NR_SYSCALL_BASE+ 67)
+ /* 68 was sys_sgetmask */
+ /* 69 was sys_ssetmask */
+#define __NR_setreuid (__NR_SYSCALL_BASE+ 70)
+#define __NR_setregid (__NR_SYSCALL_BASE+ 71)
+#define __NR_sigsuspend (__NR_SYSCALL_BASE+ 72)
+#define __NR_sigpending (__NR_SYSCALL_BASE+ 73)
+#define __NR_sethostname (__NR_SYSCALL_BASE+ 74)
+#define __NR_setrlimit (__NR_SYSCALL_BASE+ 75)
+#define __NR_getrlimit (__NR_SYSCALL_BASE+ 76) /* Back compat 2GB limited rlimit */
+#define __NR_getrusage (__NR_SYSCALL_BASE+ 77)
+#define __NR_gettimeofday (__NR_SYSCALL_BASE+ 78)
+#define __NR_settimeofday (__NR_SYSCALL_BASE+ 79)
+#define __NR_getgroups (__NR_SYSCALL_BASE+ 80)
+#define __NR_setgroups (__NR_SYSCALL_BASE+ 81)
+#define __NR_select (__NR_SYSCALL_BASE+ 82)
+#define __NR_symlink (__NR_SYSCALL_BASE+ 83)
+ /* 84 was sys_lstat */
+#define __NR_readlink (__NR_SYSCALL_BASE+ 85)
+#define __NR_uselib (__NR_SYSCALL_BASE+ 86)
+#define __NR_swapon (__NR_SYSCALL_BASE+ 87)
+#define __NR_reboot (__NR_SYSCALL_BASE+ 88)
+#define __NR_readdir (__NR_SYSCALL_BASE+ 89)
+#define __NR_mmap (__NR_SYSCALL_BASE+ 90)
+#define __NR_munmap (__NR_SYSCALL_BASE+ 91)
+#define __NR_truncate (__NR_SYSCALL_BASE+ 92)
+#define __NR_ftruncate (__NR_SYSCALL_BASE+ 93)
+#define __NR_fchmod (__NR_SYSCALL_BASE+ 94)
+#define __NR_fchown (__NR_SYSCALL_BASE+ 95)
+#define __NR_getpriority (__NR_SYSCALL_BASE+ 96)
+#define __NR_setpriority (__NR_SYSCALL_BASE+ 97)
+ /* 98 was sys_profil */
+#define __NR_statfs (__NR_SYSCALL_BASE+ 99)
+#define __NR_fstatfs (__NR_SYSCALL_BASE+100)
+ /* 101 was sys_ioperm */
+#define __NR_socketcall (__NR_SYSCALL_BASE+102)
+#define __NR_syslog (__NR_SYSCALL_BASE+103)
+#define __NR_setitimer (__NR_SYSCALL_BASE+104)
+#define __NR_getitimer (__NR_SYSCALL_BASE+105)
+#define __NR_stat (__NR_SYSCALL_BASE+106)
+#define __NR_lstat (__NR_SYSCALL_BASE+107)
+#define __NR_fstat (__NR_SYSCALL_BASE+108)
+ /* 109 was sys_uname */
+ /* 110 was sys_iopl */
+#define __NR_vhangup (__NR_SYSCALL_BASE+111)
+ /* 112 was sys_idle */
+#define __NR_syscall (__NR_SYSCALL_BASE+113) /* syscall to call a syscall! */
+#define __NR_wait4 (__NR_SYSCALL_BASE+114)
+#define __NR_swapoff (__NR_SYSCALL_BASE+115)
+#define __NR_sysinfo (__NR_SYSCALL_BASE+116)
+#define __NR_ipc (__NR_SYSCALL_BASE+117)
+#define __NR_fsync (__NR_SYSCALL_BASE+118)
+#define __NR_sigreturn (__NR_SYSCALL_BASE+119)
+#define __NR_clone (__NR_SYSCALL_BASE+120)
+#define __NR_setdomainname (__NR_SYSCALL_BASE+121)
+#define __NR_uname (__NR_SYSCALL_BASE+122)
+ /* 123 was sys_modify_ldt */
+#define __NR_adjtimex (__NR_SYSCALL_BASE+124)
+#define __NR_mprotect (__NR_SYSCALL_BASE+125)
+#define __NR_sigprocmask (__NR_SYSCALL_BASE+126)
+ /* 127 was sys_create_module */
+#define __NR_init_module (__NR_SYSCALL_BASE+128)
+#define __NR_delete_module (__NR_SYSCALL_BASE+129)
+ /* 130 was sys_get_kernel_syms */
+#define __NR_quotactl (__NR_SYSCALL_BASE+131)
+#define __NR_getpgid (__NR_SYSCALL_BASE+132)
+#define __NR_fchdir (__NR_SYSCALL_BASE+133)
+#define __NR_bdflush (__NR_SYSCALL_BASE+134)
+#define __NR_sysfs (__NR_SYSCALL_BASE+135)
+#define __NR_personality (__NR_SYSCALL_BASE+136)
+ /* 137 was sys_afs_syscall */
+#define __NR_setfsuid (__NR_SYSCALL_BASE+138)
+#define __NR_setfsgid (__NR_SYSCALL_BASE+139)
+#define __NR__llseek (__NR_SYSCALL_BASE+140)
+#define __NR_getdents (__NR_SYSCALL_BASE+141)
+#define __NR__newselect (__NR_SYSCALL_BASE+142)
+#define __NR_flock (__NR_SYSCALL_BASE+143)
+#define __NR_msync (__NR_SYSCALL_BASE+144)
+#define __NR_readv (__NR_SYSCALL_BASE+145)
+#define __NR_writev (__NR_SYSCALL_BASE+146)
+#define __NR_getsid (__NR_SYSCALL_BASE+147)
+#define __NR_fdatasync (__NR_SYSCALL_BASE+148)
+#define __NR__sysctl (__NR_SYSCALL_BASE+149)
+#define __NR_mlock (__NR_SYSCALL_BASE+150)
+#define __NR_munlock (__NR_SYSCALL_BASE+151)
+#define __NR_mlockall (__NR_SYSCALL_BASE+152)
+#define __NR_munlockall (__NR_SYSCALL_BASE+153)
+#define __NR_sched_setparam (__NR_SYSCALL_BASE+154)
+#define __NR_sched_getparam (__NR_SYSCALL_BASE+155)
+#define __NR_sched_setscheduler (__NR_SYSCALL_BASE+156)
+#define __NR_sched_getscheduler (__NR_SYSCALL_BASE+157)
+#define __NR_sched_yield (__NR_SYSCALL_BASE+158)
+#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE+159)
+#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE+160)
+#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE+161)
+#define __NR_nanosleep (__NR_SYSCALL_BASE+162)
+#define __NR_mremap (__NR_SYSCALL_BASE+163)
+#define __NR_setresuid (__NR_SYSCALL_BASE+164)
+#define __NR_getresuid (__NR_SYSCALL_BASE+165)
+ /* 166 was sys_vm86 */
+ /* 167 was sys_query_module */
+#define __NR_poll (__NR_SYSCALL_BASE+168)
+#define __NR_nfsservctl (__NR_SYSCALL_BASE+169)
+#define __NR_setresgid (__NR_SYSCALL_BASE+170)
+#define __NR_getresgid (__NR_SYSCALL_BASE+171)
+#define __NR_prctl (__NR_SYSCALL_BASE+172)
+#define __NR_rt_sigreturn (__NR_SYSCALL_BASE+173)
+#define __NR_rt_sigaction (__NR_SYSCALL_BASE+174)
+#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE+175)
+#define __NR_rt_sigpending (__NR_SYSCALL_BASE+176)
+#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE+177)
+#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE+178)
+#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE+179)
+#define __NR_pread64 (__NR_SYSCALL_BASE+180)
+#define __NR_pwrite64 (__NR_SYSCALL_BASE+181)
+#define __NR_chown (__NR_SYSCALL_BASE+182)
+#define __NR_getcwd (__NR_SYSCALL_BASE+183)
+#define __NR_capget (__NR_SYSCALL_BASE+184)
+#define __NR_capset (__NR_SYSCALL_BASE+185)
+#define __NR_sigaltstack (__NR_SYSCALL_BASE+186)
+#define __NR_sendfile (__NR_SYSCALL_BASE+187)
+ /* 188 reserved */
+ /* 189 reserved */
+#define __NR_vfork (__NR_SYSCALL_BASE+190)
+#define __NR_ugetrlimit (__NR_SYSCALL_BASE+191) /* SuS compliant getrlimit */
+#define __NR_mmap2 (__NR_SYSCALL_BASE+192)
+#define __NR_truncate64 (__NR_SYSCALL_BASE+193)
+#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194)
+#define __NR_stat64 (__NR_SYSCALL_BASE+195)
+#define __NR_lstat64 (__NR_SYSCALL_BASE+196)
+#define __NR_fstat64 (__NR_SYSCALL_BASE+197)
+#define __NR_lchown32 (__NR_SYSCALL_BASE+198)
+#define __NR_getuid32 (__NR_SYSCALL_BASE+199)
+#define __NR_getgid32 (__NR_SYSCALL_BASE+200)
+#define __NR_geteuid32 (__NR_SYSCALL_BASE+201)
+#define __NR_getegid32 (__NR_SYSCALL_BASE+202)
+#define __NR_setreuid32 (__NR_SYSCALL_BASE+203)
+#define __NR_setregid32 (__NR_SYSCALL_BASE+204)
+#define __NR_getgroups32 (__NR_SYSCALL_BASE+205)
+#define __NR_setgroups32 (__NR_SYSCALL_BASE+206)
+#define __NR_fchown32 (__NR_SYSCALL_BASE+207)
+#define __NR_setresuid32 (__NR_SYSCALL_BASE+208)
+#define __NR_getresuid32 (__NR_SYSCALL_BASE+209)
+#define __NR_setresgid32 (__NR_SYSCALL_BASE+210)
+#define __NR_getresgid32 (__NR_SYSCALL_BASE+211)
+#define __NR_chown32 (__NR_SYSCALL_BASE+212)
+#define __NR_setuid32 (__NR_SYSCALL_BASE+213)
+#define __NR_setgid32 (__NR_SYSCALL_BASE+214)
+#define __NR_setfsuid32 (__NR_SYSCALL_BASE+215)
+#define __NR_setfsgid32 (__NR_SYSCALL_BASE+216)
+#define __NR_getdents64 (__NR_SYSCALL_BASE+217)
+#define __NR_pivot_root (__NR_SYSCALL_BASE+218)
+#define __NR_mincore (__NR_SYSCALL_BASE+219)
+#define __NR_madvise (__NR_SYSCALL_BASE+220)
+#define __NR_fcntl64 (__NR_SYSCALL_BASE+221)
+ /* 222 for tux */
+ /* 223 is unused */
+#define __NR_gettid (__NR_SYSCALL_BASE+224)
+#define __NR_readahead (__NR_SYSCALL_BASE+225)
+#define __NR_setxattr (__NR_SYSCALL_BASE+226)
+#define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
+#define __NR_fsetxattr (__NR_SYSCALL_BASE+228)
+#define __NR_getxattr (__NR_SYSCALL_BASE+229)
+#define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
+#define __NR_fgetxattr (__NR_SYSCALL_BASE+231)
+#define __NR_listxattr (__NR_SYSCALL_BASE+232)
+#define __NR_llistxattr (__NR_SYSCALL_BASE+233)
+#define __NR_flistxattr (__NR_SYSCALL_BASE+234)
+#define __NR_removexattr (__NR_SYSCALL_BASE+235)
+#define __NR_lremovexattr (__NR_SYSCALL_BASE+236)
+#define __NR_fremovexattr (__NR_SYSCALL_BASE+237)
+#define __NR_tkill (__NR_SYSCALL_BASE+238)
+#define __NR_sendfile64 (__NR_SYSCALL_BASE+239)
+#define __NR_futex (__NR_SYSCALL_BASE+240)
+#define __NR_sched_setaffinity (__NR_SYSCALL_BASE+241)
+#define __NR_sched_getaffinity (__NR_SYSCALL_BASE+242)
+#define __NR_io_setup (__NR_SYSCALL_BASE+243)
+#define __NR_io_destroy (__NR_SYSCALL_BASE+244)
+#define __NR_io_getevents (__NR_SYSCALL_BASE+245)
+#define __NR_io_submit (__NR_SYSCALL_BASE+246)
+#define __NR_io_cancel (__NR_SYSCALL_BASE+247)
+#define __NR_exit_group (__NR_SYSCALL_BASE+248)
+#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249)
+#define __NR_epoll_create (__NR_SYSCALL_BASE+250)
+#define __NR_epoll_ctl (__NR_SYSCALL_BASE+251)
+#define __NR_epoll_wait (__NR_SYSCALL_BASE+252)
+#define __NR_remap_file_pages (__NR_SYSCALL_BASE+253)
+ /* 254 for set_thread_area */
+ /* 255 for get_thread_area */
+#define __NR_set_tid_address (__NR_SYSCALL_BASE+256)
+#define __NR_timer_create (__NR_SYSCALL_BASE+257)
+#define __NR_timer_settime (__NR_SYSCALL_BASE+258)
+#define __NR_timer_gettime (__NR_SYSCALL_BASE+259)
+#define __NR_timer_getoverrun (__NR_SYSCALL_BASE+260)
+#define __NR_timer_delete (__NR_SYSCALL_BASE+261)
+#define __NR_clock_settime (__NR_SYSCALL_BASE+262)
+#define __NR_clock_gettime (__NR_SYSCALL_BASE+263)
+#define __NR_clock_getres (__NR_SYSCALL_BASE+264)
+#define __NR_clock_nanosleep (__NR_SYSCALL_BASE+265)
+#define __NR_statfs64 (__NR_SYSCALL_BASE+266)
+#define __NR_fstatfs64 (__NR_SYSCALL_BASE+267)
+#define __NR_tgkill (__NR_SYSCALL_BASE+268)
+#define __NR_utimes (__NR_SYSCALL_BASE+269)
+#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE+270)
+#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE+271)
+#define __NR_pciconfig_read (__NR_SYSCALL_BASE+272)
+#define __NR_pciconfig_write (__NR_SYSCALL_BASE+273)
+#define __NR_mq_open (__NR_SYSCALL_BASE+274)
+#define __NR_mq_unlink (__NR_SYSCALL_BASE+275)
+#define __NR_mq_timedsend (__NR_SYSCALL_BASE+276)
+#define __NR_mq_timedreceive (__NR_SYSCALL_BASE+277)
+#define __NR_mq_notify (__NR_SYSCALL_BASE+278)
+#define __NR_mq_getsetattr (__NR_SYSCALL_BASE+279)
+#define __NR_waitid (__NR_SYSCALL_BASE+280)
+#define __NR_socket (__NR_SYSCALL_BASE+281)
+#define __NR_bind (__NR_SYSCALL_BASE+282)
+#define __NR_connect (__NR_SYSCALL_BASE+283)
+#define __NR_listen (__NR_SYSCALL_BASE+284)
+#define __NR_accept (__NR_SYSCALL_BASE+285)
+#define __NR_getsockname (__NR_SYSCALL_BASE+286)
+#define __NR_getpeername (__NR_SYSCALL_BASE+287)
+#define __NR_socketpair (__NR_SYSCALL_BASE+288)
+#define __NR_send (__NR_SYSCALL_BASE+289)
+#define __NR_sendto (__NR_SYSCALL_BASE+290)
+#define __NR_recv (__NR_SYSCALL_BASE+291)
+#define __NR_recvfrom (__NR_SYSCALL_BASE+292)
+#define __NR_shutdown (__NR_SYSCALL_BASE+293)
+#define __NR_setsockopt (__NR_SYSCALL_BASE+294)
+#define __NR_getsockopt (__NR_SYSCALL_BASE+295)
+#define __NR_sendmsg (__NR_SYSCALL_BASE+296)
+#define __NR_recvmsg (__NR_SYSCALL_BASE+297)
+#define __NR_semop (__NR_SYSCALL_BASE+298)
+#define __NR_semget (__NR_SYSCALL_BASE+299)
+#define __NR_semctl (__NR_SYSCALL_BASE+300)
+#define __NR_msgsnd (__NR_SYSCALL_BASE+301)
+#define __NR_msgrcv (__NR_SYSCALL_BASE+302)
+#define __NR_msgget (__NR_SYSCALL_BASE+303)
+#define __NR_msgctl (__NR_SYSCALL_BASE+304)
+#define __NR_shmat (__NR_SYSCALL_BASE+305)
+#define __NR_shmdt (__NR_SYSCALL_BASE+306)
+#define __NR_shmget (__NR_SYSCALL_BASE+307)
+#define __NR_shmctl (__NR_SYSCALL_BASE+308)
+#define __NR_add_key (__NR_SYSCALL_BASE+309)
+#define __NR_request_key (__NR_SYSCALL_BASE+310)
+#define __NR_keyctl (__NR_SYSCALL_BASE+311)
+#define __NR_semtimedop (__NR_SYSCALL_BASE+312)
+#define __NR_vserver (__NR_SYSCALL_BASE+313)
+#define __NR_ioprio_set (__NR_SYSCALL_BASE+314)
+#define __NR_ioprio_get (__NR_SYSCALL_BASE+315)
+#define __NR_inotify_init (__NR_SYSCALL_BASE+316)
+#define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317)
+#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318)
+#define __NR_mbind (__NR_SYSCALL_BASE+319)
+#define __NR_get_mempolicy (__NR_SYSCALL_BASE+320)
+#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)
+#define __NR_openat (__NR_SYSCALL_BASE+322)
+#define __NR_mkdirat (__NR_SYSCALL_BASE+323)
+#define __NR_mknodat (__NR_SYSCALL_BASE+324)
+#define __NR_fchownat (__NR_SYSCALL_BASE+325)
+#define __NR_futimesat (__NR_SYSCALL_BASE+326)
+#define __NR_fstatat64 (__NR_SYSCALL_BASE+327)
+#define __NR_unlinkat (__NR_SYSCALL_BASE+328)
+#define __NR_renameat (__NR_SYSCALL_BASE+329)
+#define __NR_linkat (__NR_SYSCALL_BASE+330)
+#define __NR_symlinkat (__NR_SYSCALL_BASE+331)
+#define __NR_readlinkat (__NR_SYSCALL_BASE+332)
+#define __NR_fchmodat (__NR_SYSCALL_BASE+333)
+#define __NR_faccessat (__NR_SYSCALL_BASE+334)
+#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
+#define __NR_ppoll (__NR_SYSCALL_BASE+336)
+#define __NR_unshare (__NR_SYSCALL_BASE+337)
+#define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
+#define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
+#define __NR_splice (__NR_SYSCALL_BASE+340)
+#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE+341)
+#define __NR_sync_file_range2 __NR_arm_sync_file_range
+#define __NR_tee (__NR_SYSCALL_BASE+342)
+#define __NR_vmsplice (__NR_SYSCALL_BASE+343)
+#define __NR_move_pages (__NR_SYSCALL_BASE+344)
+#define __NR_getcpu (__NR_SYSCALL_BASE+345)
+#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
+#define __NR_kexec_load (__NR_SYSCALL_BASE+347)
+#define __NR_utimensat (__NR_SYSCALL_BASE+348)
+#define __NR_signalfd (__NR_SYSCALL_BASE+349)
+#define __NR_timerfd_create (__NR_SYSCALL_BASE+350)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#define __NR_fallocate (__NR_SYSCALL_BASE+352)
+#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353)
+#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354)
+#define __NR_signalfd4 (__NR_SYSCALL_BASE+355)
+#define __NR_eventfd2 (__NR_SYSCALL_BASE+356)
+#define __NR_epoll_create1 (__NR_SYSCALL_BASE+357)
+#define __NR_dup3 (__NR_SYSCALL_BASE+358)
+#define __NR_pipe2 (__NR_SYSCALL_BASE+359)
+#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360)
+#define __NR_preadv (__NR_SYSCALL_BASE+361)
+#define __NR_pwritev (__NR_SYSCALL_BASE+362)
+#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
+#define __NR_perf_event_open (__NR_SYSCALL_BASE+364)
+#define __NR_recvmmsg (__NR_SYSCALL_BASE+365)
+#define __NR_accept4 (__NR_SYSCALL_BASE+366)
+#define __NR_fanotify_init (__NR_SYSCALL_BASE+367)
+#define __NR_fanotify_mark (__NR_SYSCALL_BASE+368)
+#define __NR_prlimit64 (__NR_SYSCALL_BASE+369)
+
+/*
+ * The following SWIs are ARM private.
+ */
+#define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000)
+#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
+#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
+#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
+#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
+#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
+
+/*
+ * *NOTE*: This is a ghost syscall private to the kernel. Only the
+ * __kuser_cmpxchg code in entry-armv.S should be aware of its
+ * existence. Don't ever use this from user code.
+ */
+#ifdef __KERNEL__
+#define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
+#endif
+
+/*
+ * The following syscalls are obsolete and no longer available for EABI.
+ */
+#if defined(__ARM_EABI__) && !defined(__KERNEL__)
+#undef __NR_time
+#undef __NR_umount
+#undef __NR_stime
+#undef __NR_alarm
+#undef __NR_utime
+#undef __NR_getrlimit
+#undef __NR_select
+#undef __NR_readdir
+#undef __NR_mmap
+#undef __NR_socketcall
+#undef __NR_syscall
+#undef __NR_ipc
+#endif
+
+#ifdef __KERNEL__
+
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+#define __ARCH_WANT_SYS_OLD_MMAP
+#define __ARCH_WANT_SYS_OLD_SELECT
+
+#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_IPC
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_SYS_SOCKETCALL
+#endif
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+
+/*
+ * Unimplemented (or alternatively implemented) syscalls
+ */
+#define __IGNORE_fadvise64_64 1
+#define __IGNORE_migrate_pages 1
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_ARM_UNISTD_H */
diff --git a/smartt-perf/util/include/arch/ia64/include/asm/break.h b/smartt-perf/util/include/arch/ia64/include/asm/break.h
new file mode 100644
index 0000000..e90c40e
--- /dev/null
+++ b/smartt-perf/util/include/arch/ia64/include/asm/break.h
@@ -0,0 +1,32 @@
+#ifndef _ASM_IA64_BREAK_H
+#define _ASM_IA64_BREAK_H
+
+/*
+ * IA-64 Linux break numbers.
+ *
+ * Copyright (C) 1999 Hewlett-Packard Co
+ * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+/*
+ * OS-specific debug break numbers:
+ */
+#define __IA64_BREAK_KDB 0x80100
+#define __IA64_BREAK_KPROBE 0x81000 /* .. 0x81fff */
+#define __IA64_BREAK_JPROBE 0x82000
+
+/*
+ * OS-specific break numbers:
+ */
+#define __IA64_BREAK_SYSCALL 0x100000
+
+/*
+ * Xen specific break numbers:
+ */
+#define __IA64_XEN_HYPERCALL 0x1000
+/* [__IA64_XEN_HYPERPRIVOP_START, __IA64_XEN_HYPERPRIVOP_MAX] is used
+ for xen hyperprivops */
+#define __IA64_XEN_HYPERPRIVOP_START 0x1
+#define __IA64_XEN_HYPERPRIVOP_MAX 0x1a
+
+#endif /* _ASM_IA64_BREAK_H */
diff --git a/smartt-perf/util/include/arch/ia64/include/asm/unistd.h b/smartt-perf/util/include/arch/ia64/include/asm/unistd.h
new file mode 100644
index 0000000..954d398
--- /dev/null
+++ b/smartt-perf/util/include/arch/ia64/include/asm/unistd.h
@@ -0,0 +1,377 @@
+#ifndef _ASM_IA64_UNISTD_H
+#define _ASM_IA64_UNISTD_H
+
+/*
+ * IA-64 Linux syscall numbers and inline-functions.
+ *
+ * Copyright (C) 1998-2005 Hewlett-Packard Co
+ * David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+#include <asm/break.h>
+
+#define __BREAK_SYSCALL __IA64_BREAK_SYSCALL
+
+#define __NR_ni_syscall 1024
+#define __NR_exit 1025
+#define __NR_read 1026
+#define __NR_write 1027
+#define __NR_open 1028
+#define __NR_close 1029
+#define __NR_creat 1030
+#define __NR_link 1031
+#define __NR_unlink 1032
+#define __NR_execve 1033
+#define __NR_chdir 1034
+#define __NR_fchdir 1035
+#define __NR_utimes 1036
+#define __NR_mknod 1037
+#define __NR_chmod 1038
+#define __NR_chown 1039
+#define __NR_lseek 1040
+#define __NR_getpid 1041
+#define __NR_getppid 1042
+#define __NR_mount 1043
+#define __NR_umount 1044
+#define __NR_setuid 1045
+#define __NR_getuid 1046
+#define __NR_geteuid 1047
+#define __NR_ptrace 1048
+#define __NR_access 1049
+#define __NR_sync 1050
+#define __NR_fsync 1051
+#define __NR_fdatasync 1052
+#define __NR_kill 1053
+#define __NR_rename 1054
+#define __NR_mkdir 1055
+#define __NR_rmdir 1056
+#define __NR_dup 1057
+#define __NR_pipe 1058
+#define __NR_times 1059
+#define __NR_brk 1060
+#define __NR_setgid 1061
+#define __NR_getgid 1062
+#define __NR_getegid 1063
+#define __NR_acct 1064
+#define __NR_ioctl 1065
+#define __NR_fcntl 1066
+#define __NR_umask 1067
+#define __NR_chroot 1068
+#define __NR_ustat 1069
+#define __NR_dup2 1070
+#define __NR_setreuid 1071
+#define __NR_setregid 1072
+#define __NR_getresuid 1073
+#define __NR_setresuid 1074
+#define __NR_getresgid 1075
+#define __NR_setresgid 1076
+#define __NR_getgroups 1077
+#define __NR_setgroups 1078
+#define __NR_getpgid 1079
+#define __NR_setpgid 1080
+#define __NR_setsid 1081
+#define __NR_getsid 1082
+#define __NR_sethostname 1083
+#define __NR_setrlimit 1084
+#define __NR_getrlimit 1085
+#define __NR_getrusage 1086
+#define __NR_gettimeofday 1087
+#define __NR_settimeofday 1088
+#define __NR_select 1089
+#define __NR_poll 1090
+#define __NR_symlink 1091
+#define __NR_readlink 1092
+#define __NR_uselib 1093
+#define __NR_swapon 1094
+#define __NR_swapoff 1095
+#define __NR_reboot 1096
+#define __NR_truncate 1097
+#define __NR_ftruncate 1098
+#define __NR_fchmod 1099
+#define __NR_fchown 1100
+#define __NR_getpriority 1101
+#define __NR_setpriority 1102
+#define __NR_statfs 1103
+#define __NR_fstatfs 1104
+#define __NR_gettid 1105
+#define __NR_semget 1106
+#define __NR_semop 1107
+#define __NR_semctl 1108
+#define __NR_msgget 1109
+#define __NR_msgsnd 1110
+#define __NR_msgrcv 1111
+#define __NR_msgctl 1112
+#define __NR_shmget 1113
+#define __NR_shmat 1114
+#define __NR_shmdt 1115
+#define __NR_shmctl 1116
+/* also known as klogctl() in GNU libc: */
+#define __NR_syslog 1117
+#define __NR_setitimer 1118
+#define __NR_getitimer 1119
+/* 1120 was __NR_old_stat */
+/* 1121 was __NR_old_lstat */
+/* 1122 was __NR_old_fstat */
+#define __NR_vhangup 1123
+#define __NR_lchown 1124
+#define __NR_remap_file_pages 1125
+#define __NR_wait4 1126
+#define __NR_sysinfo 1127
+#define __NR_clone 1128
+#define __NR_setdomainname 1129
+#define __NR_uname 1130
+#define __NR_adjtimex 1131
+/* 1132 was __NR_create_module */
+#define __NR_init_module 1133
+#define __NR_delete_module 1134
+/* 1135 was __NR_get_kernel_syms */
+/* 1136 was __NR_query_module */
+#define __NR_quotactl 1137
+#define __NR_bdflush 1138
+#define __NR_sysfs 1139
+#define __NR_personality 1140
+#define __NR_afs_syscall 1141
+#define __NR_setfsuid 1142
+#define __NR_setfsgid 1143
+#define __NR_getdents 1144
+#define __NR_flock 1145
+#define __NR_readv 1146
+#define __NR_writev 1147
+#define __NR_pread64 1148
+#define __NR_pwrite64 1149
+#define __NR__sysctl 1150
+#define __NR_mmap 1151
+#define __NR_munmap 1152
+#define __NR_mlock 1153
+#define __NR_mlockall 1154
+#define __NR_mprotect 1155
+#define __NR_mremap 1156
+#define __NR_msync 1157
+#define __NR_munlock 1158
+#define __NR_munlockall 1159
+#define __NR_sched_getparam 1160
+#define __NR_sched_setparam 1161
+#define __NR_sched_getscheduler 1162
+#define __NR_sched_setscheduler 1163
+#define __NR_sched_yield 1164
+#define __NR_sched_get_priority_max 1165
+#define __NR_sched_get_priority_min 1166
+#define __NR_sched_rr_get_interval 1167
+#define __NR_nanosleep 1168
+#define __NR_nfsservctl 1169
+#define __NR_prctl 1170
+/* 1171 is reserved for backwards compatibility with old __NR_getpagesize */
+#define __NR_mmap2 1172
+#define __NR_pciconfig_read 1173
+#define __NR_pciconfig_write 1174
+#define __NR_perfmonctl 1175
+#define __NR_sigaltstack 1176
+#define __NR_rt_sigaction 1177
+#define __NR_rt_sigpending 1178
+#define __NR_rt_sigprocmask 1179
+#define __NR_rt_sigqueueinfo 1180
+#define __NR_rt_sigreturn 1181
+#define __NR_rt_sigsuspend 1182
+#define __NR_rt_sigtimedwait 1183
+#define __NR_getcwd 1184
+#define __NR_capget 1185
+#define __NR_capset 1186
+#define __NR_sendfile 1187
+#define __NR_getpmsg 1188
+#define __NR_putpmsg 1189
+#define __NR_socket 1190
+#define __NR_bind 1191
+#define __NR_connect 1192
+#define __NR_listen 1193
+#define __NR_accept 1194
+#define __NR_getsockname 1195
+#define __NR_getpeername 1196
+#define __NR_socketpair 1197
+#define __NR_send 1198
+#define __NR_sendto 1199
+#define __NR_recv 1200
+#define __NR_recvfrom 1201
+#define __NR_shutdown 1202
+#define __NR_setsockopt 1203
+#define __NR_getsockopt 1204
+#define __NR_sendmsg 1205
+#define __NR_recvmsg 1206
+#define __NR_pivot_root 1207
+#define __NR_mincore 1208
+#define __NR_madvise 1209
+#define __NR_stat 1210
+#define __NR_lstat 1211
+#define __NR_fstat 1212
+#define __NR_clone2 1213
+#define __NR_getdents64 1214
+#define __NR_getunwind 1215
+#define __NR_readahead 1216
+#define __NR_setxattr 1217
+#define __NR_lsetxattr 1218
+#define __NR_fsetxattr 1219
+#define __NR_getxattr 1220
+#define __NR_lgetxattr 1221
+#define __NR_fgetxattr 1222
+#define __NR_listxattr 1223
+#define __NR_llistxattr 1224
+#define __NR_flistxattr 1225
+#define __NR_removexattr 1226
+#define __NR_lremovexattr 1227
+#define __NR_fremovexattr 1228
+#define __NR_tkill 1229
+#define __NR_futex 1230
+#define __NR_sched_setaffinity 1231
+#define __NR_sched_getaffinity 1232
+#define __NR_set_tid_address 1233
+#define __NR_fadvise64 1234
+#define __NR_tgkill 1235
+#define __NR_exit_group 1236
+#define __NR_lookup_dcookie 1237
+#define __NR_io_setup 1238
+#define __NR_io_destroy 1239
+#define __NR_io_getevents 1240
+#define __NR_io_submit 1241
+#define __NR_io_cancel 1242
+#define __NR_epoll_create 1243
+#define __NR_epoll_ctl 1244
+#define __NR_epoll_wait 1245
+#define __NR_restart_syscall 1246
+#define __NR_semtimedop 1247
+#define __NR_timer_create 1248
+#define __NR_timer_settime 1249
+#define __NR_timer_gettime 1250
+#define __NR_timer_getoverrun 1251
+#define __NR_timer_delete 1252
+#define __NR_clock_settime 1253
+#define __NR_clock_gettime 1254
+#define __NR_clock_getres 1255
+#define __NR_clock_nanosleep 1256
+#define __NR_fstatfs64 1257
+#define __NR_statfs64 1258
+#define __NR_mbind 1259
+#define __NR_get_mempolicy 1260
+#define __NR_set_mempolicy 1261
+#define __NR_mq_open 1262
+#define __NR_mq_unlink 1263
+#define __NR_mq_timedsend 1264
+#define __NR_mq_timedreceive 1265
+#define __NR_mq_notify 1266
+#define __NR_mq_getsetattr 1267
+#define __NR_kexec_load 1268
+#define __NR_vserver 1269
+#define __NR_waitid 1270
+#define __NR_add_key 1271
+#define __NR_request_key 1272
+#define __NR_keyctl 1273
+#define __NR_ioprio_set 1274
+#define __NR_ioprio_get 1275
+#define __NR_move_pages 1276
+#define __NR_inotify_init 1277
+#define __NR_inotify_add_watch 1278
+#define __NR_inotify_rm_watch 1279
+#define __NR_migrate_pages 1280
+#define __NR_openat 1281
+#define __NR_mkdirat 1282
+#define __NR_mknodat 1283
+#define __NR_fchownat 1284
+#define __NR_futimesat 1285
+#define __NR_newfstatat 1286
+#define __NR_unlinkat 1287
+#define __NR_renameat 1288
+#define __NR_linkat 1289
+#define __NR_symlinkat 1290
+#define __NR_readlinkat 1291
+#define __NR_fchmodat 1292
+#define __NR_faccessat 1293
+#define __NR_pselect6 1294
+#define __NR_ppoll 1295
+#define __NR_unshare 1296
+#define __NR_splice 1297
+#define __NR_set_robust_list 1298
+#define __NR_get_robust_list 1299
+#define __NR_sync_file_range 1300
+#define __NR_tee 1301
+#define __NR_vmsplice 1302
+#define __NR_fallocate 1303
+#define __NR_getcpu 1304
+#define __NR_epoll_pwait 1305
+#define __NR_utimensat 1306
+#define __NR_signalfd 1307
+#define __NR_timerfd 1308
+#define __NR_eventfd 1309
+#define __NR_timerfd_create 1310
+#define __NR_timerfd_settime 1311
+#define __NR_timerfd_gettime 1312
+#define __NR_signalfd4 1313
+#define __NR_eventfd2 1314
+#define __NR_epoll_create1 1315
+#define __NR_dup3 1316
+#define __NR_pipe2 1317
+#define __NR_inotify_init1 1318
+#define __NR_preadv 1319
+#define __NR_pwritev 1320
+#define __NR_rt_tgsigqueueinfo 1321
+#define __NR_recvmmsg 1322
+#define __NR_fanotify_init 1323
+#define __NR_fanotify_mark 1324
+#define __NR_prlimit64 1325
+
+#ifdef __KERNEL__
+
+
+#define NR_syscalls 302 /* length of syscall table */
+
+/*
+ * The following defines stop scripts/checksyscalls.sh from complaining about
+ * unimplemented system calls. Glibc provides for each of these by using
+ * more modern equivalent system calls.
+ */
+#define __IGNORE_fork /* clone() */
+#define __IGNORE_time /* gettimeofday() */
+#define __IGNORE_alarm /* setitimer(ITIMER_REAL, ... */
+#define __IGNORE_pause /* rt_sigprocmask(), rt_sigsuspend() */
+#define __IGNORE_utime /* utimes() */
+#define __IGNORE_getpgrp /* getpgid() */
+#define __IGNORE_vfork /* clone() */
+#define __IGNORE_umount2 /* umount() */
+
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+
+#if !defined(__ASSEMBLY__) && !defined(ASSEMBLER)
+
+#include <linux/types.h>
+#include <linux/linkage.h>
+#include <linux/compiler.h>
+
+extern long __ia64_syscall (long a0, long a1, long a2, long a3, long a4, long nr);
+
+asmlinkage unsigned long sys_mmap(
+ unsigned long addr, unsigned long len,
+ int prot, int flags,
+ int fd, long off);
+asmlinkage unsigned long sys_mmap2(
+ unsigned long addr, unsigned long len,
+ int prot, int flags,
+ int fd, long pgoff);
+struct pt_regs;
+struct sigaction;
+asmlinkage long sys_ia64_pipe(void);
+asmlinkage long sys_rt_sigaction(int sig,
+ const struct sigaction __user *act,
+ struct sigaction __user *oact,
+ size_t sigsetsize);
+
+/*
+ * "Conditional" syscalls
+ *
+ * Note, this macro can only be used in the file which defines sys_ni_syscall, i.e., in
+ * kernel/sys_ni.c. This version causes warnings because the declaration isn't a
+ * proper prototype, but we can't use __typeof__ either, because not all cond_syscall()
+ * declarations have prototypes at the moment.
+ */
+#define cond_syscall(x) asmlinkage long x (void) __attribute__((weak,alias("sys_ni_syscall")))
+
+#endif /* !__ASSEMBLY__ */
+#endif /* __KERNEL__ */
+#endif /* _ASM_IA64_UNISTD_H */
diff --git a/smartt-perf/util/include/arch/powerpc/include/asm/unistd.h b/smartt-perf/util/include/arch/powerpc/include/asm/unistd.h
new file mode 100644
index 0000000..6151937
--- /dev/null
+++ b/smartt-perf/util/include/arch/powerpc/include/asm/unistd.h
@@ -0,0 +1,426 @@
+#ifndef _ASM_POWERPC_UNISTD_H_
+#define _ASM_POWERPC_UNISTD_H_
+
+/*
+ * This file contains the system call numbers.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define __NR_restart_syscall 0
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_waitpid 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_time 13
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_lchown 16
+#define __NR_break 17
+#define __NR_oldstat 18
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_setuid 23
+#define __NR_getuid 24
+#define __NR_stime 25
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_oldfstat 28
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_stty 31
+#define __NR_gtty 32
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_ftime 35
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_prof 44
+#define __NR_brk 45
+#define __NR_setgid 46
+#define __NR_getgid 47
+#define __NR_signal 48
+#define __NR_geteuid 49
+#define __NR_getegid 50
+#define __NR_acct 51
+#define __NR_umount2 52
+#define __NR_lock 53
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_mpx 56
+#define __NR_setpgid 57
+#define __NR_ulimit 58
+#define __NR_oldolduname 59
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_sgetmask 68
+#define __NR_ssetmask 69
+#define __NR_setreuid 70
+#define __NR_setregid 71
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrlimit 76
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_getgroups 80
+#define __NR_setgroups 81
+#define __NR_select 82
+#define __NR_symlink 83
+#define __NR_oldlstat 84
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_fchown 95
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_profil 98
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_ioperm 101
+#define __NR_socketcall 102
+#define __NR_syslog 103
+#define __NR_setitimer 104
+#define __NR_getitimer 105
+#define __NR_stat 106
+#define __NR_lstat 107
+#define __NR_fstat 108
+#define __NR_olduname 109
+#define __NR_iopl 110
+#define __NR_vhangup 111
+#define __NR_idle 112
+#define __NR_vm86 113
+#define __NR_wait4 114
+#define __NR_swapoff 115
+#define __NR_sysinfo 116
+#define __NR_ipc 117
+#define __NR_fsync 118
+#define __NR_sigreturn 119
+#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
+#define __NR_modify_ldt 123
+#define __NR_adjtimex 124
+#define __NR_mprotect 125
+#define __NR_sigprocmask 126
+#define __NR_create_module 127
+#define __NR_init_module 128
+#define __NR_delete_module 129
+#define __NR_get_kernel_syms 130
+#define __NR_quotactl 131
+#define __NR_getpgid 132
+#define __NR_fchdir 133
+#define __NR_bdflush 134
+#define __NR_sysfs 135
+#define __NR_personality 136
+#define __NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define __NR_setfsuid 138
+#define __NR_setfsgid 139
+#define __NR__llseek 140
+#define __NR_getdents 141
+#define __NR__newselect 142
+#define __NR_flock 143
+#define __NR_msync 144
+#define __NR_readv 145
+#define __NR_writev 146
+#define __NR_getsid 147
+#define __NR_fdatasync 148
+#define __NR__sysctl 149
+#define __NR_mlock 150
+#define __NR_munlock 151
+#define __NR_mlockall 152
+#define __NR_munlockall 153
+#define __NR_sched_setparam 154
+#define __NR_sched_getparam 155
+#define __NR_sched_setscheduler 156
+#define __NR_sched_getscheduler 157
+#define __NR_sched_yield 158
+#define __NR_sched_get_priority_max 159
+#define __NR_sched_get_priority_min 160
+#define __NR_sched_rr_get_interval 161
+#define __NR_nanosleep 162
+#define __NR_mremap 163
+#define __NR_setresuid 164
+#define __NR_getresuid 165
+#define __NR_query_module 166
+#define __NR_poll 167
+#define __NR_nfsservctl 168
+#define __NR_setresgid 169
+#define __NR_getresgid 170
+#define __NR_prctl 171
+#define __NR_rt_sigreturn 172
+#define __NR_rt_sigaction 173
+#define __NR_rt_sigprocmask 174
+#define __NR_rt_sigpending 175
+#define __NR_rt_sigtimedwait 176
+#define __NR_rt_sigqueueinfo 177
+#define __NR_rt_sigsuspend 178
+#define __NR_pread64 179
+#define __NR_pwrite64 180
+#define __NR_chown 181
+#define __NR_getcwd 182
+#define __NR_capget 183
+#define __NR_capset 184
+#define __NR_sigaltstack 185
+#define __NR_sendfile 186
+#define __NR_getpmsg 187 /* some people actually want streams */
+#define __NR_putpmsg 188 /* some people actually want streams */
+#define __NR_vfork 189
+#define __NR_ugetrlimit 190 /* SuS compliant getrlimit */
+#define __NR_readahead 191
+#ifndef __powerpc64__ /* these are 32-bit only */
+#define __NR_mmap2 192
+#define __NR_truncate64 193
+#define __NR_ftruncate64 194
+#define __NR_stat64 195
+#define __NR_lstat64 196
+#define __NR_fstat64 197
+#endif
+#define __NR_pciconfig_read 198
+#define __NR_pciconfig_write 199
+#define __NR_pciconfig_iobase 200
+#define __NR_multiplexer 201
+#define __NR_getdents64 202
+#define __NR_pivot_root 203
+#ifndef __powerpc64__
+#define __NR_fcntl64 204
+#endif
+#define __NR_madvise 205
+#define __NR_mincore 206
+#define __NR_gettid 207
+#define __NR_tkill 208
+#define __NR_setxattr 209
+#define __NR_lsetxattr 210
+#define __NR_fsetxattr 211
+#define __NR_getxattr 212
+#define __NR_lgetxattr 213
+#define __NR_fgetxattr 214
+#define __NR_listxattr 215
+#define __NR_llistxattr 216
+#define __NR_flistxattr 217
+#define __NR_removexattr 218
+#define __NR_lremovexattr 219
+#define __NR_fremovexattr 220
+#define __NR_futex 221
+#define __NR_sched_setaffinity 222
+#define __NR_sched_getaffinity 223
+/* 224 currently unused */
+#define __NR_tuxcall 225
+#ifndef __powerpc64__
+#define __NR_sendfile64 226
+#endif
+#define __NR_io_setup 227
+#define __NR_io_destroy 228
+#define __NR_io_getevents 229
+#define __NR_io_submit 230
+#define __NR_io_cancel 231
+#define __NR_set_tid_address 232
+#define __NR_fadvise64 233
+#define __NR_exit_group 234
+#define __NR_lookup_dcookie 235
+#define __NR_epoll_create 236
+#define __NR_epoll_ctl 237
+#define __NR_epoll_wait 238
+#define __NR_remap_file_pages 239
+#define __NR_timer_create 240
+#define __NR_timer_settime 241
+#define __NR_timer_gettime 242
+#define __NR_timer_getoverrun 243
+#define __NR_timer_delete 244
+#define __NR_clock_settime 245
+#define __NR_clock_gettime 246
+#define __NR_clock_getres 247
+#define __NR_clock_nanosleep 248
+#define __NR_swapcontext 249
+#define __NR_tgkill 250
+#define __NR_utimes 251
+#define __NR_statfs64 252
+#define __NR_fstatfs64 253
+#ifndef __powerpc64__
+#define __NR_fadvise64_64 254
+#endif
+#define __NR_rtas 255
+#define __NR_sys_debug_setcontext 256
+/* Number 257 is reserved for vserver */
+#define __NR_migrate_pages 258
+#define __NR_mbind 259
+#define __NR_get_mempolicy 260
+#define __NR_set_mempolicy 261
+#define __NR_mq_open 262
+#define __NR_mq_unlink 263
+#define __NR_mq_timedsend 264
+#define __NR_mq_timedreceive 265
+#define __NR_mq_notify 266
+#define __NR_mq_getsetattr 267
+#define __NR_kexec_load 268
+#define __NR_add_key 269
+#define __NR_request_key 270
+#define __NR_keyctl 271
+#define __NR_waitid 272
+#define __NR_ioprio_set 273
+#define __NR_ioprio_get 274
+#define __NR_inotify_init 275
+#define __NR_inotify_add_watch 276
+#define __NR_inotify_rm_watch 277
+#define __NR_spu_run 278
+#define __NR_spu_create 279
+#define __NR_pselect6 280
+#define __NR_ppoll 281
+#define __NR_unshare 282
+#define __NR_splice 283
+#define __NR_tee 284
+#define __NR_vmsplice 285
+#define __NR_openat 286
+#define __NR_mkdirat 287
+#define __NR_mknodat 288
+#define __NR_fchownat 289
+#define __NR_futimesat 290
+#ifdef __powerpc64__
+#define __NR_newfstatat 291
+#else
+#define __NR_fstatat64 291
+#endif
+#define __NR_unlinkat 292
+#define __NR_renameat 293
+#define __NR_linkat 294
+#define __NR_symlinkat 295
+#define __NR_readlinkat 296
+#define __NR_fchmodat 297
+#define __NR_faccessat 298
+#define __NR_get_robust_list 299
+#define __NR_set_robust_list 300
+#define __NR_move_pages 301
+#define __NR_getcpu 302
+#define __NR_epoll_pwait 303
+#define __NR_utimensat 304
+#define __NR_signalfd 305
+#define __NR_timerfd_create 306
+#define __NR_eventfd 307
+#define __NR_sync_file_range2 308
+#define __NR_fallocate 309
+#define __NR_subpage_prot 310
+#define __NR_timerfd_settime 311
+#define __NR_timerfd_gettime 312
+#define __NR_signalfd4 313
+#define __NR_eventfd2 314
+#define __NR_epoll_create1 315
+#define __NR_dup3 316
+#define __NR_pipe2 317
+#define __NR_inotify_init1 318
+#define __NR_perf_event_open 319
+#define __NR_preadv 320
+#define __NR_pwritev 321
+#define __NR_rt_tgsigqueueinfo 322
+#define __NR_fanotify_init 323
+#define __NR_fanotify_mark 324
+#define __NR_prlimit64 325
+#define __NR_socket 326
+#define __NR_bind 327
+#define __NR_connect 328
+#define __NR_listen 329
+#define __NR_accept 330
+#define __NR_getsockname 331
+#define __NR_getpeername 332
+#define __NR_socketpair 333
+#define __NR_send 334
+#define __NR_sendto 335
+#define __NR_recv 336
+#define __NR_recvfrom 337
+#define __NR_shutdown 338
+#define __NR_setsockopt 339
+#define __NR_getsockopt 340
+#define __NR_sendmsg 341
+#define __NR_recvmsg 342
+#define __NR_recvmmsg 343
+#define __NR_accept4 344
+
+#ifdef __KERNEL__
+
+#define __NR_syscalls 345
+
+#define __NR__exit __NR_exit
+#define NR_syscalls __NR_syscalls
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/linkage.h>
+
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_IPC
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_SYS_OLD_UNAME
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+#ifdef CONFIG_PPC32
+#define __ARCH_WANT_OLD_STAT
+#endif
+#ifdef CONFIG_PPC64
+#define __ARCH_WANT_COMPAT_SYS_TIME
+#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
+#define __ARCH_WANT_SYS_NEWFSTATAT
+#endif
+
+/*
+ * "Conditional" syscalls
+ */
+#define cond_syscall(x) \
+ asmlinkage long x (void) __attribute__((weak,alias("sys_ni_syscall")))
+
+#endif /* __ASSEMBLY__ */
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_POWERPC_UNISTD_H_ */
diff --git a/smartt-perf/util/include/arch/x86/include/asm/unistd.h b/smartt-perf/util/include/arch/x86/include/asm/unistd.h
new file mode 100644
index 0000000..2a58ed3
--- /dev/null
+++ b/smartt-perf/util/include/arch/x86/include/asm/unistd.h
@@ -0,0 +1,13 @@
+#ifdef __KERNEL__
+# ifdef CONFIG_X86_32
+# include "unistd_32.h"
+# else
+# include "unistd_64.h"
+# endif
+#else
+# ifdef __i386__
+# include "unistd_32.h"
+# else
+# include "unistd_64.h"
+# endif
+#endif
diff --git a/smartt-perf/util/include/arch/x86/include/asm/unistd_32.h b/smartt-perf/util/include/arch/x86/include/asm/unistd_32.h
new file mode 100644
index 0000000..b766a5e
--- /dev/null
+++ b/smartt-perf/util/include/arch/x86/include/asm/unistd_32.h
@@ -0,0 +1,393 @@
+#ifndef _ASM_X86_UNISTD_32_H
+#define _ASM_X86_UNISTD_32_H
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#define __NR_restart_syscall 0
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_waitpid 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_time 13
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_lchown 16
+#define __NR_break 17
+#define __NR_oldstat 18
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_setuid 23
+#define __NR_getuid 24
+#define __NR_stime 25
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_oldfstat 28
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_stty 31
+#define __NR_gtty 32
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_ftime 35
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_prof 44
+#define __NR_brk 45
+#define __NR_setgid 46
+#define __NR_getgid 47
+#define __NR_signal 48
+#define __NR_geteuid 49
+#define __NR_getegid 50
+#define __NR_acct 51
+#define __NR_umount2 52
+#define __NR_lock 53
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_mpx 56
+#define __NR_setpgid 57
+#define __NR_ulimit 58
+#define __NR_oldolduname 59
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_sgetmask 68
+#define __NR_ssetmask 69
+#define __NR_setreuid 70
+#define __NR_setregid 71
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_getgroups 80
+#define __NR_setgroups 81
+#define __NR_select 82
+#define __NR_symlink 83
+#define __NR_oldlstat 84
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_fchown 95
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_profil 98
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_ioperm 101
+#define __NR_socketcall 102
+#define __NR_syslog 103
+#define __NR_setitimer 104
+#define __NR_getitimer 105
+#define __NR_stat 106
+#define __NR_lstat 107
+#define __NR_fstat 108
+#define __NR_olduname 109
+#define __NR_iopl 110
+#define __NR_vhangup 111
+#define __NR_idle 112
+#define __NR_vm86old 113
+#define __NR_wait4 114
+#define __NR_swapoff 115
+#define __NR_sysinfo 116
+#define __NR_ipc 117
+#define __NR_fsync 118
+#define __NR_sigreturn 119
+#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
+#define __NR_modify_ldt 123
+#define __NR_adjtimex 124
+#define __NR_mprotect 125
+#define __NR_sigprocmask 126
+#define __NR_create_module 127
+#define __NR_init_module 128
+#define __NR_delete_module 129
+#define __NR_get_kernel_syms 130
+#define __NR_quotactl 131
+#define __NR_getpgid 132
+#define __NR_fchdir 133
+#define __NR_bdflush 134
+#define __NR_sysfs 135
+#define __NR_personality 136
+#define __NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define __NR_setfsuid 138
+#define __NR_setfsgid 139
+#define __NR__llseek 140
+#define __NR_getdents 141
+#define __NR__newselect 142
+#define __NR_flock 143
+#define __NR_msync 144
+#define __NR_readv 145
+#define __NR_writev 146
+#define __NR_getsid 147
+#define __NR_fdatasync 148
+#define __NR__sysctl 149
+#define __NR_mlock 150
+#define __NR_munlock 151
+#define __NR_mlockall 152
+#define __NR_munlockall 153
+#define __NR_sched_setparam 154
+#define __NR_sched_getparam 155
+#define __NR_sched_setscheduler 156
+#define __NR_sched_getscheduler 157
+#define __NR_sched_yield 158
+#define __NR_sched_get_priority_max 159
+#define __NR_sched_get_priority_min 160
+#define __NR_sched_rr_get_interval 161
+#define __NR_nanosleep 162
+#define __NR_mremap 163
+#define __NR_setresuid 164
+#define __NR_getresuid 165
+#define __NR_vm86 166
+#define __NR_query_module 167
+#define __NR_poll 168
+#define __NR_nfsservctl 169
+#define __NR_setresgid 170
+#define __NR_getresgid 171
+#define __NR_prctl 172
+#define __NR_rt_sigreturn 173
+#define __NR_rt_sigaction 174
+#define __NR_rt_sigprocmask 175
+#define __NR_rt_sigpending 176
+#define __NR_rt_sigtimedwait 177
+#define __NR_rt_sigqueueinfo 178
+#define __NR_rt_sigsuspend 179
+#define __NR_pread64 180
+#define __NR_pwrite64 181
+#define __NR_chown 182
+#define __NR_getcwd 183
+#define __NR_capget 184
+#define __NR_capset 185
+#define __NR_sigaltstack 186
+#define __NR_sendfile 187
+#define __NR_getpmsg 188 /* some people actually want streams */
+#define __NR_putpmsg 189 /* some people actually want streams */
+#define __NR_vfork 190
+#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */
+#define __NR_mmap2 192
+#define __NR_truncate64 193
+#define __NR_ftruncate64 194
+#define __NR_stat64 195
+#define __NR_lstat64 196
+#define __NR_fstat64 197
+#define __NR_lchown32 198
+#define __NR_getuid32 199
+#define __NR_getgid32 200
+#define __NR_geteuid32 201
+#define __NR_getegid32 202
+#define __NR_setreuid32 203
+#define __NR_setregid32 204
+#define __NR_getgroups32 205
+#define __NR_setgroups32 206
+#define __NR_fchown32 207
+#define __NR_setresuid32 208
+#define __NR_getresuid32 209
+#define __NR_setresgid32 210
+#define __NR_getresgid32 211
+#define __NR_chown32 212
+#define __NR_setuid32 213
+#define __NR_setgid32 214
+#define __NR_setfsuid32 215
+#define __NR_setfsgid32 216
+#define __NR_pivot_root 217
+#define __NR_mincore 218
+#define __NR_madvise 219
+#define __NR_madvise1 219 /* delete when C lib stub is removed */
+#define __NR_getdents64 220
+#define __NR_fcntl64 221
+/* 223 is unused */
+#define __NR_gettid 224
+#define __NR_readahead 225
+#define __NR_setxattr 226
+#define __NR_lsetxattr 227
+#define __NR_fsetxattr 228
+#define __NR_getxattr 229
+#define __NR_lgetxattr 230
+#define __NR_fgetxattr 231
+#define __NR_listxattr 232
+#define __NR_llistxattr 233
+#define __NR_flistxattr 234
+#define __NR_removexattr 235
+#define __NR_lremovexattr 236
+#define __NR_fremovexattr 237
+#define __NR_tkill 238
+#define __NR_sendfile64 239
+#define __NR_futex 240
+#define __NR_sched_setaffinity 241
+#define __NR_sched_getaffinity 242
+#define __NR_set_thread_area 243
+#define __NR_get_thread_area 244
+#define __NR_io_setup 245
+#define __NR_io_destroy 246
+#define __NR_io_getevents 247
+#define __NR_io_submit 248
+#define __NR_io_cancel 249
+#define __NR_fadvise64 250
+/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */
+#define __NR_exit_group 252
+#define __NR_lookup_dcookie 253
+#define __NR_epoll_create 254
+#define __NR_epoll_ctl 255
+#define __NR_epoll_wait 256
+#define __NR_remap_file_pages 257
+#define __NR_set_tid_address 258
+#define __NR_timer_create 259
+#define __NR_timer_settime (__NR_timer_create+1)
+#define __NR_timer_gettime (__NR_timer_create+2)
+#define __NR_timer_getoverrun (__NR_timer_create+3)
+#define __NR_timer_delete (__NR_timer_create+4)
+#define __NR_clock_settime (__NR_timer_create+5)
+#define __NR_clock_gettime (__NR_timer_create+6)
+#define __NR_clock_getres (__NR_timer_create+7)
+#define __NR_clock_nanosleep (__NR_timer_create+8)
+#define __NR_statfs64 268
+#define __NR_fstatfs64 269
+#define __NR_tgkill 270
+#define __NR_utimes 271
+#define __NR_fadvise64_64 272
+#define __NR_vserver 273
+#define __NR_mbind 274
+#define __NR_get_mempolicy 275
+#define __NR_set_mempolicy 276
+#define __NR_mq_open 277
+#define __NR_mq_unlink (__NR_mq_open+1)
+#define __NR_mq_timedsend (__NR_mq_open+2)
+#define __NR_mq_timedreceive (__NR_mq_open+3)
+#define __NR_mq_notify (__NR_mq_open+4)
+#define __NR_mq_getsetattr (__NR_mq_open+5)
+#define __NR_kexec_load 283
+#define __NR_waitid 284
+/* #define __NR_sys_setaltroot 285 */
+#define __NR_add_key 286
+#define __NR_request_key 287
+#define __NR_keyctl 288
+#define __NR_ioprio_set 289
+#define __NR_ioprio_get 290
+#define __NR_inotify_init 291
+#define __NR_inotify_add_watch 292
+#define __NR_inotify_rm_watch 293
+#define __NR_migrate_pages 294
+#define __NR_openat 295
+#define __NR_mkdirat 296
+#define __NR_mknodat 297
+#define __NR_fchownat 298
+#define __NR_futimesat 299
+#define __NR_fstatat64 300
+#define __NR_unlinkat 301
+#define __NR_renameat 302
+#define __NR_linkat 303
+#define __NR_symlinkat 304
+#define __NR_readlinkat 305
+#define __NR_fchmodat 306
+#define __NR_faccessat 307
+#define __NR_pselect6 308
+#define __NR_ppoll 309
+#define __NR_unshare 310
+#define __NR_set_robust_list 311
+#define __NR_get_robust_list 312
+#define __NR_splice 313
+#define __NR_sync_file_range 314
+#define __NR_tee 315
+#define __NR_vmsplice 316
+#define __NR_move_pages 317
+#define __NR_getcpu 318
+#define __NR_epoll_pwait 319
+#define __NR_utimensat 320
+#define __NR_signalfd 321
+#define __NR_timerfd_create 322
+#define __NR_eventfd 323
+#define __NR_fallocate 324
+#define __NR_timerfd_settime 325
+#define __NR_timerfd_gettime 326
+#define __NR_signalfd4 327
+#define __NR_eventfd2 328
+#define __NR_epoll_create1 329
+#define __NR_dup3 330
+#define __NR_pipe2 331
+#define __NR_inotify_init1 332
+#define __NR_preadv 333
+#define __NR_pwritev 334
+#define __NR_rt_tgsigqueueinfo 335
+#define __NR_perf_event_open 336
+#define __NR_recvmmsg 337
+#define __NR_fanotify_init 338
+#define __NR_fanotify_mark 339
+#define __NR_prlimit64 340
+
+#ifdef __KERNEL__
+
+#define NR_syscalls 341
+
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_OLD_STAT
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_IPC
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_SYS_OLD_UNAME
+#define __ARCH_WANT_SYS_OLD_MMAP
+#define __ARCH_WANT_SYS_OLD_SELECT
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#ifndef cond_syscall
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_X86_UNISTD_32_H */
diff --git a/smartt-perf/util/include/arch/x86/include/asm/unistd_64.h b/smartt-perf/util/include/arch/x86/include/asm/unistd_64.h
new file mode 100644
index 0000000..363e9b8
--- /dev/null
+++ b/smartt-perf/util/include/arch/x86/include/asm/unistd_64.h
@@ -0,0 +1,715 @@
+#ifndef _ASM_X86_UNISTD_64_H
+#define _ASM_X86_UNISTD_64_H
+
+#ifndef __SYSCALL
+#define __SYSCALL(a, b)
+#endif
+
+/*
+ * This file contains the system call numbers.
+ *
+ * Note: holes are not allowed.
+ */
+
+/* at least 8 syscall per cacheline */
+#define __NR_read 0
+__SYSCALL(__NR_read, sys_read)
+#define __NR_write 1
+__SYSCALL(__NR_write, sys_write)
+#define __NR_open 2
+__SYSCALL(__NR_open, sys_open)
+#define __NR_close 3
+__SYSCALL(__NR_close, sys_close)
+#define __NR_stat 4
+__SYSCALL(__NR_stat, sys_newstat)
+#define __NR_fstat 5
+__SYSCALL(__NR_fstat, sys_newfstat)
+#define __NR_lstat 6
+__SYSCALL(__NR_lstat, sys_newlstat)
+#define __NR_poll 7
+__SYSCALL(__NR_poll, sys_poll)
+
+#define __NR_lseek 8
+__SYSCALL(__NR_lseek, sys_lseek)
+#define __NR_mmap 9
+__SYSCALL(__NR_mmap, sys_mmap)
+#define __NR_mprotect 10
+__SYSCALL(__NR_mprotect, sys_mprotect)
+#define __NR_munmap 11
+__SYSCALL(__NR_munmap, sys_munmap)
+#define __NR_brk 12
+__SYSCALL(__NR_brk, sys_brk)
+#define __NR_rt_sigaction 13
+__SYSCALL(__NR_rt_sigaction, sys_rt_sigaction)
+#define __NR_rt_sigprocmask 14
+__SYSCALL(__NR_rt_sigprocmask, sys_rt_sigprocmask)
+#define __NR_rt_sigreturn 15
+__SYSCALL(__NR_rt_sigreturn, stub_rt_sigreturn)
+
+#define __NR_ioctl 16
+__SYSCALL(__NR_ioctl, sys_ioctl)
+#define __NR_pread64 17
+__SYSCALL(__NR_pread64, sys_pread64)
+#define __NR_pwrite64 18
+__SYSCALL(__NR_pwrite64, sys_pwrite64)
+#define __NR_readv 19
+__SYSCALL(__NR_readv, sys_readv)
+#define __NR_writev 20
+__SYSCALL(__NR_writev, sys_writev)
+#define __NR_access 21
+__SYSCALL(__NR_access, sys_access)
+#define __NR_pipe 22
+__SYSCALL(__NR_pipe, sys_pipe)
+#define __NR_select 23
+__SYSCALL(__NR_select, sys_select)
+
+#define __NR_sched_yield 24
+__SYSCALL(__NR_sched_yield, sys_sched_yield)
+#define __NR_mremap 25
+__SYSCALL(__NR_mremap, sys_mremap)
+#define __NR_msync 26
+__SYSCALL(__NR_msync, sys_msync)
+#define __NR_mincore 27
+__SYSCALL(__NR_mincore, sys_mincore)
+#define __NR_madvise 28
+__SYSCALL(__NR_madvise, sys_madvise)
+#define __NR_shmget 29
+__SYSCALL(__NR_shmget, sys_shmget)
+#define __NR_shmat 30
+__SYSCALL(__NR_shmat, sys_shmat)
+#define __NR_shmctl 31
+__SYSCALL(__NR_shmctl, sys_shmctl)
+
+#define __NR_dup 32
+__SYSCALL(__NR_dup, sys_dup)
+#define __NR_dup2 33
+__SYSCALL(__NR_dup2, sys_dup2)
+#define __NR_pause 34
+__SYSCALL(__NR_pause, sys_pause)
+#define __NR_nanosleep 35
+__SYSCALL(__NR_nanosleep, sys_nanosleep)
+#define __NR_getitimer 36
+__SYSCALL(__NR_getitimer, sys_getitimer)
+#define __NR_alarm 37
+__SYSCALL(__NR_alarm, sys_alarm)
+#define __NR_setitimer 38
+__SYSCALL(__NR_setitimer, sys_setitimer)
+#define __NR_getpid 39
+__SYSCALL(__NR_getpid, sys_getpid)
+
+#define __NR_sendfile 40
+__SYSCALL(__NR_sendfile, sys_sendfile64)
+#define __NR_socket 41
+__SYSCALL(__NR_socket, sys_socket)
+#define __NR_connect 42
+__SYSCALL(__NR_connect, sys_connect)
+#define __NR_accept 43
+__SYSCALL(__NR_accept, sys_accept)
+#define __NR_sendto 44
+__SYSCALL(__NR_sendto, sys_sendto)
+#define __NR_recvfrom 45
+__SYSCALL(__NR_recvfrom, sys_recvfrom)
+#define __NR_sendmsg 46
+__SYSCALL(__NR_sendmsg, sys_sendmsg)
+#define __NR_recvmsg 47
+__SYSCALL(__NR_recvmsg, sys_recvmsg)
+
+#define __NR_shutdown 48
+__SYSCALL(__NR_shutdown, sys_shutdown)
+#define __NR_bind 49
+__SYSCALL(__NR_bind, sys_bind)
+#define __NR_listen 50
+__SYSCALL(__NR_listen, sys_listen)
+#define __NR_getsockname 51
+__SYSCALL(__NR_getsockname, sys_getsockname)
+#define __NR_getpeername 52
+__SYSCALL(__NR_getpeername, sys_getpeername)
+#define __NR_socketpair 53
+__SYSCALL(__NR_socketpair, sys_socketpair)
+#define __NR_setsockopt 54
+__SYSCALL(__NR_setsockopt, sys_setsockopt)
+#define __NR_getsockopt 55
+__SYSCALL(__NR_getsockopt, sys_getsockopt)
+
+#define __NR_clone 56
+__SYSCALL(__NR_clone, stub_clone)
+#define __NR_fork 57
+__SYSCALL(__NR_fork, stub_fork)
+#define __NR_vfork 58
+__SYSCALL(__NR_vfork, stub_vfork)
+#define __NR_execve 59
+__SYSCALL(__NR_execve, stub_execve)
+#define __NR_exit 60
+__SYSCALL(__NR_exit, sys_exit)
+#define __NR_wait4 61
+__SYSCALL(__NR_wait4, sys_wait4)
+#define __NR_kill 62
+__SYSCALL(__NR_kill, sys_kill)
+#define __NR_uname 63
+__SYSCALL(__NR_uname, sys_newuname)
+
+#define __NR_semget 64
+__SYSCALL(__NR_semget, sys_semget)
+#define __NR_semop 65
+__SYSCALL(__NR_semop, sys_semop)
+#define __NR_semctl 66
+__SYSCALL(__NR_semctl, sys_semctl)
+#define __NR_shmdt 67
+__SYSCALL(__NR_shmdt, sys_shmdt)
+#define __NR_msgget 68
+__SYSCALL(__NR_msgget, sys_msgget)
+#define __NR_msgsnd 69
+__SYSCALL(__NR_msgsnd, sys_msgsnd)
+#define __NR_msgrcv 70
+__SYSCALL(__NR_msgrcv, sys_msgrcv)
+#define __NR_msgctl 71
+__SYSCALL(__NR_msgctl, sys_msgctl)
+
+#define __NR_fcntl 72
+__SYSCALL(__NR_fcntl, sys_fcntl)
+#define __NR_flock 73
+__SYSCALL(__NR_flock, sys_flock)
+#define __NR_fsync 74
+__SYSCALL(__NR_fsync, sys_fsync)
+#define __NR_fdatasync 75
+__SYSCALL(__NR_fdatasync, sys_fdatasync)
+#define __NR_truncate 76
+__SYSCALL(__NR_truncate, sys_truncate)
+#define __NR_ftruncate 77
+__SYSCALL(__NR_ftruncate, sys_ftruncate)
+#define __NR_getdents 78
+__SYSCALL(__NR_getdents, sys_getdents)
+#define __NR_getcwd 79
+__SYSCALL(__NR_getcwd, sys_getcwd)
+
+#define __NR_chdir 80
+__SYSCALL(__NR_chdir, sys_chdir)
+#define __NR_fchdir 81
+__SYSCALL(__NR_fchdir, sys_fchdir)
+#define __NR_rename 82
+__SYSCALL(__NR_rename, sys_rename)
+#define __NR_mkdir 83
+__SYSCALL(__NR_mkdir, sys_mkdir)
+#define __NR_rmdir 84
+__SYSCALL(__NR_rmdir, sys_rmdir)
+#define __NR_creat 85
+__SYSCALL(__NR_creat, sys_creat)
+#define __NR_link 86
+__SYSCALL(__NR_link, sys_link)
+#define __NR_unlink 87
+__SYSCALL(__NR_unlink, sys_unlink)
+
+#define __NR_symlink 88
+__SYSCALL(__NR_symlink, sys_symlink)
+#define __NR_readlink 89
+__SYSCALL(__NR_readlink, sys_readlink)
+#define __NR_chmod 90
+__SYSCALL(__NR_chmod, sys_chmod)
+#define __NR_fchmod 91
+__SYSCALL(__NR_fchmod, sys_fchmod)
+#define __NR_chown 92
+__SYSCALL(__NR_chown, sys_chown)
+#define __NR_fchown 93
+__SYSCALL(__NR_fchown, sys_fchown)
+#define __NR_lchown 94
+__SYSCALL(__NR_lchown, sys_lchown)
+#define __NR_umask 95
+__SYSCALL(__NR_umask, sys_umask)
+
+#define __NR_gettimeofday 96
+__SYSCALL(__NR_gettimeofday, sys_gettimeofday)
+#define __NR_getrlimit 97
+__SYSCALL(__NR_getrlimit, sys_getrlimit)
+#define __NR_getrusage 98
+__SYSCALL(__NR_getrusage, sys_getrusage)
+#define __NR_sysinfo 99
+__SYSCALL(__NR_sysinfo, sys_sysinfo)
+#define __NR_times 100
+__SYSCALL(__NR_times, sys_times)
+#define __NR_ptrace 101
+__SYSCALL(__NR_ptrace, sys_ptrace)
+#define __NR_getuid 102
+__SYSCALL(__NR_getuid, sys_getuid)
+#define __NR_syslog 103
+__SYSCALL(__NR_syslog, sys_syslog)
+
+/* at the very end the stuff that never runs during the benchmarks */
+#define __NR_getgid 104
+__SYSCALL(__NR_getgid, sys_getgid)
+#define __NR_setuid 105
+__SYSCALL(__NR_setuid, sys_setuid)
+#define __NR_setgid 106
+__SYSCALL(__NR_setgid, sys_setgid)
+#define __NR_geteuid 107
+__SYSCALL(__NR_geteuid, sys_geteuid)
+#define __NR_getegid 108
+__SYSCALL(__NR_getegid, sys_getegid)
+#define __NR_setpgid 109
+__SYSCALL(__NR_setpgid, sys_setpgid)
+#define __NR_getppid 110
+__SYSCALL(__NR_getppid, sys_getppid)
+#define __NR_getpgrp 111
+__SYSCALL(__NR_getpgrp, sys_getpgrp)
+
+#define __NR_setsid 112
+__SYSCALL(__NR_setsid, sys_setsid)
+#define __NR_setreuid 113
+__SYSCALL(__NR_setreuid, sys_setreuid)
+#define __NR_setregid 114
+__SYSCALL(__NR_setregid, sys_setregid)
+#define __NR_getgroups 115
+__SYSCALL(__NR_getgroups, sys_getgroups)
+#define __NR_setgroups 116
+__SYSCALL(__NR_setgroups, sys_setgroups)
+#define __NR_setresuid 117
+__SYSCALL(__NR_setresuid, sys_setresuid)
+#define __NR_getresuid 118
+__SYSCALL(__NR_getresuid, sys_getresuid)
+#define __NR_setresgid 119
+__SYSCALL(__NR_setresgid, sys_setresgid)
+
+#define __NR_getresgid 120
+__SYSCALL(__NR_getresgid, sys_getresgid)
+#define __NR_getpgid 121
+__SYSCALL(__NR_getpgid, sys_getpgid)
+#define __NR_setfsuid 122
+__SYSCALL(__NR_setfsuid, sys_setfsuid)
+#define __NR_setfsgid 123
+__SYSCALL(__NR_setfsgid, sys_setfsgid)
+#define __NR_getsid 124
+__SYSCALL(__NR_getsid, sys_getsid)
+#define __NR_capget 125
+__SYSCALL(__NR_capget, sys_capget)
+#define __NR_capset 126
+__SYSCALL(__NR_capset, sys_capset)
+
+#define __NR_rt_sigpending 127
+__SYSCALL(__NR_rt_sigpending, sys_rt_sigpending)
+#define __NR_rt_sigtimedwait 128
+__SYSCALL(__NR_rt_sigtimedwait, sys_rt_sigtimedwait)
+#define __NR_rt_sigqueueinfo 129
+__SYSCALL(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo)
+#define __NR_rt_sigsuspend 130
+__SYSCALL(__NR_rt_sigsuspend, sys_rt_sigsuspend)
+#define __NR_sigaltstack 131
+__SYSCALL(__NR_sigaltstack, stub_sigaltstack)
+#define __NR_utime 132
+__SYSCALL(__NR_utime, sys_utime)
+#define __NR_mknod 133
+__SYSCALL(__NR_mknod, sys_mknod)
+
+/* Only needed for a.out */
+#define __NR_uselib 134
+__SYSCALL(__NR_uselib, sys_ni_syscall)
+#define __NR_personality 135
+__SYSCALL(__NR_personality, sys_personality)
+
+#define __NR_ustat 136
+__SYSCALL(__NR_ustat, sys_ustat)
+#define __NR_statfs 137
+__SYSCALL(__NR_statfs, sys_statfs)
+#define __NR_fstatfs 138
+__SYSCALL(__NR_fstatfs, sys_fstatfs)
+#define __NR_sysfs 139
+__SYSCALL(__NR_sysfs, sys_sysfs)
+
+#define __NR_getpriority 140
+__SYSCALL(__NR_getpriority, sys_getpriority)
+#define __NR_setpriority 141
+__SYSCALL(__NR_setpriority, sys_setpriority)
+#define __NR_sched_setparam 142
+__SYSCALL(__NR_sched_setparam, sys_sched_setparam)
+#define __NR_sched_getparam 143
+__SYSCALL(__NR_sched_getparam, sys_sched_getparam)
+#define __NR_sched_setscheduler 144
+__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler)
+#define __NR_sched_getscheduler 145
+__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler)
+#define __NR_sched_get_priority_max 146
+__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max)
+#define __NR_sched_get_priority_min 147
+__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min)
+#define __NR_sched_rr_get_interval 148
+__SYSCALL(__NR_sched_rr_get_interval, sys_sched_rr_get_interval)
+
+#define __NR_mlock 149
+__SYSCALL(__NR_mlock, sys_mlock)
+#define __NR_munlock 150
+__SYSCALL(__NR_munlock, sys_munlock)
+#define __NR_mlockall 151
+__SYSCALL(__NR_mlockall, sys_mlockall)
+#define __NR_munlockall 152
+__SYSCALL(__NR_munlockall, sys_munlockall)
+
+#define __NR_vhangup 153
+__SYSCALL(__NR_vhangup, sys_vhangup)
+
+#define __NR_modify_ldt 154
+__SYSCALL(__NR_modify_ldt, sys_modify_ldt)
+
+#define __NR_pivot_root 155
+__SYSCALL(__NR_pivot_root, sys_pivot_root)
+
+#define __NR__sysctl 156
+__SYSCALL(__NR__sysctl, sys_sysctl)
+
+#define __NR_prctl 157
+__SYSCALL(__NR_prctl, sys_prctl)
+#define __NR_arch_prctl 158
+__SYSCALL(__NR_arch_prctl, sys_arch_prctl)
+
+#define __NR_adjtimex 159
+__SYSCALL(__NR_adjtimex, sys_adjtimex)
+
+#define __NR_setrlimit 160
+__SYSCALL(__NR_setrlimit, sys_setrlimit)
+
+#define __NR_chroot 161
+__SYSCALL(__NR_chroot, sys_chroot)
+
+#define __NR_sync 162
+__SYSCALL(__NR_sync, sys_sync)
+
+#define __NR_acct 163
+__SYSCALL(__NR_acct, sys_acct)
+
+#define __NR_settimeofday 164
+__SYSCALL(__NR_settimeofday, sys_settimeofday)
+
+#define __NR_mount 165
+__SYSCALL(__NR_mount, sys_mount)
+#define __NR_umount2 166
+__SYSCALL(__NR_umount2, sys_umount)
+
+#define __NR_swapon 167
+__SYSCALL(__NR_swapon, sys_swapon)
+#define __NR_swapoff 168
+__SYSCALL(__NR_swapoff, sys_swapoff)
+
+#define __NR_reboot 169
+__SYSCALL(__NR_reboot, sys_reboot)
+
+#define __NR_sethostname 170
+__SYSCALL(__NR_sethostname, sys_sethostname)
+#define __NR_setdomainname 171
+__SYSCALL(__NR_setdomainname, sys_setdomainname)
+
+#define __NR_iopl 172
+__SYSCALL(__NR_iopl, stub_iopl)
+#define __NR_ioperm 173
+__SYSCALL(__NR_ioperm, sys_ioperm)
+
+#define __NR_create_module 174
+__SYSCALL(__NR_create_module, sys_ni_syscall)
+#define __NR_init_module 175
+__SYSCALL(__NR_init_module, sys_init_module)
+#define __NR_delete_module 176
+__SYSCALL(__NR_delete_module, sys_delete_module)
+#define __NR_get_kernel_syms 177
+__SYSCALL(__NR_get_kernel_syms, sys_ni_syscall)
+#define __NR_query_module 178
+__SYSCALL(__NR_query_module, sys_ni_syscall)
+
+#define __NR_quotactl 179
+__SYSCALL(__NR_quotactl, sys_quotactl)
+
+#define __NR_nfsservctl 180
+__SYSCALL(__NR_nfsservctl, sys_nfsservctl)
+
+/* reserved for LiS/STREAMS */
+#define __NR_getpmsg 181
+__SYSCALL(__NR_getpmsg, sys_ni_syscall)
+#define __NR_putpmsg 182
+__SYSCALL(__NR_putpmsg, sys_ni_syscall)
+
+/* reserved for AFS */
+#define __NR_afs_syscall 183
+__SYSCALL(__NR_afs_syscall, sys_ni_syscall)
+
+/* reserved for tux */
+#define __NR_tuxcall 184
+__SYSCALL(__NR_tuxcall, sys_ni_syscall)
+
+#define __NR_security 185
+__SYSCALL(__NR_security, sys_ni_syscall)
+
+#define __NR_gettid 186
+__SYSCALL(__NR_gettid, sys_gettid)
+
+#define __NR_readahead 187
+__SYSCALL(__NR_readahead, sys_readahead)
+#define __NR_setxattr 188
+__SYSCALL(__NR_setxattr, sys_setxattr)
+#define __NR_lsetxattr 189
+__SYSCALL(__NR_lsetxattr, sys_lsetxattr)
+#define __NR_fsetxattr 190
+__SYSCALL(__NR_fsetxattr, sys_fsetxattr)
+#define __NR_getxattr 191
+__SYSCALL(__NR_getxattr, sys_getxattr)
+#define __NR_lgetxattr 192
+__SYSCALL(__NR_lgetxattr, sys_lgetxattr)
+#define __NR_fgetxattr 193
+__SYSCALL(__NR_fgetxattr, sys_fgetxattr)
+#define __NR_listxattr 194
+__SYSCALL(__NR_listxattr, sys_listxattr)
+#define __NR_llistxattr 195
+__SYSCALL(__NR_llistxattr, sys_llistxattr)
+#define __NR_flistxattr 196
+__SYSCALL(__NR_flistxattr, sys_flistxattr)
+#define __NR_removexattr 197
+__SYSCALL(__NR_removexattr, sys_removexattr)
+#define __NR_lremovexattr 198
+__SYSCALL(__NR_lremovexattr, sys_lremovexattr)
+#define __NR_fremovexattr 199
+__SYSCALL(__NR_fremovexattr, sys_fremovexattr)
+#define __NR_tkill 200
+__SYSCALL(__NR_tkill, sys_tkill)
+#define __NR_time 201
+__SYSCALL(__NR_time, sys_time)
+#define __NR_futex 202
+__SYSCALL(__NR_futex, sys_futex)
+#define __NR_sched_setaffinity 203
+__SYSCALL(__NR_sched_setaffinity, sys_sched_setaffinity)
+#define __NR_sched_getaffinity 204
+__SYSCALL(__NR_sched_getaffinity, sys_sched_getaffinity)
+#define __NR_set_thread_area 205
+__SYSCALL(__NR_set_thread_area, sys_ni_syscall) /* use arch_prctl */
+#define __NR_io_setup 206
+__SYSCALL(__NR_io_setup, sys_io_setup)
+#define __NR_io_destroy 207
+__SYSCALL(__NR_io_destroy, sys_io_destroy)
+#define __NR_io_getevents 208
+__SYSCALL(__NR_io_getevents, sys_io_getevents)
+#define __NR_io_submit 209
+__SYSCALL(__NR_io_submit, sys_io_submit)
+#define __NR_io_cancel 210
+__SYSCALL(__NR_io_cancel, sys_io_cancel)
+#define __NR_get_thread_area 211
+__SYSCALL(__NR_get_thread_area, sys_ni_syscall) /* use arch_prctl */
+#define __NR_lookup_dcookie 212
+__SYSCALL(__NR_lookup_dcookie, sys_lookup_dcookie)
+#define __NR_epoll_create 213
+__SYSCALL(__NR_epoll_create, sys_epoll_create)
+#define __NR_epoll_ctl_old 214
+__SYSCALL(__NR_epoll_ctl_old, sys_ni_syscall)
+#define __NR_epoll_wait_old 215
+__SYSCALL(__NR_epoll_wait_old, sys_ni_syscall)
+#define __NR_remap_file_pages 216
+__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages)
+#define __NR_getdents64 217
+__SYSCALL(__NR_getdents64, sys_getdents64)
+#define __NR_set_tid_address 218
+__SYSCALL(__NR_set_tid_address, sys_set_tid_address)
+#define __NR_restart_syscall 219
+__SYSCALL(__NR_restart_syscall, sys_restart_syscall)
+#define __NR_semtimedop 220
+__SYSCALL(__NR_semtimedop, sys_semtimedop)
+#define __NR_fadvise64 221
+__SYSCALL(__NR_fadvise64, sys_fadvise64)
+#define __NR_timer_create 222
+__SYSCALL(__NR_timer_create, sys_timer_create)
+#define __NR_timer_settime 223
+__SYSCALL(__NR_timer_settime, sys_timer_settime)
+#define __NR_timer_gettime 224
+__SYSCALL(__NR_timer_gettime, sys_timer_gettime)
+#define __NR_timer_getoverrun 225
+__SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun)
+#define __NR_timer_delete 226
+__SYSCALL(__NR_timer_delete, sys_timer_delete)
+#define __NR_clock_settime 227
+__SYSCALL(__NR_clock_settime, sys_clock_settime)
+#define __NR_clock_gettime 228
+__SYSCALL(__NR_clock_gettime, sys_clock_gettime)
+#define __NR_clock_getres 229
+__SYSCALL(__NR_clock_getres, sys_clock_getres)
+#define __NR_clock_nanosleep 230
+__SYSCALL(__NR_clock_nanosleep, sys_clock_nanosleep)
+#define __NR_exit_group 231
+__SYSCALL(__NR_exit_group, sys_exit_group)
+#define __NR_epoll_wait 232
+__SYSCALL(__NR_epoll_wait, sys_epoll_wait)
+#define __NR_epoll_ctl 233
+__SYSCALL(__NR_epoll_ctl, sys_epoll_ctl)
+#define __NR_tgkill 234
+__SYSCALL(__NR_tgkill, sys_tgkill)
+#define __NR_utimes 235
+__SYSCALL(__NR_utimes, sys_utimes)
+#define __NR_vserver 236
+__SYSCALL(__NR_vserver, sys_ni_syscall)
+#define __NR_mbind 237
+__SYSCALL(__NR_mbind, sys_mbind)
+#define __NR_set_mempolicy 238
+__SYSCALL(__NR_set_mempolicy, sys_set_mempolicy)
+#define __NR_get_mempolicy 239
+__SYSCALL(__NR_get_mempolicy, sys_get_mempolicy)
+#define __NR_mq_open 240
+__SYSCALL(__NR_mq_open, sys_mq_open)
+#define __NR_mq_unlink 241
+__SYSCALL(__NR_mq_unlink, sys_mq_unlink)
+#define __NR_mq_timedsend 242
+__SYSCALL(__NR_mq_timedsend, sys_mq_timedsend)
+#define __NR_mq_timedreceive 243
+__SYSCALL(__NR_mq_timedreceive, sys_mq_timedreceive)
+#define __NR_mq_notify 244
+__SYSCALL(__NR_mq_notify, sys_mq_notify)
+#define __NR_mq_getsetattr 245
+__SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr)
+#define __NR_kexec_load 246
+__SYSCALL(__NR_kexec_load, sys_kexec_load)
+#define __NR_waitid 247
+__SYSCALL(__NR_waitid, sys_waitid)
+#define __NR_add_key 248
+__SYSCALL(__NR_add_key, sys_add_key)
+#define __NR_request_key 249
+__SYSCALL(__NR_request_key, sys_request_key)
+#define __NR_keyctl 250
+__SYSCALL(__NR_keyctl, sys_keyctl)
+#define __NR_ioprio_set 251
+__SYSCALL(__NR_ioprio_set, sys_ioprio_set)
+#define __NR_ioprio_get 252
+__SYSCALL(__NR_ioprio_get, sys_ioprio_get)
+#define __NR_inotify_init 253
+__SYSCALL(__NR_inotify_init, sys_inotify_init)
+#define __NR_inotify_add_watch 254
+__SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch)
+#define __NR_inotify_rm_watch 255
+__SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch)
+#define __NR_migrate_pages 256
+__SYSCALL(__NR_migrate_pages, sys_migrate_pages)
+#define __NR_openat 257
+__SYSCALL(__NR_openat, sys_openat)
+#define __NR_mkdirat 258
+__SYSCALL(__NR_mkdirat, sys_mkdirat)
+#define __NR_mknodat 259
+__SYSCALL(__NR_mknodat, sys_mknodat)
+#define __NR_fchownat 260
+__SYSCALL(__NR_fchownat, sys_fchownat)
+#define __NR_futimesat 261
+__SYSCALL(__NR_futimesat, sys_futimesat)
+#define __NR_newfstatat 262
+__SYSCALL(__NR_newfstatat, sys_newfstatat)
+#define __NR_unlinkat 263
+__SYSCALL(__NR_unlinkat, sys_unlinkat)
+#define __NR_renameat 264
+__SYSCALL(__NR_renameat, sys_renameat)
+#define __NR_linkat 265
+__SYSCALL(__NR_linkat, sys_linkat)
+#define __NR_symlinkat 266
+__SYSCALL(__NR_symlinkat, sys_symlinkat)
+#define __NR_readlinkat 267
+__SYSCALL(__NR_readlinkat, sys_readlinkat)
+#define __NR_fchmodat 268
+__SYSCALL(__NR_fchmodat, sys_fchmodat)
+#define __NR_faccessat 269
+__SYSCALL(__NR_faccessat, sys_faccessat)
+#define __NR_pselect6 270
+__SYSCALL(__NR_pselect6, sys_pselect6)
+#define __NR_ppoll 271
+__SYSCALL(__NR_ppoll, sys_ppoll)
+#define __NR_unshare 272
+__SYSCALL(__NR_unshare, sys_unshare)
+#define __NR_set_robust_list 273
+__SYSCALL(__NR_set_robust_list, sys_set_robust_list)
+#define __NR_get_robust_list 274
+__SYSCALL(__NR_get_robust_list, sys_get_robust_list)
+#define __NR_splice 275
+__SYSCALL(__NR_splice, sys_splice)
+#define __NR_tee 276
+__SYSCALL(__NR_tee, sys_tee)
+#define __NR_sync_file_range 277
+__SYSCALL(__NR_sync_file_range, sys_sync_file_range)
+#define __NR_vmsplice 278
+__SYSCALL(__NR_vmsplice, sys_vmsplice)
+#define __NR_move_pages 279
+__SYSCALL(__NR_move_pages, sys_move_pages)
+#define __NR_utimensat 280
+__SYSCALL(__NR_utimensat, sys_utimensat)
+#define __IGNORE_getcpu /* implemented as a vsyscall */
+#define __NR_epoll_pwait 281
+__SYSCALL(__NR_epoll_pwait, sys_epoll_pwait)
+#define __NR_signalfd 282
+__SYSCALL(__NR_signalfd, sys_signalfd)
+#define __NR_timerfd_create 283
+__SYSCALL(__NR_timerfd_create, sys_timerfd_create)
+#define __NR_eventfd 284
+__SYSCALL(__NR_eventfd, sys_eventfd)
+#define __NR_fallocate 285
+__SYSCALL(__NR_fallocate, sys_fallocate)
+#define __NR_timerfd_settime 286
+__SYSCALL(__NR_timerfd_settime, sys_timerfd_settime)
+#define __NR_timerfd_gettime 287
+__SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime)
+#define __NR_accept4 288
+__SYSCALL(__NR_accept4, sys_accept4)
+#define __NR_signalfd4 289
+__SYSCALL(__NR_signalfd4, sys_signalfd4)
+#define __NR_eventfd2 290
+__SYSCALL(__NR_eventfd2, sys_eventfd2)
+#define __NR_epoll_create1 291
+__SYSCALL(__NR_epoll_create1, sys_epoll_create1)
+#define __NR_dup3 292
+__SYSCALL(__NR_dup3, sys_dup3)
+#define __NR_pipe2 293
+__SYSCALL(__NR_pipe2, sys_pipe2)
+#define __NR_inotify_init1 294
+__SYSCALL(__NR_inotify_init1, sys_inotify_init1)
+#define __NR_preadv 295
+__SYSCALL(__NR_preadv, sys_preadv)
+#define __NR_pwritev 296
+__SYSCALL(__NR_pwritev, sys_pwritev)
+#define __NR_rt_tgsigqueueinfo 297
+__SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo)
+#define __NR_perf_event_open 298
+__SYSCALL(__NR_perf_event_open, sys_perf_event_open)
+#define __NR_recvmmsg 299
+__SYSCALL(__NR_recvmmsg, sys_recvmmsg)
+#define __NR_fanotify_init 300
+__SYSCALL(__NR_fanotify_init, sys_fanotify_init)
+#define __NR_fanotify_mark 301
+__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark)
+#define __NR_prlimit64 302
+__SYSCALL(__NR_prlimit64, sys_prlimit64)
+
+#ifndef __NO_STUBS
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_OLD_STAT
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_SYS_OLD_UNAME
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_COMPAT_SYS_TIME
+#endif /* __NO_STUBS */
+
+#ifdef __KERNEL__
+
+#ifndef COMPILE_OFFSETS
+#include <asm/asm-offsets.h>
+#define NR_syscalls (__NR_syscall_max + 1)
+#endif
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_X86_UNISTD_64_H */
diff --git a/smartt-perf/util/include/arch/x86/lib/memcpy_64.S b/smartt-perf/util/include/arch/x86/lib/memcpy_64.S
new file mode 100644
index 0000000..75ef61e
--- /dev/null
+++ b/smartt-perf/util/include/arch/x86/lib/memcpy_64.S
@@ -0,0 +1,191 @@
+/* Copyright 2002 Andi Kleen */
+
+#include <linux/linkage.h>
+
+#include <asm/cpufeature.h>
+#include <asm/dwarf2.h>
+
+/*
+ * memcpy - Copy a memory block.
+ *
+ * Input:
+ * rdi destination
+ * rsi source
+ * rdx count
+ *
+ * Output:
+ * rax original destination
+ */
+
+/*
+ * memcpy_c() - fast string ops (REP MOVSQ) based variant.
+ *
+ * This gets patched over the unrolled variant (below) via the
+ * alternative instructions framework:
+ */
+ .section .altinstr_replacement, "ax", @progbits
+.Lmemcpy_c:
+ movq %rdi, %rax
+
+ movl %edx, %ecx
+ shrl $3, %ecx
+ andl $7, %edx
+ rep movsq
+ movl %edx, %ecx
+ rep movsb
+ ret
+.Lmemcpy_e:
+ .previous
+
+ENTRY(__memcpy)
+ENTRY(memcpy)
+ CFI_STARTPROC
+ movq %rdi, %rax
+
+ /*
+ * Use 32bit CMP here to avoid long NOP padding.
+ */
+ cmp $0x20, %edx
+ jb .Lhandle_tail
+
+ /*
+ * We check whether memory false dependece could occur,
+ * then jump to corresponding copy mode.
+ */
+ cmp %dil, %sil
+ jl .Lcopy_backward
+ subl $0x20, %edx
+.Lcopy_forward_loop:
+ subq $0x20, %rdx
+
+ /*
+ * Move in blocks of 4x8 bytes:
+ */
+ movq 0*8(%rsi), %r8
+ movq 1*8(%rsi), %r9
+ movq 2*8(%rsi), %r10
+ movq 3*8(%rsi), %r11
+ leaq 4*8(%rsi), %rsi
+
+ movq %r8, 0*8(%rdi)
+ movq %r9, 1*8(%rdi)
+ movq %r10, 2*8(%rdi)
+ movq %r11, 3*8(%rdi)
+ leaq 4*8(%rdi), %rdi
+ jae .Lcopy_forward_loop
+ addq $0x20, %rdx
+ jmp .Lhandle_tail
+
+.Lcopy_backward:
+ /*
+ * Calculate copy position to tail.
+ */
+ addq %rdx, %rsi
+ addq %rdx, %rdi
+ subq $0x20, %rdx
+ /*
+ * At most 3 ALU operations in one cycle,
+ * so append NOPS in the same 16bytes trunk.
+ */
+ .p2align 4
+.Lcopy_backward_loop:
+ subq $0x20, %rdx
+ movq -1*8(%rsi), %r8
+ movq -2*8(%rsi), %r9
+ movq -3*8(%rsi), %r10
+ movq -4*8(%rsi), %r11
+ leaq -4*8(%rsi), %rsi
+ movq %r8, -1*8(%rdi)
+ movq %r9, -2*8(%rdi)
+ movq %r10, -3*8(%rdi)
+ movq %r11, -4*8(%rdi)
+ leaq -4*8(%rdi), %rdi
+ jae .Lcopy_backward_loop
+
+ /*
+ * Calculate copy position to head.
+ */
+ addq $0x20, %rdx
+ subq %rdx, %rsi
+ subq %rdx, %rdi
+.Lhandle_tail:
+ cmpq $16, %rdx
+ jb .Lless_16bytes
+
+ /*
+ * Move data from 16 bytes to 31 bytes.
+ */
+ movq 0*8(%rsi), %r8
+ movq 1*8(%rsi), %r9
+ movq -2*8(%rsi, %rdx), %r10
+ movq -1*8(%rsi, %rdx), %r11
+ movq %r8, 0*8(%rdi)
+ movq %r9, 1*8(%rdi)
+ movq %r10, -2*8(%rdi, %rdx)
+ movq %r11, -1*8(%rdi, %rdx)
+ retq
+ .p2align 4
+.Lless_16bytes:
+ cmpq $8, %rdx
+ jb .Lless_8bytes
+ /*
+ * Move data from 8 bytes to 15 bytes.
+ */
+ movq 0*8(%rsi), %r8
+ movq -1*8(%rsi, %rdx), %r9
+ movq %r8, 0*8(%rdi)
+ movq %r9, -1*8(%rdi, %rdx)
+ retq
+ .p2align 4
+.Lless_8bytes:
+ cmpq $4, %rdx
+ jb .Lless_3bytes
+
+ /*
+ * Move data from 4 bytes to 7 bytes.
+ */
+ movl (%rsi), %ecx
+ movl -4(%rsi, %rdx), %r8d
+ movl %ecx, (%rdi)
+ movl %r8d, -4(%rdi, %rdx)
+ retq
+ .p2align 4
+.Lless_3bytes:
+ cmpl $0, %edx
+ je .Lend
+ /*
+ * Move data from 1 bytes to 3 bytes.
+ */
+.Lloop_1:
+ movb (%rsi), %r8b
+ movb %r8b, (%rdi)
+ incq %rdi
+ incq %rsi
+ decl %edx
+ jnz .Lloop_1
+
+.Lend:
+ retq
+ CFI_ENDPROC
+ENDPROC(memcpy)
+ENDPROC(__memcpy)
+
+ /*
+ * Some CPUs run faster using the string copy instructions.
+ * It is also a lot simpler. Use this when possible:
+ */
+
+ .section .altinstructions, "a"
+ .align 8
+ .quad memcpy
+ .quad .Lmemcpy_c
+ .word X86_FEATURE_REP_GOOD
+
+ /*
+ * Replace only beginning, memcpy is used to apply alternatives,
+ * so it is silly to overwrite itself with nops - reboot is the
+ * only outcome...
+ */
+ .byte .Lmemcpy_e - .Lmemcpy_c
+ .byte .Lmemcpy_e - .Lmemcpy_c
+ .previous
diff --git a/smartt-perf/util/include/asm/arm/unistd.h b/smartt-perf/util/include/asm/arm/unistd.h
new file mode 100644
index 0000000..dd2bf53
--- /dev/null
+++ b/smartt-perf/util/include/asm/arm/unistd.h
@@ -0,0 +1,475 @@
+/*
+ * arch/arm/include/asm/unistd.h
+ *
+ * Copyright (C) 2001-2005 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Please forward _all_ changes to this file to rmk@arm.linux.org.uk,
+ * no matter what the change is. Thanks!
+ */
+#ifndef __ASM_ARM_UNISTD_H
+#define __ASM_ARM_UNISTD_H
+
+#define __NR_OABI_SYSCALL_BASE 0x900000
+
+#if defined(__thumb__) || defined(__ARM_EABI__)
+#define __NR_SYSCALL_BASE 0
+#else
+#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE
+#endif
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
+#define __NR_exit (__NR_SYSCALL_BASE+ 1)
+#define __NR_fork (__NR_SYSCALL_BASE+ 2)
+#define __NR_read (__NR_SYSCALL_BASE+ 3)
+#define __NR_write (__NR_SYSCALL_BASE+ 4)
+#define __NR_open (__NR_SYSCALL_BASE+ 5)
+#define __NR_close (__NR_SYSCALL_BASE+ 6)
+ /* 7 was sys_waitpid */
+#define __NR_creat (__NR_SYSCALL_BASE+ 8)
+#define __NR_link (__NR_SYSCALL_BASE+ 9)
+#define __NR_unlink (__NR_SYSCALL_BASE+ 10)
+#define __NR_execve (__NR_SYSCALL_BASE+ 11)
+#define __NR_chdir (__NR_SYSCALL_BASE+ 12)
+#define __NR_time (__NR_SYSCALL_BASE+ 13)
+#define __NR_mknod (__NR_SYSCALL_BASE+ 14)
+#define __NR_chmod (__NR_SYSCALL_BASE+ 15)
+#define __NR_lchown (__NR_SYSCALL_BASE+ 16)
+ /* 17 was sys_break */
+ /* 18 was sys_stat */
+#define __NR_lseek (__NR_SYSCALL_BASE+ 19)
+#define __NR_getpid (__NR_SYSCALL_BASE+ 20)
+#define __NR_mount (__NR_SYSCALL_BASE+ 21)
+#define __NR_umount (__NR_SYSCALL_BASE+ 22)
+#define __NR_setuid (__NR_SYSCALL_BASE+ 23)
+#define __NR_getuid (__NR_SYSCALL_BASE+ 24)
+#define __NR_stime (__NR_SYSCALL_BASE+ 25)
+#define __NR_ptrace (__NR_SYSCALL_BASE+ 26)
+#define __NR_alarm (__NR_SYSCALL_BASE+ 27)
+ /* 28 was sys_fstat */
+#define __NR_pause (__NR_SYSCALL_BASE+ 29)
+#define __NR_utime (__NR_SYSCALL_BASE+ 30)
+ /* 31 was sys_stty */
+ /* 32 was sys_gtty */
+#define __NR_access (__NR_SYSCALL_BASE+ 33)
+#define __NR_nice (__NR_SYSCALL_BASE+ 34)
+ /* 35 was sys_ftime */
+#define __NR_sync (__NR_SYSCALL_BASE+ 36)
+#define __NR_kill (__NR_SYSCALL_BASE+ 37)
+#define __NR_rename (__NR_SYSCALL_BASE+ 38)
+#define __NR_mkdir (__NR_SYSCALL_BASE+ 39)
+#define __NR_rmdir (__NR_SYSCALL_BASE+ 40)
+#define __NR_dup (__NR_SYSCALL_BASE+ 41)
+#define __NR_pipe (__NR_SYSCALL_BASE+ 42)
+#define __NR_times (__NR_SYSCALL_BASE+ 43)
+ /* 44 was sys_prof */
+#define __NR_brk (__NR_SYSCALL_BASE+ 45)
+#define __NR_setgid (__NR_SYSCALL_BASE+ 46)
+#define __NR_getgid (__NR_SYSCALL_BASE+ 47)
+ /* 48 was sys_signal */
+#define __NR_geteuid (__NR_SYSCALL_BASE+ 49)
+#define __NR_getegid (__NR_SYSCALL_BASE+ 50)
+#define __NR_acct (__NR_SYSCALL_BASE+ 51)
+#define __NR_umount2 (__NR_SYSCALL_BASE+ 52)
+ /* 53 was sys_lock */
+#define __NR_ioctl (__NR_SYSCALL_BASE+ 54)
+#define __NR_fcntl (__NR_SYSCALL_BASE+ 55)
+ /* 56 was sys_mpx */
+#define __NR_setpgid (__NR_SYSCALL_BASE+ 57)
+ /* 58 was sys_ulimit */
+ /* 59 was sys_olduname */
+#define __NR_umask (__NR_SYSCALL_BASE+ 60)
+#define __NR_chroot (__NR_SYSCALL_BASE+ 61)
+#define __NR_ustat (__NR_SYSCALL_BASE+ 62)
+#define __NR_dup2 (__NR_SYSCALL_BASE+ 63)
+#define __NR_getppid (__NR_SYSCALL_BASE+ 64)
+#define __NR_getpgrp (__NR_SYSCALL_BASE+ 65)
+#define __NR_setsid (__NR_SYSCALL_BASE+ 66)
+#define __NR_sigaction (__NR_SYSCALL_BASE+ 67)
+ /* 68 was sys_sgetmask */
+ /* 69 was sys_ssetmask */
+#define __NR_setreuid (__NR_SYSCALL_BASE+ 70)
+#define __NR_setregid (__NR_SYSCALL_BASE+ 71)
+#define __NR_sigsuspend (__NR_SYSCALL_BASE+ 72)
+#define __NR_sigpending (__NR_SYSCALL_BASE+ 73)
+#define __NR_sethostname (__NR_SYSCALL_BASE+ 74)
+#define __NR_setrlimit (__NR_SYSCALL_BASE+ 75)
+#define __NR_getrlimit (__NR_SYSCALL_BASE+ 76) /* Back compat 2GB limited rlimit */
+#define __NR_getrusage (__NR_SYSCALL_BASE+ 77)
+#define __NR_gettimeofday (__NR_SYSCALL_BASE+ 78)
+#define __NR_settimeofday (__NR_SYSCALL_BASE+ 79)
+#define __NR_getgroups (__NR_SYSCALL_BASE+ 80)
+#define __NR_setgroups (__NR_SYSCALL_BASE+ 81)
+#define __NR_select (__NR_SYSCALL_BASE+ 82)
+#define __NR_symlink (__NR_SYSCALL_BASE+ 83)
+ /* 84 was sys_lstat */
+#define __NR_readlink (__NR_SYSCALL_BASE+ 85)
+#define __NR_uselib (__NR_SYSCALL_BASE+ 86)
+#define __NR_swapon (__NR_SYSCALL_BASE+ 87)
+#define __NR_reboot (__NR_SYSCALL_BASE+ 88)
+#define __NR_readdir (__NR_SYSCALL_BASE+ 89)
+#define __NR_mmap (__NR_SYSCALL_BASE+ 90)
+#define __NR_munmap (__NR_SYSCALL_BASE+ 91)
+#define __NR_truncate (__NR_SYSCALL_BASE+ 92)
+#define __NR_ftruncate (__NR_SYSCALL_BASE+ 93)
+#define __NR_fchmod (__NR_SYSCALL_BASE+ 94)
+#define __NR_fchown (__NR_SYSCALL_BASE+ 95)
+#define __NR_getpriority (__NR_SYSCALL_BASE+ 96)
+#define __NR_setpriority (__NR_SYSCALL_BASE+ 97)
+ /* 98 was sys_profil */
+#define __NR_statfs (__NR_SYSCALL_BASE+ 99)
+#define __NR_fstatfs (__NR_SYSCALL_BASE+100)
+ /* 101 was sys_ioperm */
+#define __NR_socketcall (__NR_SYSCALL_BASE+102)
+#define __NR_syslog (__NR_SYSCALL_BASE+103)
+#define __NR_setitimer (__NR_SYSCALL_BASE+104)
+#define __NR_getitimer (__NR_SYSCALL_BASE+105)
+#define __NR_stat (__NR_SYSCALL_BASE+106)
+#define __NR_lstat (__NR_SYSCALL_BASE+107)
+#define __NR_fstat (__NR_SYSCALL_BASE+108)
+ /* 109 was sys_uname */
+ /* 110 was sys_iopl */
+#define __NR_vhangup (__NR_SYSCALL_BASE+111)
+ /* 112 was sys_idle */
+#define __NR_syscall (__NR_SYSCALL_BASE+113) /* syscall to call a syscall! */
+#define __NR_wait4 (__NR_SYSCALL_BASE+114)
+#define __NR_swapoff (__NR_SYSCALL_BASE+115)
+#define __NR_sysinfo (__NR_SYSCALL_BASE+116)
+#define __NR_ipc (__NR_SYSCALL_BASE+117)
+#define __NR_fsync (__NR_SYSCALL_BASE+118)
+#define __NR_sigreturn (__NR_SYSCALL_BASE+119)
+#define __NR_clone (__NR_SYSCALL_BASE+120)
+#define __NR_setdomainname (__NR_SYSCALL_BASE+121)
+#define __NR_uname (__NR_SYSCALL_BASE+122)
+ /* 123 was sys_modify_ldt */
+#define __NR_adjtimex (__NR_SYSCALL_BASE+124)
+#define __NR_mprotect (__NR_SYSCALL_BASE+125)
+#define __NR_sigprocmask (__NR_SYSCALL_BASE+126)
+ /* 127 was sys_create_module */
+#define __NR_init_module (__NR_SYSCALL_BASE+128)
+#define __NR_delete_module (__NR_SYSCALL_BASE+129)
+ /* 130 was sys_get_kernel_syms */
+#define __NR_quotactl (__NR_SYSCALL_BASE+131)
+#define __NR_getpgid (__NR_SYSCALL_BASE+132)
+#define __NR_fchdir (__NR_SYSCALL_BASE+133)
+#define __NR_bdflush (__NR_SYSCALL_BASE+134)
+#define __NR_sysfs (__NR_SYSCALL_BASE+135)
+#define __NR_personality (__NR_SYSCALL_BASE+136)
+ /* 137 was sys_afs_syscall */
+#define __NR_setfsuid (__NR_SYSCALL_BASE+138)
+#define __NR_setfsgid (__NR_SYSCALL_BASE+139)
+#define __NR__llseek (__NR_SYSCALL_BASE+140)
+#define __NR_getdents (__NR_SYSCALL_BASE+141)
+#define __NR__newselect (__NR_SYSCALL_BASE+142)
+#define __NR_flock (__NR_SYSCALL_BASE+143)
+#define __NR_msync (__NR_SYSCALL_BASE+144)
+#define __NR_readv (__NR_SYSCALL_BASE+145)
+#define __NR_writev (__NR_SYSCALL_BASE+146)
+#define __NR_getsid (__NR_SYSCALL_BASE+147)
+#define __NR_fdatasync (__NR_SYSCALL_BASE+148)
+#define __NR__sysctl (__NR_SYSCALL_BASE+149)
+#define __NR_mlock (__NR_SYSCALL_BASE+150)
+#define __NR_munlock (__NR_SYSCALL_BASE+151)
+#define __NR_mlockall (__NR_SYSCALL_BASE+152)
+#define __NR_munlockall (__NR_SYSCALL_BASE+153)
+#define __NR_sched_setparam (__NR_SYSCALL_BASE+154)
+#define __NR_sched_getparam (__NR_SYSCALL_BASE+155)
+#define __NR_sched_setscheduler (__NR_SYSCALL_BASE+156)
+#define __NR_sched_getscheduler (__NR_SYSCALL_BASE+157)
+#define __NR_sched_yield (__NR_SYSCALL_BASE+158)
+#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE+159)
+#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE+160)
+#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE+161)
+#define __NR_nanosleep (__NR_SYSCALL_BASE+162)
+#define __NR_mremap (__NR_SYSCALL_BASE+163)
+#define __NR_setresuid (__NR_SYSCALL_BASE+164)
+#define __NR_getresuid (__NR_SYSCALL_BASE+165)
+ /* 166 was sys_vm86 */
+ /* 167 was sys_query_module */
+#define __NR_poll (__NR_SYSCALL_BASE+168)
+#define __NR_nfsservctl (__NR_SYSCALL_BASE+169)
+#define __NR_setresgid (__NR_SYSCALL_BASE+170)
+#define __NR_getresgid (__NR_SYSCALL_BASE+171)
+#define __NR_prctl (__NR_SYSCALL_BASE+172)
+#define __NR_rt_sigreturn (__NR_SYSCALL_BASE+173)
+#define __NR_rt_sigaction (__NR_SYSCALL_BASE+174)
+#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE+175)
+#define __NR_rt_sigpending (__NR_SYSCALL_BASE+176)
+#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE+177)
+#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE+178)
+#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE+179)
+#define __NR_pread64 (__NR_SYSCALL_BASE+180)
+#define __NR_pwrite64 (__NR_SYSCALL_BASE+181)
+#define __NR_chown (__NR_SYSCALL_BASE+182)
+#define __NR_getcwd (__NR_SYSCALL_BASE+183)
+#define __NR_capget (__NR_SYSCALL_BASE+184)
+#define __NR_capset (__NR_SYSCALL_BASE+185)
+#define __NR_sigaltstack (__NR_SYSCALL_BASE+186)
+#define __NR_sendfile (__NR_SYSCALL_BASE+187)
+ /* 188 reserved */
+ /* 189 reserved */
+#define __NR_vfork (__NR_SYSCALL_BASE+190)
+#define __NR_ugetrlimit (__NR_SYSCALL_BASE+191) /* SuS compliant getrlimit */
+#define __NR_mmap2 (__NR_SYSCALL_BASE+192)
+#define __NR_truncate64 (__NR_SYSCALL_BASE+193)
+#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194)
+#define __NR_stat64 (__NR_SYSCALL_BASE+195)
+#define __NR_lstat64 (__NR_SYSCALL_BASE+196)
+#define __NR_fstat64 (__NR_SYSCALL_BASE+197)
+#define __NR_lchown32 (__NR_SYSCALL_BASE+198)
+#define __NR_getuid32 (__NR_SYSCALL_BASE+199)
+#define __NR_getgid32 (__NR_SYSCALL_BASE+200)
+#define __NR_geteuid32 (__NR_SYSCALL_BASE+201)
+#define __NR_getegid32 (__NR_SYSCALL_BASE+202)
+#define __NR_setreuid32 (__NR_SYSCALL_BASE+203)
+#define __NR_setregid32 (__NR_SYSCALL_BASE+204)
+#define __NR_getgroups32 (__NR_SYSCALL_BASE+205)
+#define __NR_setgroups32 (__NR_SYSCALL_BASE+206)
+#define __NR_fchown32 (__NR_SYSCALL_BASE+207)
+#define __NR_setresuid32 (__NR_SYSCALL_BASE+208)
+#define __NR_getresuid32 (__NR_SYSCALL_BASE+209)
+#define __NR_setresgid32 (__NR_SYSCALL_BASE+210)
+#define __NR_getresgid32 (__NR_SYSCALL_BASE+211)
+#define __NR_chown32 (__NR_SYSCALL_BASE+212)
+#define __NR_setuid32 (__NR_SYSCALL_BASE+213)
+#define __NR_setgid32 (__NR_SYSCALL_BASE+214)
+#define __NR_setfsuid32 (__NR_SYSCALL_BASE+215)
+#define __NR_setfsgid32 (__NR_SYSCALL_BASE+216)
+#define __NR_getdents64 (__NR_SYSCALL_BASE+217)
+#define __NR_pivot_root (__NR_SYSCALL_BASE+218)
+#define __NR_mincore (__NR_SYSCALL_BASE+219)
+#define __NR_madvise (__NR_SYSCALL_BASE+220)
+#define __NR_fcntl64 (__NR_SYSCALL_BASE+221)
+ /* 222 for tux */
+ /* 223 is unused */
+#define __NR_gettid (__NR_SYSCALL_BASE+224)
+#define __NR_readahead (__NR_SYSCALL_BASE+225)
+#define __NR_setxattr (__NR_SYSCALL_BASE+226)
+#define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
+#define __NR_fsetxattr (__NR_SYSCALL_BASE+228)
+#define __NR_getxattr (__NR_SYSCALL_BASE+229)
+#define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
+#define __NR_fgetxattr (__NR_SYSCALL_BASE+231)
+#define __NR_listxattr (__NR_SYSCALL_BASE+232)
+#define __NR_llistxattr (__NR_SYSCALL_BASE+233)
+#define __NR_flistxattr (__NR_SYSCALL_BASE+234)
+#define __NR_removexattr (__NR_SYSCALL_BASE+235)
+#define __NR_lremovexattr (__NR_SYSCALL_BASE+236)
+#define __NR_fremovexattr (__NR_SYSCALL_BASE+237)
+#define __NR_tkill (__NR_SYSCALL_BASE+238)
+#define __NR_sendfile64 (__NR_SYSCALL_BASE+239)
+#define __NR_futex (__NR_SYSCALL_BASE+240)
+#define __NR_sched_setaffinity (__NR_SYSCALL_BASE+241)
+#define __NR_sched_getaffinity (__NR_SYSCALL_BASE+242)
+#define __NR_io_setup (__NR_SYSCALL_BASE+243)
+#define __NR_io_destroy (__NR_SYSCALL_BASE+244)
+#define __NR_io_getevents (__NR_SYSCALL_BASE+245)
+#define __NR_io_submit (__NR_SYSCALL_BASE+246)
+#define __NR_io_cancel (__NR_SYSCALL_BASE+247)
+#define __NR_exit_group (__NR_SYSCALL_BASE+248)
+#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249)
+#define __NR_epoll_create (__NR_SYSCALL_BASE+250)
+#define __NR_epoll_ctl (__NR_SYSCALL_BASE+251)
+#define __NR_epoll_wait (__NR_SYSCALL_BASE+252)
+#define __NR_remap_file_pages (__NR_SYSCALL_BASE+253)
+ /* 254 for set_thread_area */
+ /* 255 for get_thread_area */
+#define __NR_set_tid_address (__NR_SYSCALL_BASE+256)
+#define __NR_timer_create (__NR_SYSCALL_BASE+257)
+#define __NR_timer_settime (__NR_SYSCALL_BASE+258)
+#define __NR_timer_gettime (__NR_SYSCALL_BASE+259)
+#define __NR_timer_getoverrun (__NR_SYSCALL_BASE+260)
+#define __NR_timer_delete (__NR_SYSCALL_BASE+261)
+#define __NR_clock_settime (__NR_SYSCALL_BASE+262)
+#define __NR_clock_gettime (__NR_SYSCALL_BASE+263)
+#define __NR_clock_getres (__NR_SYSCALL_BASE+264)
+#define __NR_clock_nanosleep (__NR_SYSCALL_BASE+265)
+#define __NR_statfs64 (__NR_SYSCALL_BASE+266)
+#define __NR_fstatfs64 (__NR_SYSCALL_BASE+267)
+#define __NR_tgkill (__NR_SYSCALL_BASE+268)
+#define __NR_utimes (__NR_SYSCALL_BASE+269)
+#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE+270)
+#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE+271)
+#define __NR_pciconfig_read (__NR_SYSCALL_BASE+272)
+#define __NR_pciconfig_write (__NR_SYSCALL_BASE+273)
+#define __NR_mq_open (__NR_SYSCALL_BASE+274)
+#define __NR_mq_unlink (__NR_SYSCALL_BASE+275)
+#define __NR_mq_timedsend (__NR_SYSCALL_BASE+276)
+#define __NR_mq_timedreceive (__NR_SYSCALL_BASE+277)
+#define __NR_mq_notify (__NR_SYSCALL_BASE+278)
+#define __NR_mq_getsetattr (__NR_SYSCALL_BASE+279)
+#define __NR_waitid (__NR_SYSCALL_BASE+280)
+#define __NR_socket (__NR_SYSCALL_BASE+281)
+#define __NR_bind (__NR_SYSCALL_BASE+282)
+#define __NR_connect (__NR_SYSCALL_BASE+283)
+#define __NR_listen (__NR_SYSCALL_BASE+284)
+#define __NR_accept (__NR_SYSCALL_BASE+285)
+#define __NR_getsockname (__NR_SYSCALL_BASE+286)
+#define __NR_getpeername (__NR_SYSCALL_BASE+287)
+#define __NR_socketpair (__NR_SYSCALL_BASE+288)
+#define __NR_send (__NR_SYSCALL_BASE+289)
+#define __NR_sendto (__NR_SYSCALL_BASE+290)
+#define __NR_recv (__NR_SYSCALL_BASE+291)
+#define __NR_recvfrom (__NR_SYSCALL_BASE+292)
+#define __NR_shutdown (__NR_SYSCALL_BASE+293)
+#define __NR_setsockopt (__NR_SYSCALL_BASE+294)
+#define __NR_getsockopt (__NR_SYSCALL_BASE+295)
+#define __NR_sendmsg (__NR_SYSCALL_BASE+296)
+#define __NR_recvmsg (__NR_SYSCALL_BASE+297)
+#define __NR_semop (__NR_SYSCALL_BASE+298)
+#define __NR_semget (__NR_SYSCALL_BASE+299)
+#define __NR_semctl (__NR_SYSCALL_BASE+300)
+#define __NR_msgsnd (__NR_SYSCALL_BASE+301)
+#define __NR_msgrcv (__NR_SYSCALL_BASE+302)
+#define __NR_msgget (__NR_SYSCALL_BASE+303)
+#define __NR_msgctl (__NR_SYSCALL_BASE+304)
+#define __NR_shmat (__NR_SYSCALL_BASE+305)
+#define __NR_shmdt (__NR_SYSCALL_BASE+306)
+#define __NR_shmget (__NR_SYSCALL_BASE+307)
+#define __NR_shmctl (__NR_SYSCALL_BASE+308)
+#define __NR_add_key (__NR_SYSCALL_BASE+309)
+#define __NR_request_key (__NR_SYSCALL_BASE+310)
+#define __NR_keyctl (__NR_SYSCALL_BASE+311)
+#define __NR_semtimedop (__NR_SYSCALL_BASE+312)
+#define __NR_vserver (__NR_SYSCALL_BASE+313)
+#define __NR_ioprio_set (__NR_SYSCALL_BASE+314)
+#define __NR_ioprio_get (__NR_SYSCALL_BASE+315)
+#define __NR_inotify_init (__NR_SYSCALL_BASE+316)
+#define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317)
+#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318)
+#define __NR_mbind (__NR_SYSCALL_BASE+319)
+#define __NR_get_mempolicy (__NR_SYSCALL_BASE+320)
+#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)
+#define __NR_openat (__NR_SYSCALL_BASE+322)
+#define __NR_mkdirat (__NR_SYSCALL_BASE+323)
+#define __NR_mknodat (__NR_SYSCALL_BASE+324)
+#define __NR_fchownat (__NR_SYSCALL_BASE+325)
+#define __NR_futimesat (__NR_SYSCALL_BASE+326)
+#define __NR_fstatat64 (__NR_SYSCALL_BASE+327)
+#define __NR_unlinkat (__NR_SYSCALL_BASE+328)
+#define __NR_renameat (__NR_SYSCALL_BASE+329)
+#define __NR_linkat (__NR_SYSCALL_BASE+330)
+#define __NR_symlinkat (__NR_SYSCALL_BASE+331)
+#define __NR_readlinkat (__NR_SYSCALL_BASE+332)
+#define __NR_fchmodat (__NR_SYSCALL_BASE+333)
+#define __NR_faccessat (__NR_SYSCALL_BASE+334)
+#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
+#define __NR_ppoll (__NR_SYSCALL_BASE+336)
+#define __NR_unshare (__NR_SYSCALL_BASE+337)
+#define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
+#define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
+#define __NR_splice (__NR_SYSCALL_BASE+340)
+#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE+341)
+#define __NR_sync_file_range2 __NR_arm_sync_file_range
+#define __NR_tee (__NR_SYSCALL_BASE+342)
+#define __NR_vmsplice (__NR_SYSCALL_BASE+343)
+#define __NR_move_pages (__NR_SYSCALL_BASE+344)
+#define __NR_getcpu (__NR_SYSCALL_BASE+345)
+#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
+#define __NR_kexec_load (__NR_SYSCALL_BASE+347)
+#define __NR_utimensat (__NR_SYSCALL_BASE+348)
+#define __NR_signalfd (__NR_SYSCALL_BASE+349)
+#define __NR_timerfd_create (__NR_SYSCALL_BASE+350)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#define __NR_fallocate (__NR_SYSCALL_BASE+352)
+#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353)
+#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354)
+#define __NR_signalfd4 (__NR_SYSCALL_BASE+355)
+#define __NR_eventfd2 (__NR_SYSCALL_BASE+356)
+#define __NR_epoll_create1 (__NR_SYSCALL_BASE+357)
+#define __NR_dup3 (__NR_SYSCALL_BASE+358)
+#define __NR_pipe2 (__NR_SYSCALL_BASE+359)
+#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360)
+#define __NR_preadv (__NR_SYSCALL_BASE+361)
+#define __NR_pwritev (__NR_SYSCALL_BASE+362)
+#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
+#define __NR_perf_event_open (__NR_SYSCALL_BASE+364)
+#define __NR_recvmmsg (__NR_SYSCALL_BASE+365)
+
+/*
+ * The following SWIs are ARM private.
+ */
+#define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000)
+#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
+#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
+#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
+#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
+#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
+
+/*
+ * *NOTE*: This is a ghost syscall private to the kernel. Only the
+ * __kuser_cmpxchg code in entry-armv.S should be aware of its
+ * existence. Don't ever use this from user code.
+ */
+#ifdef __KERNEL__
+#define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
+#endif
+
+/*
+ * The following syscalls are obsolete and no longer available for EABI.
+ */
+#if defined(__ARM_EABI__) && !defined(__KERNEL__)
+#undef __NR_time
+#undef __NR_umount
+#undef __NR_stime
+#undef __NR_alarm
+#undef __NR_utime
+#undef __NR_getrlimit
+#undef __NR_select
+#undef __NR_readdir
+#undef __NR_mmap
+#undef __NR_socketcall
+#undef __NR_syscall
+#undef __NR_ipc
+#endif
+
+#ifdef __KERNEL__
+
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+#define __ARCH_WANT_SYS_OLD_MMAP
+#define __ARCH_WANT_SYS_OLD_SELECT
+
+#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_IPC
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_SYS_SOCKETCALL
+#endif
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+
+/*
+ * Unimplemented (or alternatively implemented) syscalls
+ */
+#define __IGNORE_fadvise64_64 1
+#define __IGNORE_migrate_pages 1
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_ARM_UNISTD_H */
diff --git a/smartt-perf/util/include/asm/asm-offsets.h b/smartt-perf/util/include/asm/asm-offsets.h
new file mode 100644
index 0000000..ed53894
--- /dev/null
+++ b/smartt-perf/util/include/asm/asm-offsets.h
@@ -0,0 +1 @@
+/* stub */
diff --git a/smartt-perf/util/include/asm/bug.h b/smartt-perf/util/include/asm/bug.h
new file mode 100644
index 0000000..7fcc681
--- /dev/null
+++ b/smartt-perf/util/include/asm/bug.h
@@ -0,0 +1,22 @@
+#ifndef _PERF_ASM_GENERIC_BUG_H
+#define _PERF_ASM_GENERIC_BUG_H
+
+#define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0)
+
+#define WARN(condition, format...) ({ \
+ int __ret_warn_on = !!(condition); \
+ if (unlikely(__ret_warn_on)) \
+ __WARN_printf(format); \
+ unlikely(__ret_warn_on); \
+})
+
+#define WARN_ONCE(condition, format...) ({ \
+ static int __warned; \
+ int __ret_warn_once = !!(condition); \
+ \
+ if (unlikely(__ret_warn_once)) \
+ if (WARN(!__warned, format)) \
+ __warned = 1; \
+ unlikely(__ret_warn_once); \
+})
+#endif
diff --git a/smartt-perf/util/include/asm/byteorder.h b/smartt-perf/util/include/asm/byteorder.h
new file mode 100644
index 0000000..27d57f5
--- /dev/null
+++ b/smartt-perf/util/include/asm/byteorder.h
@@ -0,0 +1,2 @@
+#include <asm/types.h>
+#include "linux/swab.h"
diff --git a/smartt-perf/util/include/asm/cpufeature.h b/smartt-perf/util/include/asm/cpufeature.h
new file mode 100644
index 0000000..acffd5e
--- /dev/null
+++ b/smartt-perf/util/include/asm/cpufeature.h
@@ -0,0 +1,9 @@
+
+#ifndef PERF_CPUFEATURE_H
+#define PERF_CPUFEATURE_H
+
+/* cpufeature.h ... dummy header file for including arch/x86/lib/memcpy_64.S */
+
+#define X86_FEATURE_REP_GOOD 0
+
+#endif /* PERF_CPUFEATURE_H */
diff --git a/smartt-perf/util/include/asm/dwarf2.h b/smartt-perf/util/include/asm/dwarf2.h
new file mode 100644
index 0000000..bb4198e
--- /dev/null
+++ b/smartt-perf/util/include/asm/dwarf2.h
@@ -0,0 +1,11 @@
+
+#ifndef PERF_DWARF2_H
+#define PERF_DWARF2_H
+
+/* dwarf2.h ... dummy header file for including arch/x86/lib/memcpy_64.S */
+
+#define CFI_STARTPROC
+#define CFI_ENDPROC
+
+#endif /* PERF_DWARF2_H */
+
diff --git a/smartt-perf/util/include/asm/hweight.h b/smartt-perf/util/include/asm/hweight.h
new file mode 100644
index 0000000..36cf26d
--- /dev/null
+++ b/smartt-perf/util/include/asm/hweight.h
@@ -0,0 +1,8 @@
+#ifndef PERF_HWEIGHT_H
+#define PERF_HWEIGHT_H
+
+#include <linux/types.h>
+unsigned int hweight32(unsigned int w);
+unsigned long hweight64(__u64 w);
+
+#endif /* PERF_HWEIGHT_H */
diff --git a/smartt-perf/util/include/asm/ia64/unistd.h b/smartt-perf/util/include/asm/ia64/unistd.h
new file mode 100644
index 0000000..bb8b0ff
--- /dev/null
+++ b/smartt-perf/util/include/asm/ia64/unistd.h
@@ -0,0 +1,376 @@
+#ifndef _ASM_IA64_UNISTD_H
+#define _ASM_IA64_UNISTD_H
+
+/*
+ * IA-64 Linux syscall numbers and inline-functions.
+ *
+ * Copyright (C) 1998-2005 Hewlett-Packard Co
+ * David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+#include <asm/break.h>
+
+#define __BREAK_SYSCALL __IA64_BREAK_SYSCALL
+
+#define __NR_ni_syscall 1024
+#define __NR_exit 1025
+#define __NR_read 1026
+#define __NR_write 1027
+#define __NR_open 1028
+#define __NR_close 1029
+#define __NR_creat 1030
+#define __NR_link 1031
+#define __NR_unlink 1032
+#define __NR_execve 1033
+#define __NR_chdir 1034
+#define __NR_fchdir 1035
+#define __NR_utimes 1036
+#define __NR_mknod 1037
+#define __NR_chmod 1038
+#define __NR_chown 1039
+#define __NR_lseek 1040
+#define __NR_getpid 1041
+#define __NR_getppid 1042
+#define __NR_mount 1043
+#define __NR_umount 1044
+#define __NR_setuid 1045
+#define __NR_getuid 1046
+#define __NR_geteuid 1047
+#define __NR_ptrace 1048
+#define __NR_access 1049
+#define __NR_sync 1050
+#define __NR_fsync 1051
+#define __NR_fdatasync 1052
+#define __NR_kill 1053
+#define __NR_rename 1054
+#define __NR_mkdir 1055
+#define __NR_rmdir 1056
+#define __NR_dup 1057
+#define __NR_pipe 1058
+#define __NR_times 1059
+#define __NR_brk 1060
+#define __NR_setgid 1061
+#define __NR_getgid 1062
+#define __NR_getegid 1063
+#define __NR_acct 1064
+#define __NR_ioctl 1065
+#define __NR_fcntl 1066
+#define __NR_umask 1067
+#define __NR_chroot 1068
+#define __NR_ustat 1069
+#define __NR_dup2 1070
+#define __NR_setreuid 1071
+#define __NR_setregid 1072
+#define __NR_getresuid 1073
+#define __NR_setresuid 1074
+#define __NR_getresgid 1075
+#define __NR_setresgid 1076
+#define __NR_getgroups 1077
+#define __NR_setgroups 1078
+#define __NR_getpgid 1079
+#define __NR_setpgid 1080
+#define __NR_setsid 1081
+#define __NR_getsid 1082
+#define __NR_sethostname 1083
+#define __NR_setrlimit 1084
+#define __NR_getrlimit 1085
+#define __NR_getrusage 1086
+#define __NR_gettimeofday 1087
+#define __NR_settimeofday 1088
+#define __NR_select 1089
+#define __NR_poll 1090
+#define __NR_symlink 1091
+#define __NR_readlink 1092
+#define __NR_uselib 1093
+#define __NR_swapon 1094
+#define __NR_swapoff 1095
+#define __NR_reboot 1096
+#define __NR_truncate 1097
+#define __NR_ftruncate 1098
+#define __NR_fchmod 1099
+#define __NR_fchown 1100
+#define __NR_getpriority 1101
+#define __NR_setpriority 1102
+#define __NR_statfs 1103
+#define __NR_fstatfs 1104
+#define __NR_gettid 1105
+#define __NR_semget 1106
+#define __NR_semop 1107
+#define __NR_semctl 1108
+#define __NR_msgget 1109
+#define __NR_msgsnd 1110
+#define __NR_msgrcv 1111
+#define __NR_msgctl 1112
+#define __NR_shmget 1113
+#define __NR_shmat 1114
+#define __NR_shmdt 1115
+#define __NR_shmctl 1116
+/* also known as klogctl() in GNU libc: */
+#define __NR_syslog 1117
+#define __NR_setitimer 1118
+#define __NR_getitimer 1119
+/* 1120 was __NR_old_stat */
+/* 1121 was __NR_old_lstat */
+/* 1122 was __NR_old_fstat */
+#define __NR_vhangup 1123
+#define __NR_lchown 1124
+#define __NR_remap_file_pages 1125
+#define __NR_wait4 1126
+#define __NR_sysinfo 1127
+#define __NR_clone 1128
+#define __NR_setdomainname 1129
+#define __NR_uname 1130
+#define __NR_adjtimex 1131
+/* 1132 was __NR_create_module */
+#define __NR_init_module 1133
+#define __NR_delete_module 1134
+/* 1135 was __NR_get_kernel_syms */
+/* 1136 was __NR_query_module */
+#define __NR_quotactl 1137
+#define __NR_bdflush 1138
+#define __NR_sysfs 1139
+#define __NR_personality 1140
+#define __NR_afs_syscall 1141
+#define __NR_setfsuid 1142
+#define __NR_setfsgid 1143
+#define __NR_getdents 1144
+#define __NR_flock 1145
+#define __NR_readv 1146
+#define __NR_writev 1147
+#define __NR_pread64 1148
+#define __NR_pwrite64 1149
+#define __NR__sysctl 1150
+#define __NR_mmap 1151
+#define __NR_munmap 1152
+#define __NR_mlock 1153
+#define __NR_mlockall 1154
+#define __NR_mprotect 1155
+#define __NR_mremap 1156
+#define __NR_msync 1157
+#define __NR_munlock 1158
+#define __NR_munlockall 1159
+#define __NR_sched_getparam 1160
+#define __NR_sched_setparam 1161
+#define __NR_sched_getscheduler 1162
+#define __NR_sched_setscheduler 1163
+#define __NR_sched_yield 1164
+#define __NR_sched_get_priority_max 1165
+#define __NR_sched_get_priority_min 1166
+#define __NR_sched_rr_get_interval 1167
+#define __NR_nanosleep 1168
+#define __NR_nfsservctl 1169
+#define __NR_prctl 1170
+/* 1171 is reserved for backwards compatibility with old __NR_getpagesize */
+#define __NR_mmap2 1172
+#define __NR_pciconfig_read 1173
+#define __NR_pciconfig_write 1174
+#define __NR_perfmonctl 1175
+#define __NR_sigaltstack 1176
+#define __NR_rt_sigaction 1177
+#define __NR_rt_sigpending 1178
+#define __NR_rt_sigprocmask 1179
+#define __NR_rt_sigqueueinfo 1180
+#define __NR_rt_sigreturn 1181
+#define __NR_rt_sigsuspend 1182
+#define __NR_rt_sigtimedwait 1183
+#define __NR_getcwd 1184
+#define __NR_capget 1185
+#define __NR_capset 1186
+#define __NR_sendfile 1187
+#define __NR_getpmsg 1188
+#define __NR_putpmsg 1189
+#define __NR_socket 1190
+#define __NR_bind 1191
+#define __NR_connect 1192
+#define __NR_listen 1193
+#define __NR_accept 1194
+#define __NR_getsockname 1195
+#define __NR_getpeername 1196
+#define __NR_socketpair 1197
+#define __NR_send 1198
+#define __NR_sendto 1199
+#define __NR_recv 1200
+#define __NR_recvfrom 1201
+#define __NR_shutdown 1202
+#define __NR_setsockopt 1203
+#define __NR_getsockopt 1204
+#define __NR_sendmsg 1205
+#define __NR_recvmsg 1206
+#define __NR_pivot_root 1207
+#define __NR_mincore 1208
+#define __NR_madvise 1209
+#define __NR_stat 1210
+#define __NR_lstat 1211
+#define __NR_fstat 1212
+#define __NR_clone2 1213
+#define __NR_getdents64 1214
+#define __NR_getunwind 1215
+#define __NR_readahead 1216
+#define __NR_setxattr 1217
+#define __NR_lsetxattr 1218
+#define __NR_fsetxattr 1219
+#define __NR_getxattr 1220
+#define __NR_lgetxattr 1221
+#define __NR_fgetxattr 1222
+#define __NR_listxattr 1223
+#define __NR_llistxattr 1224
+#define __NR_flistxattr 1225
+#define __NR_removexattr 1226
+#define __NR_lremovexattr 1227
+#define __NR_fremovexattr 1228
+#define __NR_tkill 1229
+#define __NR_futex 1230
+#define __NR_sched_setaffinity 1231
+#define __NR_sched_getaffinity 1232
+#define __NR_set_tid_address 1233
+#define __NR_fadvise64 1234
+#define __NR_tgkill 1235
+#define __NR_exit_group 1236
+#define __NR_lookup_dcookie 1237
+#define __NR_io_setup 1238
+#define __NR_io_destroy 1239
+#define __NR_io_getevents 1240
+#define __NR_io_submit 1241
+#define __NR_io_cancel 1242
+#define __NR_epoll_create 1243
+#define __NR_epoll_ctl 1244
+#define __NR_epoll_wait 1245
+#define __NR_restart_syscall 1246
+#define __NR_semtimedop 1247
+#define __NR_timer_create 1248
+#define __NR_timer_settime 1249
+#define __NR_timer_gettime 1250
+#define __NR_timer_getoverrun 1251
+#define __NR_timer_delete 1252
+#define __NR_clock_settime 1253
+#define __NR_clock_gettime 1254
+#define __NR_clock_getres 1255
+#define __NR_clock_nanosleep 1256
+#define __NR_fstatfs64 1257
+#define __NR_statfs64 1258
+#define __NR_mbind 1259
+#define __NR_get_mempolicy 1260
+#define __NR_set_mempolicy 1261
+#define __NR_mq_open 1262
+#define __NR_mq_unlink 1263
+#define __NR_mq_timedsend 1264
+#define __NR_mq_timedreceive 1265
+#define __NR_mq_notify 1266
+#define __NR_mq_getsetattr 1267
+#define __NR_kexec_load 1268
+#define __NR_vserver 1269
+#define __NR_waitid 1270
+#define __NR_add_key 1271
+#define __NR_request_key 1272
+#define __NR_keyctl 1273
+#define __NR_ioprio_set 1274
+#define __NR_ioprio_get 1275
+#define __NR_move_pages 1276
+#define __NR_inotify_init 1277
+#define __NR_inotify_add_watch 1278
+#define __NR_inotify_rm_watch 1279
+#define __NR_migrate_pages 1280
+#define __NR_openat 1281
+#define __NR_mkdirat 1282
+#define __NR_mknodat 1283
+#define __NR_fchownat 1284
+#define __NR_futimesat 1285
+#define __NR_newfstatat 1286
+#define __NR_unlinkat 1287
+#define __NR_renameat 1288
+#define __NR_linkat 1289
+#define __NR_symlinkat 1290
+#define __NR_readlinkat 1291
+#define __NR_fchmodat 1292
+#define __NR_faccessat 1293
+#define __NR_pselect6 1294
+#define __NR_ppoll 1295
+#define __NR_unshare 1296
+#define __NR_splice 1297
+#define __NR_set_robust_list 1298
+#define __NR_get_robust_list 1299
+#define __NR_sync_file_range 1300
+#define __NR_tee 1301
+#define __NR_vmsplice 1302
+#define __NR_fallocate 1303
+#define __NR_getcpu 1304
+#define __NR_epoll_pwait 1305
+#define __NR_utimensat 1306
+#define __NR_signalfd 1307
+#define __NR_timerfd 1308
+#define __NR_eventfd 1309
+#define __NR_timerfd_create 1310
+#define __NR_timerfd_settime 1311
+#define __NR_timerfd_gettime 1312
+#define __NR_signalfd4 1313
+#define __NR_eventfd2 1314
+#define __NR_epoll_create1 1315
+#define __NR_dup3 1316
+#define __NR_pipe2 1317
+#define __NR_inotify_init1 1318
+#define __NR_preadv 1319
+#define __NR_pwritev 1320
+#define __NR_rt_tgsigqueueinfo 1321
+#define __NR_recvmmsg 1322
+
+#ifdef __KERNEL__
+
+
+#define NR_syscalls 299 /* length of syscall table */
+
+/*
+ * The following defines stop scripts/checksyscalls.sh from complaining about
+ * unimplemented system calls. Glibc provides for each of these by using
+ * more modern equivalent system calls.
+ */
+#define __IGNORE_fork /* clone() */
+#define __IGNORE_time /* gettimeofday() */
+#define __IGNORE_alarm /* setitimer(ITIMER_REAL, ... */
+#define __IGNORE_pause /* rt_sigprocmask(), rt_sigsuspend() */
+#define __IGNORE_utime /* utimes() */
+#define __IGNORE_getpgrp /* getpgid() */
+#define __IGNORE_vfork /* clone() */
+#define __IGNORE_umount2 /* umount() */
+
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+
+#if !defined(__ASSEMBLY__) && !defined(ASSEMBLER)
+
+#include <linux/types.h>
+#include <linux/linkage.h>
+#include <linux/compiler.h>
+
+extern long __ia64_syscall (long a0, long a1, long a2, long a3, long a4, long nr);
+
+asmlinkage unsigned long sys_mmap(
+ unsigned long addr, unsigned long len,
+ int prot, int flags,
+ int fd, long off);
+asmlinkage unsigned long sys_mmap2(
+ unsigned long addr, unsigned long len,
+ int prot, int flags,
+ int fd, long pgoff);
+struct pt_regs;
+struct sigaction;
+long sys_execve(char __user *filename, char __user * __user *argv,
+ char __user * __user *envp, struct pt_regs *regs);
+asmlinkage long sys_ia64_pipe(void);
+asmlinkage long sys_rt_sigaction(int sig,
+ const struct sigaction __user *act,
+ struct sigaction __user *oact,
+ size_t sigsetsize);
+
+/*
+ * "Conditional" syscalls
+ *
+ * Note, this macro can only be used in the file which defines sys_ni_syscall, i.e., in
+ * kernel/sys_ni.c. This version causes warnings because the declaration isn't a
+ * proper prototype, but we can't use __typeof__ either, because not all cond_syscall()
+ * declarations have prototypes at the moment.
+ */
+#define cond_syscall(x) asmlinkage long x (void) __attribute__((weak,alias("sys_ni_syscall")))
+
+#endif /* !__ASSEMBLY__ */
+#endif /* __KERNEL__ */
+#endif /* _ASM_IA64_UNISTD_H */
diff --git a/smartt-perf/util/include/asm/swab.h b/smartt-perf/util/include/asm/swab.h
new file mode 100644
index 0000000..ed53894
--- /dev/null
+++ b/smartt-perf/util/include/asm/swab.h
@@ -0,0 +1 @@
+/* stub */
diff --git a/smartt-perf/util/include/asm/system.h b/smartt-perf/util/include/asm/system.h
new file mode 100644
index 0000000..710cecc
--- /dev/null
+++ b/smartt-perf/util/include/asm/system.h
@@ -0,0 +1 @@
+/* Empty */
diff --git a/smartt-perf/util/include/asm/uaccess.h b/smartt-perf/util/include/asm/uaccess.h
new file mode 100644
index 0000000..d0f72b8
--- /dev/null
+++ b/smartt-perf/util/include/asm/uaccess.h
@@ -0,0 +1,14 @@
+#ifndef _PERF_ASM_UACCESS_H_
+#define _PERF_ASM_UACCESS_H_
+
+#define __get_user(src, dest) \
+({ \
+ (src) = *dest; \
+ 0; \
+})
+
+#define get_user __get_user
+
+#define access_ok(type, addr, size) 1
+
+#endif
diff --git a/smartt-perf/util/include/asm/x86/unistd.h b/smartt-perf/util/include/asm/x86/unistd.h
new file mode 100644
index 0000000..2a58ed3
--- /dev/null
+++ b/smartt-perf/util/include/asm/x86/unistd.h
@@ -0,0 +1,13 @@
+#ifdef __KERNEL__
+# ifdef CONFIG_X86_32
+# include "unistd_32.h"
+# else
+# include "unistd_64.h"
+# endif
+#else
+# ifdef __i386__
+# include "unistd_32.h"
+# else
+# include "unistd_64.h"
+# endif
+#endif
diff --git a/smartt-perf/util/include/asm/x86/unistd_32.h b/smartt-perf/util/include/asm/x86/unistd_32.h
new file mode 100644
index 0000000..beb9b5f
--- /dev/null
+++ b/smartt-perf/util/include/asm/x86/unistd_32.h
@@ -0,0 +1,390 @@
+#ifndef _ASM_X86_UNISTD_32_H
+#define _ASM_X86_UNISTD_32_H
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#define __NR_restart_syscall 0
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_waitpid 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_time 13
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_lchown 16
+#define __NR_break 17
+#define __NR_oldstat 18
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_setuid 23
+#define __NR_getuid 24
+#define __NR_stime 25
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_oldfstat 28
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_stty 31
+#define __NR_gtty 32
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_ftime 35
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_prof 44
+#define __NR_brk 45
+#define __NR_setgid 46
+#define __NR_getgid 47
+#define __NR_signal 48
+#define __NR_geteuid 49
+#define __NR_getegid 50
+#define __NR_acct 51
+#define __NR_umount2 52
+#define __NR_lock 53
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_mpx 56
+#define __NR_setpgid 57
+#define __NR_ulimit 58
+#define __NR_oldolduname 59
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_sgetmask 68
+#define __NR_ssetmask 69
+#define __NR_setreuid 70
+#define __NR_setregid 71
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_getgroups 80
+#define __NR_setgroups 81
+#define __NR_select 82
+#define __NR_symlink 83
+#define __NR_oldlstat 84
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_fchown 95
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_profil 98
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_ioperm 101
+#define __NR_socketcall 102
+#define __NR_syslog 103
+#define __NR_setitimer 104
+#define __NR_getitimer 105
+#define __NR_stat 106
+#define __NR_lstat 107
+#define __NR_fstat 108
+#define __NR_olduname 109
+#define __NR_iopl 110
+#define __NR_vhangup 111
+#define __NR_idle 112
+#define __NR_vm86old 113
+#define __NR_wait4 114
+#define __NR_swapoff 115
+#define __NR_sysinfo 116
+#define __NR_ipc 117
+#define __NR_fsync 118
+#define __NR_sigreturn 119
+#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
+#define __NR_modify_ldt 123
+#define __NR_adjtimex 124
+#define __NR_mprotect 125
+#define __NR_sigprocmask 126
+#define __NR_create_module 127
+#define __NR_init_module 128
+#define __NR_delete_module 129
+#define __NR_get_kernel_syms 130
+#define __NR_quotactl 131
+#define __NR_getpgid 132
+#define __NR_fchdir 133
+#define __NR_bdflush 134
+#define __NR_sysfs 135
+#define __NR_personality 136
+#define __NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define __NR_setfsuid 138
+#define __NR_setfsgid 139
+#define __NR__llseek 140
+#define __NR_getdents 141
+#define __NR__newselect 142
+#define __NR_flock 143
+#define __NR_msync 144
+#define __NR_readv 145
+#define __NR_writev 146
+#define __NR_getsid 147
+#define __NR_fdatasync 148
+#define __NR__sysctl 149
+#define __NR_mlock 150
+#define __NR_munlock 151
+#define __NR_mlockall 152
+#define __NR_munlockall 153
+#define __NR_sched_setparam 154
+#define __NR_sched_getparam 155
+#define __NR_sched_setscheduler 156
+#define __NR_sched_getscheduler 157
+#define __NR_sched_yield 158
+#define __NR_sched_get_priority_max 159
+#define __NR_sched_get_priority_min 160
+#define __NR_sched_rr_get_interval 161
+#define __NR_nanosleep 162
+#define __NR_mremap 163
+#define __NR_setresuid 164
+#define __NR_getresuid 165
+#define __NR_vm86 166
+#define __NR_query_module 167
+#define __NR_poll 168
+#define __NR_nfsservctl 169
+#define __NR_setresgid 170
+#define __NR_getresgid 171
+#define __NR_prctl 172
+#define __NR_rt_sigreturn 173
+#define __NR_rt_sigaction 174
+#define __NR_rt_sigprocmask 175
+#define __NR_rt_sigpending 176
+#define __NR_rt_sigtimedwait 177
+#define __NR_rt_sigqueueinfo 178
+#define __NR_rt_sigsuspend 179
+#define __NR_pread64 180
+#define __NR_pwrite64 181
+#define __NR_chown 182
+#define __NR_getcwd 183
+#define __NR_capget 184
+#define __NR_capset 185
+#define __NR_sigaltstack 186
+#define __NR_sendfile 187
+#define __NR_getpmsg 188 /* some people actually want streams */
+#define __NR_putpmsg 189 /* some people actually want streams */
+#define __NR_vfork 190
+#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */
+#define __NR_mmap2 192
+#define __NR_truncate64 193
+#define __NR_ftruncate64 194
+#define __NR_stat64 195
+#define __NR_lstat64 196
+#define __NR_fstat64 197
+#define __NR_lchown32 198
+#define __NR_getuid32 199
+#define __NR_getgid32 200
+#define __NR_geteuid32 201
+#define __NR_getegid32 202
+#define __NR_setreuid32 203
+#define __NR_setregid32 204
+#define __NR_getgroups32 205
+#define __NR_setgroups32 206
+#define __NR_fchown32 207
+#define __NR_setresuid32 208
+#define __NR_getresuid32 209
+#define __NR_setresgid32 210
+#define __NR_getresgid32 211
+#define __NR_chown32 212
+#define __NR_setuid32 213
+#define __NR_setgid32 214
+#define __NR_setfsuid32 215
+#define __NR_setfsgid32 216
+#define __NR_pivot_root 217
+#define __NR_mincore 218
+#define __NR_madvise 219
+#define __NR_madvise1 219 /* delete when C lib stub is removed */
+#define __NR_getdents64 220
+#define __NR_fcntl64 221
+/* 223 is unused */
+#define __NR_gettid 224
+#define __NR_readahead 225
+#define __NR_setxattr 226
+#define __NR_lsetxattr 227
+#define __NR_fsetxattr 228
+#define __NR_getxattr 229
+#define __NR_lgetxattr 230
+#define __NR_fgetxattr 231
+#define __NR_listxattr 232
+#define __NR_llistxattr 233
+#define __NR_flistxattr 234
+#define __NR_removexattr 235
+#define __NR_lremovexattr 236
+#define __NR_fremovexattr 237
+#define __NR_tkill 238
+#define __NR_sendfile64 239
+#define __NR_futex 240
+#define __NR_sched_setaffinity 241
+#define __NR_sched_getaffinity 242
+#define __NR_set_thread_area 243
+#define __NR_get_thread_area 244
+#define __NR_io_setup 245
+#define __NR_io_destroy 246
+#define __NR_io_getevents 247
+#define __NR_io_submit 248
+#define __NR_io_cancel 249
+#define __NR_fadvise64 250
+/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */
+#define __NR_exit_group 252
+#define __NR_lookup_dcookie 253
+#define __NR_epoll_create 254
+#define __NR_epoll_ctl 255
+#define __NR_epoll_wait 256
+#define __NR_remap_file_pages 257
+#define __NR_set_tid_address 258
+#define __NR_timer_create 259
+#define __NR_timer_settime (__NR_timer_create+1)
+#define __NR_timer_gettime (__NR_timer_create+2)
+#define __NR_timer_getoverrun (__NR_timer_create+3)
+#define __NR_timer_delete (__NR_timer_create+4)
+#define __NR_clock_settime (__NR_timer_create+5)
+#define __NR_clock_gettime (__NR_timer_create+6)
+#define __NR_clock_getres (__NR_timer_create+7)
+#define __NR_clock_nanosleep (__NR_timer_create+8)
+#define __NR_statfs64 268
+#define __NR_fstatfs64 269
+#define __NR_tgkill 270
+#define __NR_utimes 271
+#define __NR_fadvise64_64 272
+#define __NR_vserver 273
+#define __NR_mbind 274
+#define __NR_get_mempolicy 275
+#define __NR_set_mempolicy 276
+#define __NR_mq_open 277
+#define __NR_mq_unlink (__NR_mq_open+1)
+#define __NR_mq_timedsend (__NR_mq_open+2)
+#define __NR_mq_timedreceive (__NR_mq_open+3)
+#define __NR_mq_notify (__NR_mq_open+4)
+#define __NR_mq_getsetattr (__NR_mq_open+5)
+#define __NR_kexec_load 283
+#define __NR_waitid 284
+/* #define __NR_sys_setaltroot 285 */
+#define __NR_add_key 286
+#define __NR_request_key 287
+#define __NR_keyctl 288
+#define __NR_ioprio_set 289
+#define __NR_ioprio_get 290
+#define __NR_inotify_init 291
+#define __NR_inotify_add_watch 292
+#define __NR_inotify_rm_watch 293
+#define __NR_migrate_pages 294
+#define __NR_openat 295
+#define __NR_mkdirat 296
+#define __NR_mknodat 297
+#define __NR_fchownat 298
+#define __NR_futimesat 299
+#define __NR_fstatat64 300
+#define __NR_unlinkat 301
+#define __NR_renameat 302
+#define __NR_linkat 303
+#define __NR_symlinkat 304
+#define __NR_readlinkat 305
+#define __NR_fchmodat 306
+#define __NR_faccessat 307
+#define __NR_pselect6 308
+#define __NR_ppoll 309
+#define __NR_unshare 310
+#define __NR_set_robust_list 311
+#define __NR_get_robust_list 312
+#define __NR_splice 313
+#define __NR_sync_file_range 314
+#define __NR_tee 315
+#define __NR_vmsplice 316
+#define __NR_move_pages 317
+#define __NR_getcpu 318
+#define __NR_epoll_pwait 319
+#define __NR_utimensat 320
+#define __NR_signalfd 321
+#define __NR_timerfd_create 322
+#define __NR_eventfd 323
+#define __NR_fallocate 324
+#define __NR_timerfd_settime 325
+#define __NR_timerfd_gettime 326
+#define __NR_signalfd4 327
+#define __NR_eventfd2 328
+#define __NR_epoll_create1 329
+#define __NR_dup3 330
+#define __NR_pipe2 331
+#define __NR_inotify_init1 332
+#define __NR_preadv 333
+#define __NR_pwritev 334
+#define __NR_rt_tgsigqueueinfo 335
+#define __NR_perf_event_open 336
+#define __NR_recvmmsg 337
+
+#ifdef __KERNEL__
+
+#define NR_syscalls 338
+
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_OLD_STAT
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_IPC
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_SYS_OLD_UNAME
+#define __ARCH_WANT_SYS_OLD_MMAP
+#define __ARCH_WANT_SYS_OLD_SELECT
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#ifndef cond_syscall
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_X86_UNISTD_32_H */
diff --git a/smartt-perf/util/include/asm/x86/unistd_64.h b/smartt-perf/util/include/asm/x86/unistd_64.h
new file mode 100644
index 0000000..ff4307b
--- /dev/null
+++ b/smartt-perf/util/include/asm/x86/unistd_64.h
@@ -0,0 +1,709 @@
+#ifndef _ASM_X86_UNISTD_64_H
+#define _ASM_X86_UNISTD_64_H
+
+#ifndef __SYSCALL
+#define __SYSCALL(a, b)
+#endif
+
+/*
+ * This file contains the system call numbers.
+ *
+ * Note: holes are not allowed.
+ */
+
+/* at least 8 syscall per cacheline */
+#define __NR_read 0
+__SYSCALL(__NR_read, sys_read)
+#define __NR_write 1
+__SYSCALL(__NR_write, sys_write)
+#define __NR_open 2
+__SYSCALL(__NR_open, sys_open)
+#define __NR_close 3
+__SYSCALL(__NR_close, sys_close)
+#define __NR_stat 4
+__SYSCALL(__NR_stat, sys_newstat)
+#define __NR_fstat 5
+__SYSCALL(__NR_fstat, sys_newfstat)
+#define __NR_lstat 6
+__SYSCALL(__NR_lstat, sys_newlstat)
+#define __NR_poll 7
+__SYSCALL(__NR_poll, sys_poll)
+
+#define __NR_lseek 8
+__SYSCALL(__NR_lseek, sys_lseek)
+#define __NR_mmap 9
+__SYSCALL(__NR_mmap, sys_mmap)
+#define __NR_mprotect 10
+__SYSCALL(__NR_mprotect, sys_mprotect)
+#define __NR_munmap 11
+__SYSCALL(__NR_munmap, sys_munmap)
+#define __NR_brk 12
+__SYSCALL(__NR_brk, sys_brk)
+#define __NR_rt_sigaction 13
+__SYSCALL(__NR_rt_sigaction, sys_rt_sigaction)
+#define __NR_rt_sigprocmask 14
+__SYSCALL(__NR_rt_sigprocmask, sys_rt_sigprocmask)
+#define __NR_rt_sigreturn 15
+__SYSCALL(__NR_rt_sigreturn, stub_rt_sigreturn)
+
+#define __NR_ioctl 16
+__SYSCALL(__NR_ioctl, sys_ioctl)
+#define __NR_pread64 17
+__SYSCALL(__NR_pread64, sys_pread64)
+#define __NR_pwrite64 18
+__SYSCALL(__NR_pwrite64, sys_pwrite64)
+#define __NR_readv 19
+__SYSCALL(__NR_readv, sys_readv)
+#define __NR_writev 20
+__SYSCALL(__NR_writev, sys_writev)
+#define __NR_access 21
+__SYSCALL(__NR_access, sys_access)
+#define __NR_pipe 22
+__SYSCALL(__NR_pipe, sys_pipe)
+#define __NR_select 23
+__SYSCALL(__NR_select, sys_select)
+
+#define __NR_sched_yield 24
+__SYSCALL(__NR_sched_yield, sys_sched_yield)
+#define __NR_mremap 25
+__SYSCALL(__NR_mremap, sys_mremap)
+#define __NR_msync 26
+__SYSCALL(__NR_msync, sys_msync)
+#define __NR_mincore 27
+__SYSCALL(__NR_mincore, sys_mincore)
+#define __NR_madvise 28
+__SYSCALL(__NR_madvise, sys_madvise)
+#define __NR_shmget 29
+__SYSCALL(__NR_shmget, sys_shmget)
+#define __NR_shmat 30
+__SYSCALL(__NR_shmat, sys_shmat)
+#define __NR_shmctl 31
+__SYSCALL(__NR_shmctl, sys_shmctl)
+
+#define __NR_dup 32
+__SYSCALL(__NR_dup, sys_dup)
+#define __NR_dup2 33
+__SYSCALL(__NR_dup2, sys_dup2)
+#define __NR_pause 34
+__SYSCALL(__NR_pause, sys_pause)
+#define __NR_nanosleep 35
+__SYSCALL(__NR_nanosleep, sys_nanosleep)
+#define __NR_getitimer 36
+__SYSCALL(__NR_getitimer, sys_getitimer)
+#define __NR_alarm 37
+__SYSCALL(__NR_alarm, sys_alarm)
+#define __NR_setitimer 38
+__SYSCALL(__NR_setitimer, sys_setitimer)
+#define __NR_getpid 39
+__SYSCALL(__NR_getpid, sys_getpid)
+
+#define __NR_sendfile 40
+__SYSCALL(__NR_sendfile, sys_sendfile64)
+#define __NR_socket 41
+__SYSCALL(__NR_socket, sys_socket)
+#define __NR_connect 42
+__SYSCALL(__NR_connect, sys_connect)
+#define __NR_accept 43
+__SYSCALL(__NR_accept, sys_accept)
+#define __NR_sendto 44
+__SYSCALL(__NR_sendto, sys_sendto)
+#define __NR_recvfrom 45
+__SYSCALL(__NR_recvfrom, sys_recvfrom)
+#define __NR_sendmsg 46
+__SYSCALL(__NR_sendmsg, sys_sendmsg)
+#define __NR_recvmsg 47
+__SYSCALL(__NR_recvmsg, sys_recvmsg)
+
+#define __NR_shutdown 48
+__SYSCALL(__NR_shutdown, sys_shutdown)
+#define __NR_bind 49
+__SYSCALL(__NR_bind, sys_bind)
+#define __NR_listen 50
+__SYSCALL(__NR_listen, sys_listen)
+#define __NR_getsockname 51
+__SYSCALL(__NR_getsockname, sys_getsockname)
+#define __NR_getpeername 52
+__SYSCALL(__NR_getpeername, sys_getpeername)
+#define __NR_socketpair 53
+__SYSCALL(__NR_socketpair, sys_socketpair)
+#define __NR_setsockopt 54
+__SYSCALL(__NR_setsockopt, sys_setsockopt)
+#define __NR_getsockopt 55
+__SYSCALL(__NR_getsockopt, sys_getsockopt)
+
+#define __NR_clone 56
+__SYSCALL(__NR_clone, stub_clone)
+#define __NR_fork 57
+__SYSCALL(__NR_fork, stub_fork)
+#define __NR_vfork 58
+__SYSCALL(__NR_vfork, stub_vfork)
+#define __NR_execve 59
+__SYSCALL(__NR_execve, stub_execve)
+#define __NR_exit 60
+__SYSCALL(__NR_exit, sys_exit)
+#define __NR_wait4 61
+__SYSCALL(__NR_wait4, sys_wait4)
+#define __NR_kill 62
+__SYSCALL(__NR_kill, sys_kill)
+#define __NR_uname 63
+__SYSCALL(__NR_uname, sys_newuname)
+
+#define __NR_semget 64
+__SYSCALL(__NR_semget, sys_semget)
+#define __NR_semop 65
+__SYSCALL(__NR_semop, sys_semop)
+#define __NR_semctl 66
+__SYSCALL(__NR_semctl, sys_semctl)
+#define __NR_shmdt 67
+__SYSCALL(__NR_shmdt, sys_shmdt)
+#define __NR_msgget 68
+__SYSCALL(__NR_msgget, sys_msgget)
+#define __NR_msgsnd 69
+__SYSCALL(__NR_msgsnd, sys_msgsnd)
+#define __NR_msgrcv 70
+__SYSCALL(__NR_msgrcv, sys_msgrcv)
+#define __NR_msgctl 71
+__SYSCALL(__NR_msgctl, sys_msgctl)
+
+#define __NR_fcntl 72
+__SYSCALL(__NR_fcntl, sys_fcntl)
+#define __NR_flock 73
+__SYSCALL(__NR_flock, sys_flock)
+#define __NR_fsync 74
+__SYSCALL(__NR_fsync, sys_fsync)
+#define __NR_fdatasync 75
+__SYSCALL(__NR_fdatasync, sys_fdatasync)
+#define __NR_truncate 76
+__SYSCALL(__NR_truncate, sys_truncate)
+#define __NR_ftruncate 77
+__SYSCALL(__NR_ftruncate, sys_ftruncate)
+#define __NR_getdents 78
+__SYSCALL(__NR_getdents, sys_getdents)
+#define __NR_getcwd 79
+__SYSCALL(__NR_getcwd, sys_getcwd)
+
+#define __NR_chdir 80
+__SYSCALL(__NR_chdir, sys_chdir)
+#define __NR_fchdir 81
+__SYSCALL(__NR_fchdir, sys_fchdir)
+#define __NR_rename 82
+__SYSCALL(__NR_rename, sys_rename)
+#define __NR_mkdir 83
+__SYSCALL(__NR_mkdir, sys_mkdir)
+#define __NR_rmdir 84
+__SYSCALL(__NR_rmdir, sys_rmdir)
+#define __NR_creat 85
+__SYSCALL(__NR_creat, sys_creat)
+#define __NR_link 86
+__SYSCALL(__NR_link, sys_link)
+#define __NR_unlink 87
+__SYSCALL(__NR_unlink, sys_unlink)
+
+#define __NR_symlink 88
+__SYSCALL(__NR_symlink, sys_symlink)
+#define __NR_readlink 89
+__SYSCALL(__NR_readlink, sys_readlink)
+#define __NR_chmod 90
+__SYSCALL(__NR_chmod, sys_chmod)
+#define __NR_fchmod 91
+__SYSCALL(__NR_fchmod, sys_fchmod)
+#define __NR_chown 92
+__SYSCALL(__NR_chown, sys_chown)
+#define __NR_fchown 93
+__SYSCALL(__NR_fchown, sys_fchown)
+#define __NR_lchown 94
+__SYSCALL(__NR_lchown, sys_lchown)
+#define __NR_umask 95
+__SYSCALL(__NR_umask, sys_umask)
+
+#define __NR_gettimeofday 96
+__SYSCALL(__NR_gettimeofday, sys_gettimeofday)
+#define __NR_getrlimit 97
+__SYSCALL(__NR_getrlimit, sys_getrlimit)
+#define __NR_getrusage 98
+__SYSCALL(__NR_getrusage, sys_getrusage)
+#define __NR_sysinfo 99
+__SYSCALL(__NR_sysinfo, sys_sysinfo)
+#define __NR_times 100
+__SYSCALL(__NR_times, sys_times)
+#define __NR_ptrace 101
+__SYSCALL(__NR_ptrace, sys_ptrace)
+#define __NR_getuid 102
+__SYSCALL(__NR_getuid, sys_getuid)
+#define __NR_syslog 103
+__SYSCALL(__NR_syslog, sys_syslog)
+
+/* at the very end the stuff that never runs during the benchmarks */
+#define __NR_getgid 104
+__SYSCALL(__NR_getgid, sys_getgid)
+#define __NR_setuid 105
+__SYSCALL(__NR_setuid, sys_setuid)
+#define __NR_setgid 106
+__SYSCALL(__NR_setgid, sys_setgid)
+#define __NR_geteuid 107
+__SYSCALL(__NR_geteuid, sys_geteuid)
+#define __NR_getegid 108
+__SYSCALL(__NR_getegid, sys_getegid)
+#define __NR_setpgid 109
+__SYSCALL(__NR_setpgid, sys_setpgid)
+#define __NR_getppid 110
+__SYSCALL(__NR_getppid, sys_getppid)
+#define __NR_getpgrp 111
+__SYSCALL(__NR_getpgrp, sys_getpgrp)
+
+#define __NR_setsid 112
+__SYSCALL(__NR_setsid, sys_setsid)
+#define __NR_setreuid 113
+__SYSCALL(__NR_setreuid, sys_setreuid)
+#define __NR_setregid 114
+__SYSCALL(__NR_setregid, sys_setregid)
+#define __NR_getgroups 115
+__SYSCALL(__NR_getgroups, sys_getgroups)
+#define __NR_setgroups 116
+__SYSCALL(__NR_setgroups, sys_setgroups)
+#define __NR_setresuid 117
+__SYSCALL(__NR_setresuid, sys_setresuid)
+#define __NR_getresuid 118
+__SYSCALL(__NR_getresuid, sys_getresuid)
+#define __NR_setresgid 119
+__SYSCALL(__NR_setresgid, sys_setresgid)
+
+#define __NR_getresgid 120
+__SYSCALL(__NR_getresgid, sys_getresgid)
+#define __NR_getpgid 121
+__SYSCALL(__NR_getpgid, sys_getpgid)
+#define __NR_setfsuid 122
+__SYSCALL(__NR_setfsuid, sys_setfsuid)
+#define __NR_setfsgid 123
+__SYSCALL(__NR_setfsgid, sys_setfsgid)
+#define __NR_getsid 124
+__SYSCALL(__NR_getsid, sys_getsid)
+#define __NR_capget 125
+__SYSCALL(__NR_capget, sys_capget)
+#define __NR_capset 126
+__SYSCALL(__NR_capset, sys_capset)
+
+#define __NR_rt_sigpending 127
+__SYSCALL(__NR_rt_sigpending, sys_rt_sigpending)
+#define __NR_rt_sigtimedwait 128
+__SYSCALL(__NR_rt_sigtimedwait, sys_rt_sigtimedwait)
+#define __NR_rt_sigqueueinfo 129
+__SYSCALL(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo)
+#define __NR_rt_sigsuspend 130
+__SYSCALL(__NR_rt_sigsuspend, sys_rt_sigsuspend)
+#define __NR_sigaltstack 131
+__SYSCALL(__NR_sigaltstack, stub_sigaltstack)
+#define __NR_utime 132
+__SYSCALL(__NR_utime, sys_utime)
+#define __NR_mknod 133
+__SYSCALL(__NR_mknod, sys_mknod)
+
+/* Only needed for a.out */
+#define __NR_uselib 134
+__SYSCALL(__NR_uselib, sys_ni_syscall)
+#define __NR_personality 135
+__SYSCALL(__NR_personality, sys_personality)
+
+#define __NR_ustat 136
+__SYSCALL(__NR_ustat, sys_ustat)
+#define __NR_statfs 137
+__SYSCALL(__NR_statfs, sys_statfs)
+#define __NR_fstatfs 138
+__SYSCALL(__NR_fstatfs, sys_fstatfs)
+#define __NR_sysfs 139
+__SYSCALL(__NR_sysfs, sys_sysfs)
+
+#define __NR_getpriority 140
+__SYSCALL(__NR_getpriority, sys_getpriority)
+#define __NR_setpriority 141
+__SYSCALL(__NR_setpriority, sys_setpriority)
+#define __NR_sched_setparam 142
+__SYSCALL(__NR_sched_setparam, sys_sched_setparam)
+#define __NR_sched_getparam 143
+__SYSCALL(__NR_sched_getparam, sys_sched_getparam)
+#define __NR_sched_setscheduler 144
+__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler)
+#define __NR_sched_getscheduler 145
+__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler)
+#define __NR_sched_get_priority_max 146
+__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max)
+#define __NR_sched_get_priority_min 147
+__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min)
+#define __NR_sched_rr_get_interval 148
+__SYSCALL(__NR_sched_rr_get_interval, sys_sched_rr_get_interval)
+
+#define __NR_mlock 149
+__SYSCALL(__NR_mlock, sys_mlock)
+#define __NR_munlock 150
+__SYSCALL(__NR_munlock, sys_munlock)
+#define __NR_mlockall 151
+__SYSCALL(__NR_mlockall, sys_mlockall)
+#define __NR_munlockall 152
+__SYSCALL(__NR_munlockall, sys_munlockall)
+
+#define __NR_vhangup 153
+__SYSCALL(__NR_vhangup, sys_vhangup)
+
+#define __NR_modify_ldt 154
+__SYSCALL(__NR_modify_ldt, sys_modify_ldt)
+
+#define __NR_pivot_root 155
+__SYSCALL(__NR_pivot_root, sys_pivot_root)
+
+#define __NR__sysctl 156
+__SYSCALL(__NR__sysctl, sys_sysctl)
+
+#define __NR_prctl 157
+__SYSCALL(__NR_prctl, sys_prctl)
+#define __NR_arch_prctl 158
+__SYSCALL(__NR_arch_prctl, sys_arch_prctl)
+
+#define __NR_adjtimex 159
+__SYSCALL(__NR_adjtimex, sys_adjtimex)
+
+#define __NR_setrlimit 160
+__SYSCALL(__NR_setrlimit, sys_setrlimit)
+
+#define __NR_chroot 161
+__SYSCALL(__NR_chroot, sys_chroot)
+
+#define __NR_sync 162
+__SYSCALL(__NR_sync, sys_sync)
+
+#define __NR_acct 163
+__SYSCALL(__NR_acct, sys_acct)
+
+#define __NR_settimeofday 164
+__SYSCALL(__NR_settimeofday, sys_settimeofday)
+
+#define __NR_mount 165
+__SYSCALL(__NR_mount, sys_mount)
+#define __NR_umount2 166
+__SYSCALL(__NR_umount2, sys_umount)
+
+#define __NR_swapon 167
+__SYSCALL(__NR_swapon, sys_swapon)
+#define __NR_swapoff 168
+__SYSCALL(__NR_swapoff, sys_swapoff)
+
+#define __NR_reboot 169
+__SYSCALL(__NR_reboot, sys_reboot)
+
+#define __NR_sethostname 170
+__SYSCALL(__NR_sethostname, sys_sethostname)
+#define __NR_setdomainname 171
+__SYSCALL(__NR_setdomainname, sys_setdomainname)
+
+#define __NR_iopl 172
+__SYSCALL(__NR_iopl, stub_iopl)
+#define __NR_ioperm 173
+__SYSCALL(__NR_ioperm, sys_ioperm)
+
+#define __NR_create_module 174
+__SYSCALL(__NR_create_module, sys_ni_syscall)
+#define __NR_init_module 175
+__SYSCALL(__NR_init_module, sys_init_module)
+#define __NR_delete_module 176
+__SYSCALL(__NR_delete_module, sys_delete_module)
+#define __NR_get_kernel_syms 177
+__SYSCALL(__NR_get_kernel_syms, sys_ni_syscall)
+#define __NR_query_module 178
+__SYSCALL(__NR_query_module, sys_ni_syscall)
+
+#define __NR_quotactl 179
+__SYSCALL(__NR_quotactl, sys_quotactl)
+
+#define __NR_nfsservctl 180
+__SYSCALL(__NR_nfsservctl, sys_nfsservctl)
+
+/* reserved for LiS/STREAMS */
+#define __NR_getpmsg 181
+__SYSCALL(__NR_getpmsg, sys_ni_syscall)
+#define __NR_putpmsg 182
+__SYSCALL(__NR_putpmsg, sys_ni_syscall)
+
+/* reserved for AFS */
+#define __NR_afs_syscall 183
+__SYSCALL(__NR_afs_syscall, sys_ni_syscall)
+
+/* reserved for tux */
+#define __NR_tuxcall 184
+__SYSCALL(__NR_tuxcall, sys_ni_syscall)
+
+#define __NR_security 185
+__SYSCALL(__NR_security, sys_ni_syscall)
+
+#define __NR_gettid 186
+__SYSCALL(__NR_gettid, sys_gettid)
+
+#define __NR_readahead 187
+__SYSCALL(__NR_readahead, sys_readahead)
+#define __NR_setxattr 188
+__SYSCALL(__NR_setxattr, sys_setxattr)
+#define __NR_lsetxattr 189
+__SYSCALL(__NR_lsetxattr, sys_lsetxattr)
+#define __NR_fsetxattr 190
+__SYSCALL(__NR_fsetxattr, sys_fsetxattr)
+#define __NR_getxattr 191
+__SYSCALL(__NR_getxattr, sys_getxattr)
+#define __NR_lgetxattr 192
+__SYSCALL(__NR_lgetxattr, sys_lgetxattr)
+#define __NR_fgetxattr 193
+__SYSCALL(__NR_fgetxattr, sys_fgetxattr)
+#define __NR_listxattr 194
+__SYSCALL(__NR_listxattr, sys_listxattr)
+#define __NR_llistxattr 195
+__SYSCALL(__NR_llistxattr, sys_llistxattr)
+#define __NR_flistxattr 196
+__SYSCALL(__NR_flistxattr, sys_flistxattr)
+#define __NR_removexattr 197
+__SYSCALL(__NR_removexattr, sys_removexattr)
+#define __NR_lremovexattr 198
+__SYSCALL(__NR_lremovexattr, sys_lremovexattr)
+#define __NR_fremovexattr 199
+__SYSCALL(__NR_fremovexattr, sys_fremovexattr)
+#define __NR_tkill 200
+__SYSCALL(__NR_tkill, sys_tkill)
+#define __NR_time 201
+__SYSCALL(__NR_time, sys_time)
+#define __NR_futex 202
+__SYSCALL(__NR_futex, sys_futex)
+#define __NR_sched_setaffinity 203
+__SYSCALL(__NR_sched_setaffinity, sys_sched_setaffinity)
+#define __NR_sched_getaffinity 204
+__SYSCALL(__NR_sched_getaffinity, sys_sched_getaffinity)
+#define __NR_set_thread_area 205
+__SYSCALL(__NR_set_thread_area, sys_ni_syscall) /* use arch_prctl */
+#define __NR_io_setup 206
+__SYSCALL(__NR_io_setup, sys_io_setup)
+#define __NR_io_destroy 207
+__SYSCALL(__NR_io_destroy, sys_io_destroy)
+#define __NR_io_getevents 208
+__SYSCALL(__NR_io_getevents, sys_io_getevents)
+#define __NR_io_submit 209
+__SYSCALL(__NR_io_submit, sys_io_submit)
+#define __NR_io_cancel 210
+__SYSCALL(__NR_io_cancel, sys_io_cancel)
+#define __NR_get_thread_area 211
+__SYSCALL(__NR_get_thread_area, sys_ni_syscall) /* use arch_prctl */
+#define __NR_lookup_dcookie 212
+__SYSCALL(__NR_lookup_dcookie, sys_lookup_dcookie)
+#define __NR_epoll_create 213
+__SYSCALL(__NR_epoll_create, sys_epoll_create)
+#define __NR_epoll_ctl_old 214
+__SYSCALL(__NR_epoll_ctl_old, sys_ni_syscall)
+#define __NR_epoll_wait_old 215
+__SYSCALL(__NR_epoll_wait_old, sys_ni_syscall)
+#define __NR_remap_file_pages 216
+__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages)
+#define __NR_getdents64 217
+__SYSCALL(__NR_getdents64, sys_getdents64)
+#define __NR_set_tid_address 218
+__SYSCALL(__NR_set_tid_address, sys_set_tid_address)
+#define __NR_restart_syscall 219
+__SYSCALL(__NR_restart_syscall, sys_restart_syscall)
+#define __NR_semtimedop 220
+__SYSCALL(__NR_semtimedop, sys_semtimedop)
+#define __NR_fadvise64 221
+__SYSCALL(__NR_fadvise64, sys_fadvise64)
+#define __NR_timer_create 222
+__SYSCALL(__NR_timer_create, sys_timer_create)
+#define __NR_timer_settime 223
+__SYSCALL(__NR_timer_settime, sys_timer_settime)
+#define __NR_timer_gettime 224
+__SYSCALL(__NR_timer_gettime, sys_timer_gettime)
+#define __NR_timer_getoverrun 225
+__SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun)
+#define __NR_timer_delete 226
+__SYSCALL(__NR_timer_delete, sys_timer_delete)
+#define __NR_clock_settime 227
+__SYSCALL(__NR_clock_settime, sys_clock_settime)
+#define __NR_clock_gettime 228
+__SYSCALL(__NR_clock_gettime, sys_clock_gettime)
+#define __NR_clock_getres 229
+__SYSCALL(__NR_clock_getres, sys_clock_getres)
+#define __NR_clock_nanosleep 230
+__SYSCALL(__NR_clock_nanosleep, sys_clock_nanosleep)
+#define __NR_exit_group 231
+__SYSCALL(__NR_exit_group, sys_exit_group)
+#define __NR_epoll_wait 232
+__SYSCALL(__NR_epoll_wait, sys_epoll_wait)
+#define __NR_epoll_ctl 233
+__SYSCALL(__NR_epoll_ctl, sys_epoll_ctl)
+#define __NR_tgkill 234
+__SYSCALL(__NR_tgkill, sys_tgkill)
+#define __NR_utimes 235
+__SYSCALL(__NR_utimes, sys_utimes)
+#define __NR_vserver 236
+__SYSCALL(__NR_vserver, sys_ni_syscall)
+#define __NR_mbind 237
+__SYSCALL(__NR_mbind, sys_mbind)
+#define __NR_set_mempolicy 238
+__SYSCALL(__NR_set_mempolicy, sys_set_mempolicy)
+#define __NR_get_mempolicy 239
+__SYSCALL(__NR_get_mempolicy, sys_get_mempolicy)
+#define __NR_mq_open 240
+__SYSCALL(__NR_mq_open, sys_mq_open)
+#define __NR_mq_unlink 241
+__SYSCALL(__NR_mq_unlink, sys_mq_unlink)
+#define __NR_mq_timedsend 242
+__SYSCALL(__NR_mq_timedsend, sys_mq_timedsend)
+#define __NR_mq_timedreceive 243
+__SYSCALL(__NR_mq_timedreceive, sys_mq_timedreceive)
+#define __NR_mq_notify 244
+__SYSCALL(__NR_mq_notify, sys_mq_notify)
+#define __NR_mq_getsetattr 245
+__SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr)
+#define __NR_kexec_load 246
+__SYSCALL(__NR_kexec_load, sys_kexec_load)
+#define __NR_waitid 247
+__SYSCALL(__NR_waitid, sys_waitid)
+#define __NR_add_key 248
+__SYSCALL(__NR_add_key, sys_add_key)
+#define __NR_request_key 249
+__SYSCALL(__NR_request_key, sys_request_key)
+#define __NR_keyctl 250
+__SYSCALL(__NR_keyctl, sys_keyctl)
+#define __NR_ioprio_set 251
+__SYSCALL(__NR_ioprio_set, sys_ioprio_set)
+#define __NR_ioprio_get 252
+__SYSCALL(__NR_ioprio_get, sys_ioprio_get)
+#define __NR_inotify_init 253
+__SYSCALL(__NR_inotify_init, sys_inotify_init)
+#define __NR_inotify_add_watch 254
+__SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch)
+#define __NR_inotify_rm_watch 255
+__SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch)
+#define __NR_migrate_pages 256
+__SYSCALL(__NR_migrate_pages, sys_migrate_pages)
+#define __NR_openat 257
+__SYSCALL(__NR_openat, sys_openat)
+#define __NR_mkdirat 258
+__SYSCALL(__NR_mkdirat, sys_mkdirat)
+#define __NR_mknodat 259
+__SYSCALL(__NR_mknodat, sys_mknodat)
+#define __NR_fchownat 260
+__SYSCALL(__NR_fchownat, sys_fchownat)
+#define __NR_futimesat 261
+__SYSCALL(__NR_futimesat, sys_futimesat)
+#define __NR_newfstatat 262
+__SYSCALL(__NR_newfstatat, sys_newfstatat)
+#define __NR_unlinkat 263
+__SYSCALL(__NR_unlinkat, sys_unlinkat)
+#define __NR_renameat 264
+__SYSCALL(__NR_renameat, sys_renameat)
+#define __NR_linkat 265
+__SYSCALL(__NR_linkat, sys_linkat)
+#define __NR_symlinkat 266
+__SYSCALL(__NR_symlinkat, sys_symlinkat)
+#define __NR_readlinkat 267
+__SYSCALL(__NR_readlinkat, sys_readlinkat)
+#define __NR_fchmodat 268
+__SYSCALL(__NR_fchmodat, sys_fchmodat)
+#define __NR_faccessat 269
+__SYSCALL(__NR_faccessat, sys_faccessat)
+#define __NR_pselect6 270
+__SYSCALL(__NR_pselect6, sys_pselect6)
+#define __NR_ppoll 271
+__SYSCALL(__NR_ppoll, sys_ppoll)
+#define __NR_unshare 272
+__SYSCALL(__NR_unshare, sys_unshare)
+#define __NR_set_robust_list 273
+__SYSCALL(__NR_set_robust_list, sys_set_robust_list)
+#define __NR_get_robust_list 274
+__SYSCALL(__NR_get_robust_list, sys_get_robust_list)
+#define __NR_splice 275
+__SYSCALL(__NR_splice, sys_splice)
+#define __NR_tee 276
+__SYSCALL(__NR_tee, sys_tee)
+#define __NR_sync_file_range 277
+__SYSCALL(__NR_sync_file_range, sys_sync_file_range)
+#define __NR_vmsplice 278
+__SYSCALL(__NR_vmsplice, sys_vmsplice)
+#define __NR_move_pages 279
+__SYSCALL(__NR_move_pages, sys_move_pages)
+#define __NR_utimensat 280
+__SYSCALL(__NR_utimensat, sys_utimensat)
+#define __IGNORE_getcpu /* implemented as a vsyscall */
+#define __NR_epoll_pwait 281
+__SYSCALL(__NR_epoll_pwait, sys_epoll_pwait)
+#define __NR_signalfd 282
+__SYSCALL(__NR_signalfd, sys_signalfd)
+#define __NR_timerfd_create 283
+__SYSCALL(__NR_timerfd_create, sys_timerfd_create)
+#define __NR_eventfd 284
+__SYSCALL(__NR_eventfd, sys_eventfd)
+#define __NR_fallocate 285
+__SYSCALL(__NR_fallocate, sys_fallocate)
+#define __NR_timerfd_settime 286
+__SYSCALL(__NR_timerfd_settime, sys_timerfd_settime)
+#define __NR_timerfd_gettime 287
+__SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime)
+#define __NR_accept4 288
+__SYSCALL(__NR_accept4, sys_accept4)
+#define __NR_signalfd4 289
+__SYSCALL(__NR_signalfd4, sys_signalfd4)
+#define __NR_eventfd2 290
+__SYSCALL(__NR_eventfd2, sys_eventfd2)
+#define __NR_epoll_create1 291
+__SYSCALL(__NR_epoll_create1, sys_epoll_create1)
+#define __NR_dup3 292
+__SYSCALL(__NR_dup3, sys_dup3)
+#define __NR_pipe2 293
+__SYSCALL(__NR_pipe2, sys_pipe2)
+#define __NR_inotify_init1 294
+__SYSCALL(__NR_inotify_init1, sys_inotify_init1)
+#define __NR_preadv 295
+__SYSCALL(__NR_preadv, sys_preadv)
+#define __NR_pwritev 296
+__SYSCALL(__NR_pwritev, sys_pwritev)
+#define __NR_rt_tgsigqueueinfo 297
+__SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo)
+#define __NR_perf_event_open 298
+__SYSCALL(__NR_perf_event_open, sys_perf_event_open)
+#define __NR_recvmmsg 299
+__SYSCALL(__NR_recvmmsg, sys_recvmmsg)
+
+#ifndef __NO_STUBS
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_OLD_STAT
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_SYS_OLD_UNAME
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_COMPAT_SYS_TIME
+#endif /* __NO_STUBS */
+
+#ifdef __KERNEL__
+
+#ifndef COMPILE_OFFSETS
+#include <asm/asm-offsets.h>
+#define NR_syscalls (__NR_syscall_max + 1)
+#endif
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_X86_UNISTD_64_H */
diff --git a/smartt-perf/util/include/dwarf-regs.h b/smartt-perf/util/include/dwarf-regs.h
new file mode 100644
index 0000000..cf6727e
--- /dev/null
+++ b/smartt-perf/util/include/dwarf-regs.h
@@ -0,0 +1,8 @@
+#ifndef _PERF_DWARF_REGS_H_
+#define _PERF_DWARF_REGS_H_
+
+#ifdef DWARF_SUPPORT
+const char *get_arch_regstr(unsigned int n);
+#endif
+
+#endif
diff --git a/smartt-perf/util/include/linux/bitmap.h b/smartt-perf/util/include/linux/bitmap.h
new file mode 100644
index 0000000..eda4416
--- /dev/null
+++ b/smartt-perf/util/include/linux/bitmap.h
@@ -0,0 +1,35 @@
+#ifndef _PERF_BITOPS_H
+#define _PERF_BITOPS_H
+
+#include <string.h>
+#include <linux/bitops.h>
+
+int __bitmap_weight(const unsigned long *bitmap, int bits);
+
+#define BITMAP_LAST_WORD_MASK(nbits) \
+( \
+ ((nbits) % BITS_PER_LONG) ? \
+ (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
+)
+
+#define small_const_nbits(nbits) \
+ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
+
+static inline void bitmap_zero(unsigned long *dst, int nbits)
+{
+ if (small_const_nbits(nbits))
+ *dst = 0UL;
+ else {
+ int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ memset(dst, 0, len);
+ }
+}
+
+static inline int bitmap_weight(const unsigned long *src, int nbits)
+{
+ if (small_const_nbits(nbits))
+ return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
+ return __bitmap_weight(src, nbits);
+}
+
+#endif /* _PERF_BITOPS_H */
diff --git a/smartt-perf/util/include/linux/bitops.h b/smartt-perf/util/include/linux/bitops.h
new file mode 100644
index 0000000..305c848
--- /dev/null
+++ b/smartt-perf/util/include/linux/bitops.h
@@ -0,0 +1,33 @@
+#ifndef _PERF_LINUX_BITOPS_H_
+#define _PERF_LINUX_BITOPS_H_
+
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <asm/hweight.h>
+
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+
+static inline void set_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
+}
+
+static inline void clear_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG));
+}
+
+static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+ return ((1UL << (nr % BITS_PER_LONG)) &
+ (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
+}
+
+static inline unsigned long hweight_long(unsigned long w)
+{
+ return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
+}
+
+#endif
diff --git a/smartt-perf/util/include/linux/compiler.h b/smartt-perf/util/include/linux/compiler.h
new file mode 100644
index 0000000..791f9dd
--- /dev/null
+++ b/smartt-perf/util/include/linux/compiler.h
@@ -0,0 +1,12 @@
+#ifndef _PERF_LINUX_COMPILER_H_
+#define _PERF_LINUX_COMPILER_H_
+
+#ifndef __always_inline
+#define __always_inline inline
+#endif
+#define __user
+#define __attribute_const__
+
+#define __used __attribute__((__unused__))
+
+#endif
diff --git a/smartt-perf/util/include/linux/ctype.h b/smartt-perf/util/include/linux/ctype.h
new file mode 100644
index 0000000..a53d4ee
--- /dev/null
+++ b/smartt-perf/util/include/linux/ctype.h
@@ -0,0 +1 @@
+#include "../util.h"
diff --git a/smartt-perf/util/include/linux/hash.h b/smartt-perf/util/include/linux/hash.h
new file mode 100644
index 0000000..06d25c1
--- /dev/null
+++ b/smartt-perf/util/include/linux/hash.h
@@ -0,0 +1,70 @@
+#ifndef _LINUX_HASH_H
+#define _LINUX_HASH_H
+/* Fast hashing routine for ints, longs and pointers.
+ (C) 2002 William Lee Irwin III, IBM */
+
+/*
+ * Knuth recommends primes in approximately golden ratio to the maximum
+ * integer representable by a machine word for multiplicative hashing.
+ * Chuck Lever verified the effectiveness of this technique:
+ * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
+ *
+ * These primes are chosen to be bit-sparse, that is operations on
+ * them can use shifts and additions instead of multiplications for
+ * machines where multiplications are slow.
+ */
+
+#include <asm/types.h>
+
+/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
+#define GOLDEN_RATIO_PRIME_32 0x9e370001UL
+/* 2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
+#define GOLDEN_RATIO_PRIME_64 0x9e37fffffffc0001UL
+
+#if BITS_PER_LONG == 32
+#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_PRIME_32
+#define hash_long(val, bits) hash_32(val, bits)
+#elif BITS_PER_LONG == 64
+#define hash_long(val, bits) hash_64(val, bits)
+#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_PRIME_64
+#else
+#error Wordsize not 32 or 64
+#endif
+
+static inline u64 hash_64(u64 val, unsigned int bits)
+{
+ u64 hash = val;
+
+ /* Sigh, gcc can't optimise this alone like it does for 32 bits. */
+ u64 n = hash;
+ n <<= 18;
+ hash -= n;
+ n <<= 33;
+ hash -= n;
+ n <<= 3;
+ hash += n;
+ n <<= 3;
+ hash -= n;
+ n <<= 4;
+ hash += n;
+ n <<= 2;
+ hash += n;
+
+ /* High bits are more random, so use them. */
+ return hash >> (64 - bits);
+}
+
+static inline u32 hash_32(u32 val, unsigned int bits)
+{
+ /* On some cpus multiply is faster, on others gcc will do shifts */
+ u32 hash = val * GOLDEN_RATIO_PRIME_32;
+
+ /* High bits are more random, so use them. */
+ return hash >> (32 - bits);
+}
+
+static inline unsigned long hash_ptr(void *ptr, unsigned int bits)
+{
+ return hash_long((unsigned long)ptr, bits);
+}
+#endif /* _LINUX_HASH_H */
diff --git a/smartt-perf/util/include/linux/hw_breakpoint.h b/smartt-perf/util/include/linux/hw_breakpoint.h
new file mode 100644
index 0000000..d1e55fe
--- /dev/null
+++ b/smartt-perf/util/include/linux/hw_breakpoint.h
@@ -0,0 +1,150 @@
+#ifndef _LINUX_HW_BREAKPOINT_H
+#define _LINUX_HW_BREAKPOINT_H
+
+enum {
+ HW_BREAKPOINT_LEN_1 = 1,
+ HW_BREAKPOINT_LEN_2 = 2,
+ HW_BREAKPOINT_LEN_4 = 4,
+ HW_BREAKPOINT_LEN_8 = 8,
+};
+
+enum {
+ HW_BREAKPOINT_EMPTY = 0,
+ HW_BREAKPOINT_R = 1,
+ HW_BREAKPOINT_W = 2,
+ HW_BREAKPOINT_RW = HW_BREAKPOINT_R | HW_BREAKPOINT_W,
+ HW_BREAKPOINT_X = 4,
+ HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X,
+};
+
+enum bp_type_idx {
+ TYPE_INST = 0,
+#ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS
+ TYPE_DATA = 0,
+#else
+ TYPE_DATA = 1,
+#endif
+ TYPE_MAX
+};
+
+#ifdef __KERNEL__
+
+#include <linux/perf_event.h>
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+
+extern int __init init_hw_breakpoint(void);
+
+static inline void hw_breakpoint_init(struct perf_event_attr *attr)
+{
+ memset(attr, 0, sizeof(*attr));
+
+ attr->type = PERF_TYPE_BREAKPOINT;
+ attr->size = sizeof(*attr);
+ /*
+ * As it's for in-kernel or ptrace use, we want it to be pinned
+ * and to call its callback every hits.
+ */
+ attr->pinned = 1;
+ attr->sample_period = 1;
+}
+
+static inline void ptrace_breakpoint_init(struct perf_event_attr *attr)
+{
+ hw_breakpoint_init(attr);
+ attr->exclude_kernel = 1;
+}
+
+static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
+{
+ return bp->attr.bp_addr;
+}
+
+static inline int hw_breakpoint_type(struct perf_event *bp)
+{
+ return bp->attr.bp_type;
+}
+
+static inline unsigned long hw_breakpoint_len(struct perf_event *bp)
+{
+ return bp->attr.bp_len;
+}
+
+extern struct perf_event *
+register_user_hw_breakpoint(struct perf_event_attr *attr,
+ perf_overflow_handler_t triggered,
+ struct task_struct *tsk);
+
+/* FIXME: only change from the attr, and don't unregister */
+extern int
+modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr);
+
+/*
+ * Kernel breakpoints are not associated with any particular thread.
+ */
+extern struct perf_event *
+register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
+ perf_overflow_handler_t triggered,
+ int cpu);
+
+extern struct perf_event * __percpu *
+register_wide_hw_breakpoint(struct perf_event_attr *attr,
+ perf_overflow_handler_t triggered);
+
+extern int register_perf_hw_breakpoint(struct perf_event *bp);
+extern int __register_perf_hw_breakpoint(struct perf_event *bp);
+extern void unregister_hw_breakpoint(struct perf_event *bp);
+extern void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events);
+
+extern int dbg_reserve_bp_slot(struct perf_event *bp);
+extern int dbg_release_bp_slot(struct perf_event *bp);
+extern int reserve_bp_slot(struct perf_event *bp);
+extern void release_bp_slot(struct perf_event *bp);
+
+extern void flush_ptrace_hw_breakpoint(struct task_struct *tsk);
+
+static inline struct arch_hw_breakpoint *counter_arch_bp(struct perf_event *bp)
+{
+ return &bp->hw.info;
+}
+
+#else /* !CONFIG_HAVE_HW_BREAKPOINT */
+
+static inline int __init init_hw_breakpoint(void) { return 0; }
+
+static inline struct perf_event *
+register_user_hw_breakpoint(struct perf_event_attr *attr,
+ perf_overflow_handler_t triggered,
+ struct task_struct *tsk) { return NULL; }
+static inline int
+modify_user_hw_breakpoint(struct perf_event *bp,
+ struct perf_event_attr *attr) { return -ENOSYS; }
+static inline struct perf_event *
+register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
+ perf_overflow_handler_t triggered,
+ int cpu) { return NULL; }
+static inline struct perf_event * __percpu *
+register_wide_hw_breakpoint(struct perf_event_attr *attr,
+ perf_overflow_handler_t triggered) { return NULL; }
+static inline int
+register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
+static inline int
+__register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
+static inline void unregister_hw_breakpoint(struct perf_event *bp) { }
+static inline void
+unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events) { }
+static inline int
+reserve_bp_slot(struct perf_event *bp) {return -ENOSYS; }
+static inline void release_bp_slot(struct perf_event *bp) { }
+
+static inline void flush_ptrace_hw_breakpoint(struct task_struct *tsk) { }
+
+static inline struct arch_hw_breakpoint *counter_arch_bp(struct perf_event *bp)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_HW_BREAKPOINT_H */
diff --git a/smartt-perf/util/include/linux/kernel.h b/smartt-perf/util/include/linux/kernel.h
new file mode 100644
index 0000000..1eb804f
--- /dev/null
+++ b/smartt-perf/util/include/linux/kernel.h
@@ -0,0 +1,111 @@
+#ifndef PERF_LINUX_KERNEL_H_
+#define PERF_LINUX_KERNEL_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
+#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
+#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *)0)->member) * __mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+
+#ifndef max
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef min
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef BUG_ON
+#define BUG_ON(cond) assert(!(cond))
+#endif
+
+/*
+ * Both need more care to handle endianness
+ * (Don't use bitmap_copy_le() for now)
+ */
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+
+static inline int
+vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ int i;
+ ssize_t ssize = size;
+
+ i = vsnprintf(buf, size, fmt, args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)
+{
+ va_list args;
+ ssize_t ssize = size;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline unsigned long
+simple_strtoul(const char *nptr, char **endptr, int base)
+{
+ return strtoul(nptr, endptr, base);
+}
+
+int eprintf(int level,
+ const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+#endif
+
+#define pr_err(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warning(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug(fmt, ...) \
+ eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debugN(n, fmt, ...) \
+ eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+
+#endif
diff --git a/smartt-perf/util/include/linux/linkage.h b/smartt-perf/util/include/linux/linkage.h
new file mode 100644
index 0000000..06387cf
--- /dev/null
+++ b/smartt-perf/util/include/linux/linkage.h
@@ -0,0 +1,13 @@
+
+#ifndef PERF_LINUX_LINKAGE_H_
+#define PERF_LINUX_LINKAGE_H_
+
+/* linkage.h ... for including arch/x86/lib/memcpy_64.S */
+
+#define ENTRY(name) \
+ .globl name; \
+ name:
+
+#define ENDPROC(name)
+
+#endif /* PERF_LINUX_LINKAGE_H_ */
diff --git a/smartt-perf/util/include/linux/list.h b/smartt-perf/util/include/linux/list.h
new file mode 100644
index 0000000..f86acef
--- /dev/null
+++ b/smartt-perf/util/include/linux/list.h
@@ -0,0 +1,751 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/poison.h>
+#include <linux/prefetch.h>
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+#ifndef CONFIG_DEBUG_LIST
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+#else
+extern void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next);
+#endif
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+#ifndef CONFIG_DEBUG_LIST
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+#else
+extern void __list_del_entry(struct list_head *entry);
+extern void list_del(struct list_head *entry);
+#endif
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *new)
+{
+ list_replace(old, new);
+ INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del_entry(entry);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del_entry(list);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del_entry(list);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+ const struct list_head *head)
+{
+ return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_rotate_left - rotate the list to the left
+ * @head: the head of the list
+ */
+static inline void list_rotate_left(struct list_head *head)
+{
+ struct list_head *first;
+
+ if (!list_empty(head)) {
+ first = head->next;
+ list_move_tail(first, head);
+ }
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+ return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ struct list_head *new_first = entry->next;
+ list->next = head->next;
+ list->next->prev = list;
+ list->prev = entry;
+ entry->next = list;
+ head->next = new_first;
+ new_first->prev = head;
+}
+
+/**
+ * list_cut_position - cut a list into two
+ * @list: a new list to add all removed entries
+ * @head: a list with entries
+ * @entry: an entry within head, could be the head itself
+ * and if so we won't cut the list
+ *
+ * This helper moves the initial part of @head, up to and
+ * including @entry, from @head to @list. You should
+ * pass on @entry an element you know is on @head. @list
+ * should be an empty list or a list you do not care about
+ * losing its data.
+ *
+ */
+static inline void list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ if (list_empty(head))
+ return;
+ if (list_is_singular(head) &&
+ (head->next != entry && head != entry))
+ return;
+ if (entry == head)
+ INIT_LIST_HEAD(list);
+ else
+ __list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+
+ first->prev = prev;
+ prev->next = first;
+
+ last->next = next;
+ next->prev = last;
+}
+
+/**
+ * list_splice - join two lists, this is designed for stacks
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head->prev, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head, head->next);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head->prev, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; prefetch(pos->next), pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
+ pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+ for (pos = (head)->prev, n = pos->prev; \
+ prefetch(pos->prev), pos != (head); \
+ pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ prefetch(pos->member.prev), &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member) \
+ for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
+ prefetch(pos->member.prev), &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) \
+ for (; prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue - continue list iteration safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from - iterate over list from current point safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) \
+ for (n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/**
+ * list_safe_reset_next - reset a stale list_for_each_entry_safe loop
+ * @pos: the loop cursor used in the list_for_each_entry_safe loop
+ * @n: temporary storage used in list_for_each_entry_safe
+ * @member: the name of the list_struct within the struct.
+ *
+ * list_safe_reset_next is not safe to use in general if the list may be
+ * modified concurrently (eg. the lock is dropped in the loop body). An
+ * exception to this is if the cursor element (pos) is pinned in the list,
+ * and list_safe_reset_next is called after re-taking the lock and before
+ * completing the current iteration of the loop body.
+ */
+#define list_safe_reset_next(pos, n, member) \
+ n = list_entry(pos->member.next, typeof(*pos), member)
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+ h->next = NULL;
+ h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (!hlist_unhashed(n)) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ n->next = next;
+ next->pprev = &n->next;
+
+ if(next->next)
+ next->next->pprev = &next->next;
+}
+
+/* after that we'll appear to be on some hlist and hlist_del will work */
+static inline void hlist_add_fake(struct hlist_node *n)
+{
+ n->pprev = &n->next;
+}
+
+/*
+ * Move a list from one list head to another. Fixup the pprev
+ * reference of the first entry if it exists.
+ */
+static inline void hlist_move_list(struct hlist_head *old,
+ struct hlist_head *new)
+{
+ new->first = old->first;
+ if (new->first)
+ new->first->pprev = &new->first;
+ old->first = NULL;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+#endif
+
+
+#ifndef PERF_LIST_H
+#define PERF_LIST_H
+/**
+ * list_del_range - deletes range of entries from list.
+ * @begin: first element in the range to delete from the list.
+ * @end: last element in the range to delete from the list.
+ * Note: list_empty on the range of entries does not return true after this,
+ * the entries is in an undefined state.
+ */
+static inline void list_del_range(struct list_head *begin,
+ struct list_head *end)
+{
+ begin->prev->next = end->next;
+ end->next->prev = begin->prev;
+}
+
+/**
+ * list_for_each_from - iterate over a list from one of its nodes
+ * @pos: the &struct list_head to use as a loop cursor, from where to start
+ * @head: the head for your list.
+ */
+#define list_for_each_from(pos, head) \
+ for (; prefetch(pos->next), pos != (head); pos = pos->next)
+#endif
+
diff --git a/smartt-perf/util/include/linux/magic.h b/smartt-perf/util/include/linux/magic.h
new file mode 100644
index 0000000..62730ea
--- /dev/null
+++ b/smartt-perf/util/include/linux/magic.h
@@ -0,0 +1,63 @@
+#ifndef __LINUX_MAGIC_H__
+#define __LINUX_MAGIC_H__
+
+#define ADFS_SUPER_MAGIC 0xadf5
+#define AFFS_SUPER_MAGIC 0xadff
+#define AFS_SUPER_MAGIC 0x5346414F
+#define AUTOFS_SUPER_MAGIC 0x0187
+#define CODA_SUPER_MAGIC 0x73757245
+#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */
+#define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */
+#define DEBUGFS_MAGIC 0x64626720
+#define SYSFS_MAGIC 0x62656572
+#define SECURITYFS_MAGIC 0x73636673
+#define SELINUX_MAGIC 0xf97cff8c
+#define RAMFS_MAGIC 0x858458f6 /* some random number */
+#define TMPFS_MAGIC 0x01021994
+#define HUGETLBFS_MAGIC 0x958458f6 /* some random number */
+#define SQUASHFS_MAGIC 0x73717368
+#define ECRYPTFS_SUPER_MAGIC 0xf15f
+#define EFS_SUPER_MAGIC 0x414A53
+#define EXT2_SUPER_MAGIC 0xEF53
+#define EXT3_SUPER_MAGIC 0xEF53
+#define XENFS_SUPER_MAGIC 0xabba1974
+#define EXT4_SUPER_MAGIC 0xEF53
+#define BTRFS_SUPER_MAGIC 0x9123683E
+#define HPFS_SUPER_MAGIC 0xf995e849
+#define ISOFS_SUPER_MAGIC 0x9660
+#define JFFS2_SUPER_MAGIC 0x72b6
+#define ANON_INODE_FS_MAGIC 0x09041934
+
+#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */
+#define MINIX3_SUPER_MAGIC 0x4d5a /* minix V3 fs */
+
+#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
+#define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */
+#define NFS_SUPER_MAGIC 0x6969
+#define OPENPROM_SUPER_MAGIC 0x9fa1
+#define PROC_SUPER_MAGIC 0x9fa0
+#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */
+
+#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */
+ /* used by file system utilities that
+ look at the superblock, etc. */
+#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
+#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
+#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
+
+#define SMB_SUPER_MAGIC 0x517B
+#define USBDEVICE_SUPER_MAGIC 0x9fa2
+#define CGROUP_SUPER_MAGIC 0x27e0eb
+
+#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA
+
+#define STACK_END_MAGIC 0x57AC6E9D
+
+#define DEVPTS_SUPER_MAGIC 0x1cd1
+#define SOCKFS_MAGIC 0x534F434B
+#define V9FS_MAGIC 0x01021997
+
+#endif /* __LINUX_MAGIC_H__ */
diff --git a/smartt-perf/util/include/linux/module.h b/smartt-perf/util/include/linux/module.h
new file mode 100644
index 0000000..b43e2dc
--- /dev/null
+++ b/smartt-perf/util/include/linux/module.h
@@ -0,0 +1,6 @@
+#ifndef PERF_LINUX_MODULE_H
+#define PERF_LINUX_MODULE_H
+
+#define EXPORT_SYMBOL(name)
+
+#endif
diff --git a/smartt-perf/util/include/linux/perf_event.h b/smartt-perf/util/include/linux/perf_event.h
new file mode 100644
index 0000000..dda5b0a
--- /dev/null
+++ b/smartt-perf/util/include/linux/perf_event.h
@@ -0,0 +1,1180 @@
+/*
+ * Performance events:
+ *
+ * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2009, Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2008-2009, Red Hat, Inc., Peter Zijlstra
+ *
+ * Data type definitions, declarations, prototypes.
+ *
+ * Started by: Thomas Gleixner and Ingo Molnar
+ *
+ * For licencing details see kernel-base/COPYING
+ */
+#ifndef _LINUX_PERF_EVENT_H
+#define _LINUX_PERF_EVENT_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/byteorder.h>
+
+/*
+ * User-space ABI bits:
+ */
+
+/*
+ * attr.type
+ */
+enum perf_type_id {
+ PERF_TYPE_HARDWARE = 0,
+ PERF_TYPE_SOFTWARE = 1,
+ PERF_TYPE_TRACEPOINT = 2,
+ PERF_TYPE_HW_CACHE = 3,
+ PERF_TYPE_RAW = 4,
+ PERF_TYPE_BREAKPOINT = 5,
+
+ PERF_TYPE_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized performance event event_id types, used by the
+ * attr.event_id parameter of the sys_perf_event_open()
+ * syscall:
+ */
+enum perf_hw_id {
+ /*
+ * Common hardware events, generalized by the kernel:
+ */
+ PERF_COUNT_HW_CPU_CYCLES = 0,
+ PERF_COUNT_HW_INSTRUCTIONS = 1,
+ PERF_COUNT_HW_CACHE_REFERENCES = 2,
+ PERF_COUNT_HW_CACHE_MISSES = 3,
+ PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
+ PERF_COUNT_HW_BRANCH_MISSES = 5,
+ PERF_COUNT_HW_BUS_CYCLES = 6,
+
+ PERF_COUNT_HW_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized hardware cache events:
+ *
+ * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x
+ * { read, write, prefetch } x
+ * { accesses, misses }
+ */
+enum perf_hw_cache_id {
+ PERF_COUNT_HW_CACHE_L1D = 0,
+ PERF_COUNT_HW_CACHE_L1I = 1,
+ PERF_COUNT_HW_CACHE_LL = 2,
+ PERF_COUNT_HW_CACHE_DTLB = 3,
+ PERF_COUNT_HW_CACHE_ITLB = 4,
+ PERF_COUNT_HW_CACHE_BPU = 5,
+
+ PERF_COUNT_HW_CACHE_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_id {
+ PERF_COUNT_HW_CACHE_OP_READ = 0,
+ PERF_COUNT_HW_CACHE_OP_WRITE = 1,
+ PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
+
+ PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_result_id {
+ PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
+ PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
+
+ PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */
+};
+
+/*
+ * Special "software" events provided by the kernel, even if the hardware
+ * does not support performance events. These events measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum perf_sw_ids {
+ PERF_COUNT_SW_CPU_CLOCK = 0,
+ PERF_COUNT_SW_TASK_CLOCK = 1,
+ PERF_COUNT_SW_PAGE_FAULTS = 2,
+ PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
+ PERF_COUNT_SW_CPU_MIGRATIONS = 4,
+ PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
+ PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
+ PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
+ PERF_COUNT_SW_EMULATION_FAULTS = 8,
+
+ PERF_COUNT_SW_MAX, /* non-ABI */
+};
+
+/*
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_sample_format {
+ PERF_SAMPLE_IP = 1U << 0,
+ PERF_SAMPLE_TID = 1U << 1,
+ PERF_SAMPLE_TIME = 1U << 2,
+ PERF_SAMPLE_ADDR = 1U << 3,
+ PERF_SAMPLE_READ = 1U << 4,
+ PERF_SAMPLE_CALLCHAIN = 1U << 5,
+ PERF_SAMPLE_ID = 1U << 6,
+ PERF_SAMPLE_CPU = 1U << 7,
+ PERF_SAMPLE_PERIOD = 1U << 8,
+ PERF_SAMPLE_STREAM_ID = 1U << 9,
+ PERF_SAMPLE_RAW = 1U << 10,
+
+ PERF_SAMPLE_MAX = 1U << 11, /* non-ABI */
+};
+
+/*
+ * The format of the data returned by read() on a perf event fd,
+ * as specified by attr.read_format:
+ *
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ *
+ * { u64 nr;
+ * { u64 time_enabled; } && PERF_FORMAT_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_RUNNING
+ * { u64 value;
+ * { u64 id; } && PERF_FORMAT_ID
+ * } cntr[nr];
+ * } && PERF_FORMAT_GROUP
+ * };
+ */
+enum perf_event_read_format {
+ PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
+ PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
+ PERF_FORMAT_ID = 1U << 2,
+ PERF_FORMAT_GROUP = 1U << 3,
+
+ PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
+};
+
+#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
+
+/*
+ * Hardware event_id to monitor via a performance monitoring event:
+ */
+struct perf_event_attr {
+
+ /*
+ * Major type: hardware/software/tracepoint/etc.
+ */
+ __u32 type;
+
+ /*
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ __u32 size;
+
+ /*
+ * Type specific configuration information.
+ */
+ __u64 config;
+
+ union {
+ __u64 sample_period;
+ __u64 sample_freq;
+ };
+
+ __u64 sample_type;
+ __u64 read_format;
+
+ __u64 disabled : 1, /* off by default */
+ inherit : 1, /* children inherit it */
+ pinned : 1, /* must always be on PMU */
+ exclusive : 1, /* only group on PMU */
+ exclude_user : 1, /* don't count user */
+ exclude_kernel : 1, /* ditto kernel */
+ exclude_hv : 1, /* ditto hypervisor */
+ exclude_idle : 1, /* don't count when idle */
+ mmap : 1, /* include mmap data */
+ comm : 1, /* include comm data */
+ freq : 1, /* use freq, not period */
+ inherit_stat : 1, /* per task counts */
+ enable_on_exec : 1, /* next exec enables */
+ task : 1, /* trace fork/exit */
+ watermark : 1, /* wakeup_watermark */
+ /*
+ * precise_ip:
+ *
+ * 0 - SAMPLE_IP can have arbitrary skid
+ * 1 - SAMPLE_IP must have constant skid
+ * 2 - SAMPLE_IP requested to have 0 skid
+ * 3 - SAMPLE_IP must have 0 skid
+ *
+ * See also PERF_RECORD_MISC_EXACT_IP
+ */
+ precise_ip : 2, /* skid constraint */
+ mmap_data : 1, /* non-exec mmap data */
+ sample_id_all : 1, /* sample_type all events */
+
+ __reserved_1 : 45;
+
+ union {
+ __u32 wakeup_events; /* wakeup every n events */
+ __u32 wakeup_watermark; /* bytes before wakeup */
+ };
+
+ __u32 bp_type;
+ __u64 bp_addr;
+ __u64 bp_len;
+};
+
+/*
+ * Ioctls that can be done on a perf event fd:
+ */
+#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
+#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
+#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
+#define PERF_EVENT_IOC_RESET _IO ('$', 3)
+#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
+#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
+#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
+
+enum perf_event_ioc_flags {
+ PERF_IOC_FLAG_GROUP = 1U << 0,
+};
+
+/*
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_event_mmap_page {
+ __u32 version; /* version number of this structure */
+ __u32 compat_version; /* lowest version this is compat with */
+
+ /*
+ * Bits needed to read the hw events in user-space.
+ *
+ * u32 seq;
+ * s64 count;
+ *
+ * do {
+ * seq = pc->lock;
+ *
+ * barrier()
+ * if (pc->index) {
+ * count = pmc_read(pc->index - 1);
+ * count += pc->offset;
+ * } else
+ * goto regular_read;
+ *
+ * barrier();
+ * } while (pc->lock != seq);
+ *
+ * NOTE: for obvious reason this only works on self-monitoring
+ * processes.
+ */
+ __u32 lock; /* seqlock for synchronization */
+ __u32 index; /* hardware event identifier */
+ __s64 offset; /* add to hardware event value */
+ __u64 time_enabled; /* time event active */
+ __u64 time_running; /* time event on cpu */
+
+ /*
+ * Hole for extension of the self monitor capabilities
+ */
+
+ __u64 __reserved[123]; /* align to 1k */
+
+ /*
+ * Control data for the mmap() data buffer.
+ *
+ * User-space reading the @data_head value should issue an rmb(), on
+ * SMP capable platforms, after reading this value -- see
+ * perf_event_wakeup().
+ *
+ * When the mapping is PROT_WRITE the @data_tail value should be
+ * written by userspace to reflect the last read data. In this case
+ * the kernel will not over-write unread data.
+ */
+ __u64 data_head; /* head in the data section */
+ __u64 data_tail; /* user-space written tail */
+};
+
+#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0)
+#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0)
+#define PERF_RECORD_MISC_KERNEL (1 << 0)
+#define PERF_RECORD_MISC_USER (2 << 0)
+#define PERF_RECORD_MISC_HYPERVISOR (3 << 0)
+#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0)
+#define PERF_RECORD_MISC_GUEST_USER (5 << 0)
+
+/*
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ */
+#define PERF_RECORD_MISC_EXACT_IP (1 << 14)
+/*
+ * Reserve the last bit to indicate some extended misc field
+ */
+#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15)
+
+struct perf_event_header {
+ __u32 type;
+ __u16 misc;
+ __u16 size;
+};
+
+enum perf_event_type {
+
+ /*
+ * If perf_event_attr.sample_id_all is set then all event types will
+ * have the sample_type selected fields related to where/when
+ * (identity) an event took place (TID, TIME, ID, CPU, STREAM_ID)
+ * described in PERF_RECORD_SAMPLE below, it will be stashed just after
+ * the perf_event_header and the fields already present for the existing
+ * fields, i.e. at the end of the payload. That way a newer perf.data
+ * file will be supported by older perf tools, with these new optional
+ * fields being ignored.
+ *
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * };
+ */
+ PERF_RECORD_MMAP = 1,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * u64 lost;
+ * };
+ */
+ PERF_RECORD_LOST = 2,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * };
+ */
+ PERF_RECORD_COMM = 3,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * };
+ */
+ PERF_RECORD_EXIT = 4,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 time;
+ * u64 id;
+ * u64 stream_id;
+ * };
+ */
+ PERF_RECORD_THROTTLE = 5,
+ PERF_RECORD_UNTHROTTLE = 6,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * };
+ */
+ PERF_RECORD_FORK = 7,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, tid;
+ *
+ * struct read_format values;
+ * };
+ */
+ PERF_RECORD_READ = 8,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * { u64 ip; } && PERF_SAMPLE_IP
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 addr; } && PERF_SAMPLE_ADDR
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 period; } && PERF_SAMPLE_PERIOD
+ *
+ * { struct read_format values; } && PERF_SAMPLE_READ
+ *
+ * { u64 nr,
+ * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];}&& PERF_SAMPLE_RAW
+ * };
+ */
+ PERF_RECORD_SAMPLE = 9,
+
+ PERF_RECORD_MAX, /* non-ABI */
+};
+
+enum perf_callchain_context {
+ PERF_CONTEXT_HV = (__u64)-32,
+ PERF_CONTEXT_KERNEL = (__u64)-128,
+ PERF_CONTEXT_USER = (__u64)-512,
+
+ PERF_CONTEXT_GUEST = (__u64)-2048,
+ PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176,
+ PERF_CONTEXT_GUEST_USER = (__u64)-2560,
+
+ PERF_CONTEXT_MAX = (__u64)-4095,
+};
+
+#define PERF_FLAG_FD_NO_GROUP (1U << 0)
+#define PERF_FLAG_FD_OUTPUT (1U << 1)
+
+#ifdef __KERNEL__
+/*
+ * Kernel-internal data types and definitions:
+ */
+
+#ifdef CONFIG_PERF_EVENTS
+# include <asm/perf_event.h>
+# include <asm/local64.h>
+#endif
+
+struct perf_guest_info_callbacks {
+ int (*is_in_guest) (void);
+ int (*is_user_mode) (void);
+ unsigned long (*get_guest_ip) (void);
+};
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+#include <asm/hw_breakpoint.h>
+#endif
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/fs.h>
+#include <linux/pid_namespace.h>
+#include <linux/workqueue.h>
+#include <linux/ftrace.h>
+#include <linux/cpu.h>
+#include <linux/irq_work.h>
+#include <linux/jump_label_ref.h>
+#include <asm/atomic.h>
+#include <asm/local.h>
+
+#define PERF_MAX_STACK_DEPTH 255
+
+struct perf_callchain_entry {
+ __u64 nr;
+ __u64 ip[PERF_MAX_STACK_DEPTH];
+};
+
+struct perf_raw_record {
+ u32 size;
+ void *data;
+};
+
+struct perf_branch_entry {
+ __u64 from;
+ __u64 to;
+ __u64 flags;
+};
+
+struct perf_branch_stack {
+ __u64 nr;
+ struct perf_branch_entry entries[0];
+};
+
+struct task_struct;
+
+/**
+ * struct hw_perf_event - performance event hardware details:
+ */
+struct hw_perf_event {
+#ifdef CONFIG_PERF_EVENTS
+ union {
+ struct { /* hardware */
+ u64 config;
+ u64 last_tag;
+ unsigned long config_base;
+ unsigned long event_base;
+ int idx;
+ int last_cpu;
+ };
+ struct { /* software */
+ struct hrtimer hrtimer;
+ };
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ struct { /* breakpoint */
+ struct arch_hw_breakpoint info;
+ struct list_head bp_list;
+ /*
+ * Crufty hack to avoid the chicken and egg
+ * problem hw_breakpoint has with context
+ * creation and event initalization.
+ */
+ struct task_struct *bp_target;
+ };
+#endif
+ };
+ int state;
+ local64_t prev_count;
+ u64 sample_period;
+ u64 last_period;
+ local64_t period_left;
+ u64 interrupts;
+
+ u64 freq_time_stamp;
+ u64 freq_count_stamp;
+#endif
+};
+
+/*
+ * hw_perf_event::state flags
+ */
+#define PERF_HES_STOPPED 0x01 /* the counter is stopped */
+#define PERF_HES_UPTODATE 0x02 /* event->count up-to-date */
+#define PERF_HES_ARCH 0x04
+
+struct perf_event;
+
+/*
+ * Common implementation detail of pmu::{start,commit,cancel}_txn
+ */
+#define PERF_EVENT_TXN 0x1
+
+/**
+ * struct pmu - generic performance monitoring unit
+ */
+struct pmu {
+ struct list_head entry;
+
+ struct device *dev;
+ char *name;
+ int type;
+
+ int * __percpu pmu_disable_count;
+ struct perf_cpu_context * __percpu pmu_cpu_context;
+ int task_ctx_nr;
+
+ /*
+ * Fully disable/enable this PMU, can be used to protect from the PMI
+ * as well as for lazy/batch writing of the MSRs.
+ */
+ void (*pmu_enable) (struct pmu *pmu); /* optional */
+ void (*pmu_disable) (struct pmu *pmu); /* optional */
+
+ /*
+ * Try and initialize the event for this PMU.
+ * Should return -ENOENT when the @event doesn't match this PMU.
+ */
+ int (*event_init) (struct perf_event *event);
+
+#define PERF_EF_START 0x01 /* start the counter when adding */
+#define PERF_EF_RELOAD 0x02 /* reload the counter when starting */
+#define PERF_EF_UPDATE 0x04 /* update the counter when stopping */
+
+ /*
+ * Adds/Removes a counter to/from the PMU, can be done inside
+ * a transaction, see the ->*_txn() methods.
+ */
+ int (*add) (struct perf_event *event, int flags);
+ void (*del) (struct perf_event *event, int flags);
+
+ /*
+ * Starts/Stops a counter present on the PMU. The PMI handler
+ * should stop the counter when perf_event_overflow() returns
+ * !0. ->start() will be used to continue.
+ */
+ void (*start) (struct perf_event *event, int flags);
+ void (*stop) (struct perf_event *event, int flags);
+
+ /*
+ * Updates the counter value of the event.
+ */
+ void (*read) (struct perf_event *event);
+
+ /*
+ * Group events scheduling is treated as a transaction, add
+ * group events as a whole and perform one schedulability test.
+ * If the test fails, roll back the whole group
+ *
+ * Start the transaction, after this ->add() doesn't need to
+ * do schedulability tests.
+ */
+ void (*start_txn) (struct pmu *pmu); /* optional */
+ /*
+ * If ->start_txn() disabled the ->add() schedulability test
+ * then ->commit_txn() is required to perform one. On success
+ * the transaction is closed. On error the transaction is kept
+ * open until ->cancel_txn() is called.
+ */
+ int (*commit_txn) (struct pmu *pmu); /* optional */
+ /*
+ * Will cancel the transaction, assumes ->del() is called
+ * for each successfull ->add() during the transaction.
+ */
+ void (*cancel_txn) (struct pmu *pmu); /* optional */
+};
+
+/**
+ * enum perf_event_active_state - the states of a event
+ */
+enum perf_event_active_state {
+ PERF_EVENT_STATE_ERROR = -2,
+ PERF_EVENT_STATE_OFF = -1,
+ PERF_EVENT_STATE_INACTIVE = 0,
+ PERF_EVENT_STATE_ACTIVE = 1,
+};
+
+struct file;
+
+#define PERF_BUFFER_WRITABLE 0x01
+
+struct perf_buffer {
+ atomic_t refcount;
+ struct rcu_head rcu_head;
+#ifdef CONFIG_PERF_USE_VMALLOC
+ struct work_struct work;
+ int page_order; /* allocation order */
+#endif
+ int nr_pages; /* nr of data pages */
+ int writable; /* are we writable */
+
+ atomic_t poll; /* POLL_ for wakeups */
+
+ local_t head; /* write position */
+ local_t nest; /* nested writers */
+ local_t events; /* event limit */
+ local_t wakeup; /* wakeup stamp */
+ local_t lost; /* nr records lost */
+
+ long watermark; /* wakeup watermark */
+
+ struct perf_event_mmap_page *user_page;
+ void *data_pages[0];
+};
+
+struct perf_sample_data;
+
+typedef void (*perf_overflow_handler_t)(struct perf_event *, int,
+ struct perf_sample_data *,
+ struct pt_regs *regs);
+
+enum perf_group_flag {
+ PERF_GROUP_SOFTWARE = 0x1,
+};
+
+#define SWEVENT_HLIST_BITS 8
+#define SWEVENT_HLIST_SIZE (1 << SWEVENT_HLIST_BITS)
+
+struct swevent_hlist {
+ struct hlist_head heads[SWEVENT_HLIST_SIZE];
+ struct rcu_head rcu_head;
+};
+
+#define PERF_ATTACH_CONTEXT 0x01
+#define PERF_ATTACH_GROUP 0x02
+#define PERF_ATTACH_TASK 0x04
+
+/**
+ * struct perf_event - performance event kernel representation:
+ */
+struct perf_event {
+#ifdef CONFIG_PERF_EVENTS
+ struct list_head group_entry;
+ struct list_head event_entry;
+ struct list_head sibling_list;
+ struct hlist_node hlist_entry;
+ int nr_siblings;
+ int group_flags;
+ struct perf_event *group_leader;
+ struct pmu *pmu;
+
+ enum perf_event_active_state state;
+ unsigned int attach_state;
+ local64_t count;
+ atomic64_t child_count;
+
+ /*
+ * These are the total time in nanoseconds that the event
+ * has been enabled (i.e. eligible to run, and the task has
+ * been scheduled in, if this is a per-task event)
+ * and running (scheduled onto the CPU), respectively.
+ *
+ * They are computed from tstamp_enabled, tstamp_running and
+ * tstamp_stopped when the event is in INACTIVE or ACTIVE state.
+ */
+ u64 total_time_enabled;
+ u64 total_time_running;
+
+ /*
+ * These are timestamps used for computing total_time_enabled
+ * and total_time_running when the event is in INACTIVE or
+ * ACTIVE state, measured in nanoseconds from an arbitrary point
+ * in time.
+ * tstamp_enabled: the notional time when the event was enabled
+ * tstamp_running: the notional time when the event was scheduled on
+ * tstamp_stopped: in INACTIVE state, the notional time when the
+ * event was scheduled off.
+ */
+ u64 tstamp_enabled;
+ u64 tstamp_running;
+ u64 tstamp_stopped;
+
+ /*
+ * timestamp shadows the actual context timing but it can
+ * be safely used in NMI interrupt context. It reflects the
+ * context time as it was when the event was last scheduled in.
+ *
+ * ctx_time already accounts for ctx->timestamp. Therefore to
+ * compute ctx_time for a sample, simply add perf_clock().
+ */
+ u64 shadow_ctx_time;
+
+ struct perf_event_attr attr;
+ u16 header_size;
+ u16 id_header_size;
+ u16 read_size;
+ struct hw_perf_event hw;
+
+ struct perf_event_context *ctx;
+ struct file *filp;
+
+ /*
+ * These accumulate total time (in nanoseconds) that children
+ * events have been enabled and running, respectively.
+ */
+ atomic64_t child_total_time_enabled;
+ atomic64_t child_total_time_running;
+
+ /*
+ * Protect attach/detach and child_list:
+ */
+ struct mutex child_mutex;
+ struct list_head child_list;
+ struct perf_event *parent;
+
+ int oncpu;
+ int cpu;
+
+ struct list_head owner_entry;
+ struct task_struct *owner;
+
+ /* mmap bits */
+ struct mutex mmap_mutex;
+ atomic_t mmap_count;
+ int mmap_locked;
+ struct user_struct *mmap_user;
+ struct perf_buffer *buffer;
+
+ /* poll related */
+ wait_queue_head_t waitq;
+ struct fasync_struct *fasync;
+
+ /* delayed work for NMIs and such */
+ int pending_wakeup;
+ int pending_kill;
+ int pending_disable;
+ struct irq_work pending;
+
+ atomic_t event_limit;
+
+ void (*destroy)(struct perf_event *);
+ struct rcu_head rcu_head;
+
+ struct pid_namespace *ns;
+ u64 id;
+
+ perf_overflow_handler_t overflow_handler;
+
+#ifdef CONFIG_EVENT_TRACING
+ struct ftrace_event_call *tp_event;
+ struct event_filter *filter;
+#endif
+
+#endif /* CONFIG_PERF_EVENTS */
+};
+
+enum perf_event_context_type {
+ task_context,
+ cpu_context,
+};
+
+/**
+ * struct perf_event_context - event context structure
+ *
+ * Used as a container for task events and CPU events as well:
+ */
+struct perf_event_context {
+ enum perf_event_context_type type;
+ struct pmu *pmu;
+ /*
+ * Protect the states of the events in the list,
+ * nr_active, and the list:
+ */
+ raw_spinlock_t lock;
+ /*
+ * Protect the list of events. Locking either mutex or lock
+ * is sufficient to ensure the list doesn't change; to change
+ * the list you need to lock both the mutex and the spinlock.
+ */
+ struct mutex mutex;
+
+ struct list_head pinned_groups;
+ struct list_head flexible_groups;
+ struct list_head event_list;
+ int nr_events;
+ int nr_active;
+ int is_active;
+ int nr_stat;
+ int rotate_disable;
+ atomic_t refcount;
+ struct task_struct *task;
+
+ /*
+ * Context clock, runs when context enabled.
+ */
+ u64 time;
+ u64 timestamp;
+
+ /*
+ * These fields let us detect when two contexts have both
+ * been cloned (inherited) from a common ancestor.
+ */
+ struct perf_event_context *parent_ctx;
+ u64 parent_gen;
+ u64 generation;
+ int pin_count;
+ struct rcu_head rcu_head;
+};
+
+/*
+ * Number of contexts where an event can trigger:
+ * task, softirq, hardirq, nmi.
+ */
+#define PERF_NR_CONTEXTS 4
+
+/**
+ * struct perf_event_cpu_context - per cpu event context structure
+ */
+struct perf_cpu_context {
+ struct perf_event_context ctx;
+ struct perf_event_context *task_ctx;
+ int active_oncpu;
+ int exclusive;
+ struct list_head rotation_list;
+ int jiffies_interval;
+ struct pmu *active_pmu;
+};
+
+struct perf_output_handle {
+ struct perf_event *event;
+ struct perf_buffer *buffer;
+ unsigned long wakeup;
+ unsigned long size;
+ void *addr;
+ int page;
+ int nmi;
+ int sample;
+};
+
+#ifdef CONFIG_PERF_EVENTS
+
+extern int perf_pmu_register(struct pmu *pmu, char *name, int type);
+extern void perf_pmu_unregister(struct pmu *pmu);
+
+extern int perf_num_counters(void);
+extern const char *perf_pmu_name(void);
+extern void __perf_event_task_sched_in(struct task_struct *task);
+extern void __perf_event_task_sched_out(struct task_struct *task, struct task_struct *next);
+extern int perf_event_init_task(struct task_struct *child);
+extern void perf_event_exit_task(struct task_struct *child);
+extern void perf_event_free_task(struct task_struct *task);
+extern void perf_event_delayed_put(struct task_struct *task);
+extern void perf_event_print_debug(void);
+extern void perf_pmu_disable(struct pmu *pmu);
+extern void perf_pmu_enable(struct pmu *pmu);
+extern int perf_event_task_disable(void);
+extern int perf_event_task_enable(void);
+extern void perf_event_update_userpage(struct perf_event *event);
+extern int perf_event_release_kernel(struct perf_event *event);
+extern struct perf_event *
+perf_event_create_kernel_counter(struct perf_event_attr *attr,
+ int cpu,
+ struct task_struct *task,
+ perf_overflow_handler_t callback);
+extern u64 perf_event_read_value(struct perf_event *event,
+ u64 *enabled, u64 *running);
+
+struct perf_sample_data {
+ u64 type;
+
+ u64 ip;
+ struct {
+ u32 pid;
+ u32 tid;
+ } tid_entry;
+ u64 time;
+ u64 addr;
+ u64 id;
+ u64 stream_id;
+ struct {
+ u32 cpu;
+ u32 reserved;
+ } cpu_entry;
+ u64 period;
+ struct perf_callchain_entry *callchain;
+ struct perf_raw_record *raw;
+};
+
+static inline
+void perf_sample_data_init(struct perf_sample_data *data, u64 addr)
+{
+ data->addr = addr;
+ data->raw = NULL;
+}
+
+extern void perf_output_sample(struct perf_output_handle *handle,
+ struct perf_event_header *header,
+ struct perf_sample_data *data,
+ struct perf_event *event);
+extern void perf_prepare_sample(struct perf_event_header *header,
+ struct perf_sample_data *data,
+ struct perf_event *event,
+ struct pt_regs *regs);
+
+extern int perf_event_overflow(struct perf_event *event, int nmi,
+ struct perf_sample_data *data,
+ struct pt_regs *regs);
+
+static inline bool is_sampling_event(struct perf_event *event)
+{
+ return event->attr.sample_period != 0;
+}
+
+/*
+ * Return 1 for a software event, 0 for a hardware event
+ */
+static inline int is_software_event(struct perf_event *event)
+{
+ return event->pmu->task_ctx_nr == perf_sw_context;
+}
+
+extern atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX];
+
+extern void __perf_sw_event(u32, u64, int, struct pt_regs *, u64);
+
+#ifndef perf_arch_fetch_caller_regs
+static inline void
+perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip) { }
+#endif
+
+/*
+ * Take a snapshot of the regs. Skip ip and frame pointer to
+ * the nth caller. We only need a few of the regs:
+ * - ip for PERF_SAMPLE_IP
+ * - cs for user_mode() tests
+ * - bp for callchains
+ * - eflags, for future purposes, just in case
+ */
+static inline void perf_fetch_caller_regs(struct pt_regs *regs)
+{
+ memset(regs, 0, sizeof(*regs));
+
+ perf_arch_fetch_caller_regs(regs, CALLER_ADDR0);
+}
+
+static __always_inline void
+perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr)
+{
+ struct pt_regs hot_regs;
+
+ JUMP_LABEL(&perf_swevent_enabled[event_id], have_event);
+ return;
+
+have_event:
+ if (!regs) {
+ perf_fetch_caller_regs(&hot_regs);
+ regs = &hot_regs;
+ }
+ __perf_sw_event(event_id, nr, nmi, regs, addr);
+}
+
+extern atomic_t perf_task_events;
+
+static inline void perf_event_task_sched_in(struct task_struct *task)
+{
+ COND_STMT(&perf_task_events, __perf_event_task_sched_in(task));
+}
+
+static inline
+void perf_event_task_sched_out(struct task_struct *task, struct task_struct *next)
+{
+ perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0);
+
+ COND_STMT(&perf_task_events, __perf_event_task_sched_out(task, next));
+}
+
+extern void perf_event_mmap(struct vm_area_struct *vma);
+extern struct perf_guest_info_callbacks *perf_guest_cbs;
+extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
+extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
+
+extern void perf_event_comm(struct task_struct *tsk);
+extern void perf_event_fork(struct task_struct *tsk);
+
+/* Callchains */
+DECLARE_PER_CPU(struct perf_callchain_entry, perf_callchain_entry);
+
+extern void perf_callchain_user(struct perf_callchain_entry *entry,
+ struct pt_regs *regs);
+extern void perf_callchain_kernel(struct perf_callchain_entry *entry,
+ struct pt_regs *regs);
+
+
+static inline void
+perf_callchain_store(struct perf_callchain_entry *entry, u64 ip)
+{
+ if (entry->nr < PERF_MAX_STACK_DEPTH)
+ entry->ip[entry->nr++] = ip;
+}
+
+extern int sysctl_perf_event_paranoid;
+extern int sysctl_perf_event_mlock;
+extern int sysctl_perf_event_sample_rate;
+
+static inline bool perf_paranoid_tracepoint_raw(void)
+{
+ return sysctl_perf_event_paranoid > -1;
+}
+
+static inline bool perf_paranoid_cpu(void)
+{
+ return sysctl_perf_event_paranoid > 0;
+}
+
+static inline bool perf_paranoid_kernel(void)
+{
+ return sysctl_perf_event_paranoid > 1;
+}
+
+extern void perf_event_init(void);
+extern void perf_tp_event(u64 addr, u64 count, void *record,
+ int entry_size, struct pt_regs *regs,
+ struct hlist_head *head, int rctx);
+extern void perf_bp_event(struct perf_event *event, void *data);
+
+#ifndef perf_misc_flags
+#define perf_misc_flags(regs) (user_mode(regs) ? PERF_RECORD_MISC_USER : \
+ PERF_RECORD_MISC_KERNEL)
+#define perf_instruction_pointer(regs) instruction_pointer(regs)
+#endif
+
+extern int perf_output_begin(struct perf_output_handle *handle,
+ struct perf_event *event, unsigned int size,
+ int nmi, int sample);
+extern void perf_output_end(struct perf_output_handle *handle);
+extern void perf_output_copy(struct perf_output_handle *handle,
+ const void *buf, unsigned int len);
+extern int perf_swevent_get_recursion_context(void);
+extern void perf_swevent_put_recursion_context(int rctx);
+extern void perf_event_enable(struct perf_event *event);
+extern void perf_event_disable(struct perf_event *event);
+extern void perf_event_task_tick(void);
+#else
+static inline void
+perf_event_task_sched_in(struct task_struct *task) { }
+static inline void
+perf_event_task_sched_out(struct task_struct *task,
+ struct task_struct *next) { }
+static inline int perf_event_init_task(struct task_struct *child) { return 0; }
+static inline void perf_event_exit_task(struct task_struct *child) { }
+static inline void perf_event_free_task(struct task_struct *task) { }
+static inline void perf_event_delayed_put(struct task_struct *task) { }
+static inline void perf_event_print_debug(void) { }
+static inline int perf_event_task_disable(void) { return -EINVAL; }
+static inline int perf_event_task_enable(void) { return -EINVAL; }
+
+static inline void
+perf_sw_event(u32 event_id, u64 nr, int nmi,
+ struct pt_regs *regs, u64 addr) { }
+static inline void
+perf_bp_event(struct perf_event *event, void *data) { }
+
+static inline int perf_register_guest_info_callbacks
+(struct perf_guest_info_callbacks *callbacks) { return 0; }
+static inline int perf_unregister_guest_info_callbacks
+(struct perf_guest_info_callbacks *callbacks) { return 0; }
+
+static inline void perf_event_mmap(struct vm_area_struct *vma) { }
+static inline void perf_event_comm(struct task_struct *tsk) { }
+static inline void perf_event_fork(struct task_struct *tsk) { }
+static inline void perf_event_init(void) { }
+static inline int perf_swevent_get_recursion_context(void) { return -1; }
+static inline void perf_swevent_put_recursion_context(int rctx) { }
+static inline void perf_event_enable(struct perf_event *event) { }
+static inline void perf_event_disable(struct perf_event *event) { }
+static inline void perf_event_task_tick(void) { }
+#endif
+
+#define perf_output_put(handle, x) \
+ perf_output_copy((handle), &(x), sizeof(x))
+
+/*
+ * This has to have a higher priority than migration_notifier in sched.c.
+ */
+#define perf_cpu_notifier(fn) \
+do { \
+ static struct notifier_block fn##_nb __cpuinitdata = \
+ { .notifier_call = fn, .priority = CPU_PRI_PERF }; \
+ fn(&fn##_nb, (unsigned long)CPU_UP_PREPARE, \
+ (void *)(unsigned long)smp_processor_id()); \
+ fn(&fn##_nb, (unsigned long)CPU_STARTING, \
+ (void *)(unsigned long)smp_processor_id()); \
+ fn(&fn##_nb, (unsigned long)CPU_ONLINE, \
+ (void *)(unsigned long)smp_processor_id()); \
+ register_cpu_notifier(&fn##_nb); \
+} while (0)
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_PERF_EVENT_H */
diff --git a/smartt-perf/util/include/linux/poison.h b/smartt-perf/util/include/linux/poison.h
new file mode 100644
index 0000000..2110a81
--- /dev/null
+++ b/smartt-perf/util/include/linux/poison.h
@@ -0,0 +1,89 @@
+#ifndef _LINUX_POISON_H
+#define _LINUX_POISON_H
+
+/********** include/linux/list.h **********/
+
+/*
+ * Architectures might want to move the poison pointer offset
+ * into some well-recognized area such as 0xdead000000000000,
+ * that is also not mappable by user-space exploits:
+ */
+#ifdef CONFIG_ILLEGAL_POINTER_VALUE
+# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
+#else
+# define POISON_POINTER_DELTA 0
+#endif
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
+#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
+
+/********** include/linux/timer.h **********/
+/*
+ * Magic number "tsta" to indicate a static timer initializer
+ * for the object debugging code.
+ */
+#define TIMER_ENTRY_STATIC ((void *) 0x74737461)
+
+/********** mm/debug-pagealloc.c **********/
+#define PAGE_POISON 0xaa
+
+/********** mm/slab.c **********/
+/*
+ * Magic nums for obj red zoning.
+ * Placed in the first word before and the first word after an obj.
+ */
+#define RED_INACTIVE 0x09F911029D74E35BULL /* when obj is inactive */
+#define RED_ACTIVE 0xD84156C5635688C0ULL /* when obj is active */
+
+#define SLUB_RED_INACTIVE 0xbb
+#define SLUB_RED_ACTIVE 0xcc
+
+/* ...and for poisoning */
+#define POISON_INUSE 0x5a /* for use-uninitialised poisoning */
+#define POISON_FREE 0x6b /* for use-after-free poisoning */
+#define POISON_END 0xa5 /* end-byte of poisoning */
+
+/********** arch/$ARCH/mm/init.c **********/
+#define POISON_FREE_INITMEM 0xcc
+
+/********** arch/ia64/hp/common/sba_iommu.c **********/
+/*
+ * arch/ia64/hp/common/sba_iommu.c uses a 16-byte poison string with a
+ * value of "SBAIOMMU POISON\0" for spill-over poisoning.
+ */
+
+/********** fs/jbd/journal.c **********/
+#define JBD_POISON_FREE 0x5b
+#define JBD2_POISON_FREE 0x5c
+
+/********** drivers/base/dmapool.c **********/
+#define POOL_POISON_FREED 0xa7 /* !inuse */
+#define POOL_POISON_ALLOCATED 0xa9 /* !initted */
+
+/********** drivers/atm/ **********/
+#define ATM_POISON_FREE 0x12
+#define ATM_POISON 0xdeadbeef
+
+/********** net/ **********/
+#define NEIGHBOR_DEAD 0xdeadbeef
+#define NETFILTER_LINK_POISON 0xdead57ac
+
+/********** kernel/mutexes **********/
+#define MUTEX_DEBUG_INIT 0x11
+#define MUTEX_DEBUG_FREE 0x22
+
+/********** lib/flex_array.c **********/
+#define FLEX_ARRAY_FREE 0x6c /* for use-after-free poisoning */
+
+/********** security/ **********/
+#define KEY_DESTROY 0xbd
+
+/********** sound/oss/ **********/
+#define OSS_POISON_FREE 0xAB
+
+#endif
diff --git a/smartt-perf/util/include/linux/prefetch.h b/smartt-perf/util/include/linux/prefetch.h
new file mode 100644
index 0000000..7841e48
--- /dev/null
+++ b/smartt-perf/util/include/linux/prefetch.h
@@ -0,0 +1,6 @@
+#ifndef PERF_LINUX_PREFETCH_H
+#define PERF_LINUX_PREFETCH_H
+
+static inline void prefetch(void *a __attribute__((unused))) { }
+
+#endif
diff --git a/smartt-perf/util/include/linux/rbtree.h b/smartt-perf/util/include/linux/rbtree.h
new file mode 100644
index 0000000..7066acb
--- /dev/null
+++ b/smartt-perf/util/include/linux/rbtree.h
@@ -0,0 +1,169 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ linux/include/linux/rbtree.h
+
+ To use rbtrees you'll have to implement your own insert and search cores.
+ This will avoid us to use callbacks and to drop drammatically performances.
+ I know it's not the cleaner way, but in C (not in C++) to get
+ performances and genericity...
+
+ Some example of insert and search follows here. The search is a plain
+ normal search over an ordered tree. The insert instead must be implemented
+ in two steps: First, the code must insert the element in order as a red leaf
+ in the tree, and then the support library function rb_insert_color() must
+ be called. Such function will do the not trivial work to rebalance the
+ rbtree, if necessary.
+
+-----------------------------------------------------------------------
+static inline struct page * rb_search_page_cache(struct inode * inode,
+ unsigned long offset)
+{
+ struct rb_node * n = inode->i_rb_page_cache.rb_node;
+ struct page * page;
+
+ while (n)
+ {
+ page = rb_entry(n, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ n = n->rb_left;
+ else if (offset > page->offset)
+ n = n->rb_right;
+ else
+ return page;
+ }
+ return NULL;
+}
+
+static inline struct page * __rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
+ struct rb_node * parent = NULL;
+ struct page * page;
+
+ while (*p)
+ {
+ parent = *p;
+ page = rb_entry(parent, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ p = &(*p)->rb_left;
+ else if (offset > page->offset)
+ p = &(*p)->rb_right;
+ else
+ return page;
+ }
+
+ rb_link_node(node, parent, p);
+
+ return NULL;
+}
+
+static inline struct page * rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct page * ret;
+ if ((ret = __rb_insert_page_cache(inode, offset, node)))
+ goto out;
+ rb_insert_color(node, &inode->i_rb_page_cache);
+ out:
+ return ret;
+}
+-----------------------------------------------------------------------
+*/
+
+#ifndef _LINUX_RBTREE_H
+#define _LINUX_RBTREE_H
+
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+
+struct rb_node
+{
+ unsigned long rb_parent_color;
+#define RB_RED 0
+#define RB_BLACK 1
+ struct rb_node *rb_right;
+ struct rb_node *rb_left;
+} __attribute__((aligned(sizeof(long))));
+ /* The alignment might seem pointless, but allegedly CRIS needs it */
+
+struct rb_root
+{
+ struct rb_node *rb_node;
+};
+
+
+#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
+#define rb_color(r) ((r)->rb_parent_color & 1)
+#define rb_is_red(r) (!rb_color(r))
+#define rb_is_black(r) rb_color(r)
+#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
+#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
+
+static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
+}
+static inline void rb_set_color(struct rb_node *rb, int color)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
+}
+
+#define RB_ROOT (struct rb_root) { NULL, }
+#define rb_entry(ptr, type, member) container_of(ptr, type, member)
+
+#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
+#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
+#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
+
+extern void rb_insert_color(struct rb_node *, struct rb_root *);
+extern void rb_erase(struct rb_node *, struct rb_root *);
+
+typedef void (*rb_augment_f)(struct rb_node *node, void *data);
+
+extern void rb_augment_insert(struct rb_node *node,
+ rb_augment_f func, void *data);
+extern struct rb_node *rb_augment_erase_begin(struct rb_node *node);
+extern void rb_augment_erase_end(struct rb_node *node,
+ rb_augment_f func, void *data);
+
+/* Find logical next and previous nodes in a tree */
+extern struct rb_node *rb_next(const struct rb_node *);
+extern struct rb_node *rb_prev(const struct rb_node *);
+extern struct rb_node *rb_first(const struct rb_root *);
+extern struct rb_node *rb_last(const struct rb_root *);
+
+/* Fast replacement of a single node without remove/rebalance/add/rebalance */
+extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root);
+
+static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
+ struct rb_node ** rb_link)
+{
+ node->rb_parent_color = (unsigned long )parent;
+ node->rb_left = node->rb_right = NULL;
+
+ *rb_link = node;
+}
+
+#endif /* _LINUX_RBTREE_H */
diff --git a/smartt-perf/util/include/linux/string.h b/smartt-perf/util/include/linux/string.h
new file mode 100644
index 0000000..3b2f590
--- /dev/null
+++ b/smartt-perf/util/include/linux/string.h
@@ -0,0 +1 @@
+#include <string.h>
diff --git a/smartt-perf/util/include/linux/stringify.h b/smartt-perf/util/include/linux/stringify.h
new file mode 100644
index 0000000..841cec8
--- /dev/null
+++ b/smartt-perf/util/include/linux/stringify.h
@@ -0,0 +1,12 @@
+#ifndef __LINUX_STRINGIFY_H
+#define __LINUX_STRINGIFY_H
+
+/* Indirect stringification. Doing two levels allows the parameter to be a
+ * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
+#endif /* !__LINUX_STRINGIFY_H */
diff --git a/smartt-perf/util/include/linux/swab.h b/smartt-perf/util/include/linux/swab.h
new file mode 100644
index 0000000..ea0c02f
--- /dev/null
+++ b/smartt-perf/util/include/linux/swab.h
@@ -0,0 +1,299 @@
+#ifndef _LINUX_SWAB_H
+#define _LINUX_SWAB_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <asm/swab.h>
+
+/*
+ * casts are necessary for constants, because we never know how for sure
+ * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way.
+ */
+#define ___constant_swab16(x) ((__u16)( \
+ (((__u16)(x) & (__u16)0x00ffU) << 8) | \
+ (((__u16)(x) & (__u16)0xff00U) >> 8)))
+
+#define ___constant_swab32(x) ((__u32)( \
+ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
+ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
+ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
+ (((__u32)(x) & (__u32)0xff000000UL) >> 24)))
+
+#define ___constant_swab64(x) ((__u64)( \
+ (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \
+ (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \
+ (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \
+ (((__u64)(x) & (__u64)0x00000000ff000000ULL) << 8) | \
+ (((__u64)(x) & (__u64)0x000000ff00000000ULL) >> 8) | \
+ (((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | \
+ (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \
+ (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56)))
+
+#define ___constant_swahw32(x) ((__u32)( \
+ (((__u32)(x) & (__u32)0x0000ffffUL) << 16) | \
+ (((__u32)(x) & (__u32)0xffff0000UL) >> 16)))
+
+#define ___constant_swahb32(x) ((__u32)( \
+ (((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | \
+ (((__u32)(x) & (__u32)0xff00ff00UL) >> 8)))
+
+/*
+ * Implement the following as inlines, but define the interface using
+ * macros to allow constant folding when possible:
+ * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32
+ */
+
+static inline __attribute_const__ __u16 __fswab16(__u16 val)
+{
+#ifdef __arch_swab16
+ return __arch_swab16(val);
+#else
+ return ___constant_swab16(val);
+#endif
+}
+
+static inline __attribute_const__ __u32 __fswab32(__u32 val)
+{
+#ifdef __arch_swab32
+ return __arch_swab32(val);
+#else
+ return ___constant_swab32(val);
+#endif
+}
+
+static inline __attribute_const__ __u64 __fswab64(__u64 val)
+{
+#ifdef __arch_swab64
+ return __arch_swab64(val);
+#elif defined(__SWAB_64_THRU_32__)
+ __u32 h = val >> 32;
+ __u32 l = val & ((1ULL << 32) - 1);
+ return (((__u64)__fswab32(l)) << 32) | ((__u64)(__fswab32(h)));
+#else
+ return ___constant_swab64(val);
+#endif
+}
+
+static inline __attribute_const__ __u32 __fswahw32(__u32 val)
+{
+#ifdef __arch_swahw32
+ return __arch_swahw32(val);
+#else
+ return ___constant_swahw32(val);
+#endif
+}
+
+static inline __attribute_const__ __u32 __fswahb32(__u32 val)
+{
+#ifdef __arch_swahb32
+ return __arch_swahb32(val);
+#else
+ return ___constant_swahb32(val);
+#endif
+}
+
+/**
+ * __swab16 - return a byteswapped 16-bit value
+ * @x: value to byteswap
+ */
+#define __swab16(x) \
+ (__builtin_constant_p((__u16)(x)) ? \
+ ___constant_swab16(x) : \
+ __fswab16(x))
+
+/**
+ * __swab32 - return a byteswapped 32-bit value
+ * @x: value to byteswap
+ */
+#define __swab32(x) \
+ (__builtin_constant_p((__u32)(x)) ? \
+ ___constant_swab32(x) : \
+ __fswab32(x))
+
+/**
+ * __swab64 - return a byteswapped 64-bit value
+ * @x: value to byteswap
+ */
+#define __swab64(x) \
+ (__builtin_constant_p((__u64)(x)) ? \
+ ___constant_swab64(x) : \
+ __fswab64(x))
+
+/**
+ * __swahw32 - return a word-swapped 32-bit value
+ * @x: value to wordswap
+ *
+ * __swahw32(0x12340000) is 0x00001234
+ */
+#define __swahw32(x) \
+ (__builtin_constant_p((__u32)(x)) ? \
+ ___constant_swahw32(x) : \
+ __fswahw32(x))
+
+/**
+ * __swahb32 - return a high and low byte-swapped 32-bit value
+ * @x: value to byteswap
+ *
+ * __swahb32(0x12345678) is 0x34127856
+ */
+#define __swahb32(x) \
+ (__builtin_constant_p((__u32)(x)) ? \
+ ___constant_swahb32(x) : \
+ __fswahb32(x))
+
+/**
+ * __swab16p - return a byteswapped 16-bit value from a pointer
+ * @p: pointer to a naturally-aligned 16-bit value
+ */
+static inline __u16 __swab16p(const __u16 *p)
+{
+#ifdef __arch_swab16p
+ return __arch_swab16p(p);
+#else
+ return __swab16(*p);
+#endif
+}
+
+/**
+ * __swab32p - return a byteswapped 32-bit value from a pointer
+ * @p: pointer to a naturally-aligned 32-bit value
+ */
+static inline __u32 __swab32p(const __u32 *p)
+{
+#ifdef __arch_swab32p
+ return __arch_swab32p(p);
+#else
+ return __swab32(*p);
+#endif
+}
+
+/**
+ * __swab64p - return a byteswapped 64-bit value from a pointer
+ * @p: pointer to a naturally-aligned 64-bit value
+ */
+static inline __u64 __swab64p(const __u64 *p)
+{
+#ifdef __arch_swab64p
+ return __arch_swab64p(p);
+#else
+ return __swab64(*p);
+#endif
+}
+
+/**
+ * __swahw32p - return a wordswapped 32-bit value from a pointer
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahw32() for details of wordswapping.
+ */
+static inline __u32 __swahw32p(const __u32 *p)
+{
+#ifdef __arch_swahw32p
+ return __arch_swahw32p(p);
+#else
+ return __swahw32(*p);
+#endif
+}
+
+/**
+ * __swahb32p - return a high and low byteswapped 32-bit value from a pointer
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahb32() for details of high/low byteswapping.
+ */
+static inline __u32 __swahb32p(const __u32 *p)
+{
+#ifdef __arch_swahb32p
+ return __arch_swahb32p(p);
+#else
+ return __swahb32(*p);
+#endif
+}
+
+/**
+ * __swab16s - byteswap a 16-bit value in-place
+ * @p: pointer to a naturally-aligned 16-bit value
+ */
+static inline void __swab16s(__u16 *p)
+{
+#ifdef __arch_swab16s
+ __arch_swab16s(p);
+#else
+ *p = __swab16p(p);
+#endif
+}
+/**
+ * __swab32s - byteswap a 32-bit value in-place
+ * @p: pointer to a naturally-aligned 32-bit value
+ */
+static inline void __swab32s(__u32 *p)
+{
+#ifdef __arch_swab32s
+ __arch_swab32s(p);
+#else
+ *p = __swab32p(p);
+#endif
+}
+
+/**
+ * __swab64s - byteswap a 64-bit value in-place
+ * @p: pointer to a naturally-aligned 64-bit value
+ */
+static inline void __swab64s(__u64 *p)
+{
+#ifdef __arch_swab64s
+ __arch_swab64s(p);
+#else
+ *p = __swab64p(p);
+#endif
+}
+
+/**
+ * __swahw32s - wordswap a 32-bit value in-place
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahw32() for details of wordswapping
+ */
+static inline void __swahw32s(__u32 *p)
+{
+#ifdef __arch_swahw32s
+ __arch_swahw32s(p);
+#else
+ *p = __swahw32p(p);
+#endif
+}
+
+/**
+ * __swahb32s - high and low byteswap a 32-bit value in-place
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahb32() for details of high and low byte swapping
+ */
+static inline void __swahb32s(__u32 *p)
+{
+#ifdef __arch_swahb32s
+ __arch_swahb32s(p);
+#else
+ *p = __swahb32p(p);
+#endif
+}
+
+#ifdef __KERNEL__
+# define swab16 __swab16
+# define swab32 __swab32
+# define swab64 __swab64
+# define swahw32 __swahw32
+# define swahb32 __swahb32
+# define swab16p __swab16p
+# define swab32p __swab32p
+# define swab64p __swab64p
+# define swahw32p __swahw32p
+# define swahb32p __swahb32p
+# define swab16s __swab16s
+# define swab32s __swab32s
+# define swab64s __swab64s
+# define swahw32s __swahw32s
+# define swahb32s __swahb32s
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SWAB_H */
diff --git a/smartt-perf/util/include/linux/types.h b/smartt-perf/util/include/linux/types.h
new file mode 100644
index 0000000..12de3b8
--- /dev/null
+++ b/smartt-perf/util/include/linux/types.h
@@ -0,0 +1,21 @@
+#ifndef _PERF_LINUX_TYPES_H_
+#define _PERF_LINUX_TYPES_H_
+
+#include <asm/types.h>
+
+#define DECLARE_BITMAP(name,bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#endif
diff --git a/smartt-perf/util/levenshtein.c b/smartt-perf/util/levenshtein.c
new file mode 100644
index 0000000..e521d15
--- /dev/null
+++ b/smartt-perf/util/levenshtein.c
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "levenshtein.h"
+
+/*
+ * This function implements the Damerau-Levenshtein algorithm to
+ * calculate a distance between strings.
+ *
+ * Basically, it says how many letters need to be swapped, substituted,
+ * deleted from, or added to string1, at least, to get string2.
+ *
+ * The idea is to build a distance matrix for the substrings of both
+ * strings. To avoid a large space complexity, only the last three rows
+ * are kept in memory (if swaps had the same or higher cost as one deletion
+ * plus one insertion, only two rows would be needed).
+ *
+ * At any stage, "i + 1" denotes the length of the current substring of
+ * string1 that the distance is calculated for.
+ *
+ * row2 holds the current row, row1 the previous row (i.e. for the substring
+ * of string1 of length "i"), and row0 the row before that.
+ *
+ * In other words, at the start of the big loop, row2[j + 1] contains the
+ * Damerau-Levenshtein distance between the substring of string1 of length
+ * "i" and the substring of string2 of length "j + 1".
+ *
+ * All the big loop does is determine the partial minimum-cost paths.
+ *
+ * It does so by calculating the costs of the path ending in characters
+ * i (in string1) and j (in string2), respectively, given that the last
+ * operation is a substition, a swap, a deletion, or an insertion.
+ *
+ * This implementation allows the costs to be weighted:
+ *
+ * - w (as in "sWap")
+ * - s (as in "Substitution")
+ * - a (for insertion, AKA "Add")
+ * - d (as in "Deletion")
+ *
+ * Note that this algorithm calculates a distance _iff_ d == a.
+ */
+int levenshtein(const char *string1, const char *string2,
+ int w, int s, int a, int d)
+{
+ int len1 = strlen(string1), len2 = strlen(string2);
+ int *row0 = malloc(sizeof(int) * (len2 + 1));
+ int *row1 = malloc(sizeof(int) * (len2 + 1));
+ int *row2 = malloc(sizeof(int) * (len2 + 1));
+ int i, j;
+
+ for (j = 0; j <= len2; j++)
+ row1[j] = j * a;
+ for (i = 0; i < len1; i++) {
+ int *dummy;
+
+ row2[0] = (i + 1) * d;
+ for (j = 0; j < len2; j++) {
+ /* substitution */
+ row2[j + 1] = row1[j] + s * (string1[i] != string2[j]);
+ /* swap */
+ if (i > 0 && j > 0 && string1[i - 1] == string2[j] &&
+ string1[i] == string2[j - 1] &&
+ row2[j + 1] > row0[j - 1] + w)
+ row2[j + 1] = row0[j - 1] + w;
+ /* deletion */
+ if (row2[j + 1] > row1[j + 1] + d)
+ row2[j + 1] = row1[j + 1] + d;
+ /* insertion */
+ if (row2[j + 1] > row2[j] + a)
+ row2[j + 1] = row2[j] + a;
+ }
+
+ dummy = row0;
+ row0 = row1;
+ row1 = row2;
+ row2 = dummy;
+ }
+
+ i = row1[len2];
+ free(row0);
+ free(row1);
+ free(row2);
+
+ return i;
+}
diff --git a/smartt-perf/util/levenshtein.h b/smartt-perf/util/levenshtein.h
new file mode 100644
index 0000000..b0fcb6d
--- /dev/null
+++ b/smartt-perf/util/levenshtein.h
@@ -0,0 +1,8 @@
+#ifndef __PERF_LEVENSHTEIN_H
+#define __PERF_LEVENSHTEIN_H
+
+int levenshtein(const char *string1, const char *string2,
+ int swap_penalty, int substition_penalty,
+ int insertion_penalty, int deletion_penalty);
+
+#endif /* __PERF_LEVENSHTEIN_H */
diff --git a/smartt-perf/util/map.c b/smartt-perf/util/map.c
new file mode 100644
index 0000000..a16ecab
--- /dev/null
+++ b/smartt-perf/util/map.c
@@ -0,0 +1,683 @@
+#include "symbol.h"
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "map.h"
+
+const char *map_type__name[MAP__NR_TYPES] = {
+ [MAP__FUNCTION] = "Functions",
+ [MAP__VARIABLE] = "Variables",
+};
+
+static inline int is_anon_memory(const char *filename)
+{
+ return strcmp(filename, "//anon") == 0;
+}
+
+void map__init(struct map *self, enum map_type type,
+ u64 start, u64 end, u64 pgoff, struct dso *dso)
+{
+ self->type = type;
+ self->start = start;
+ self->end = end;
+ self->pgoff = pgoff;
+ self->dso = dso;
+ self->map_ip = map__map_ip;
+ self->unmap_ip = map__unmap_ip;
+ RB_CLEAR_NODE(&self->rb_node);
+ self->groups = NULL;
+ self->referenced = false;
+}
+
+struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
+ u64 pgoff, u32 pid, char *filename,
+ enum map_type type)
+{
+ struct map *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ char newfilename[PATH_MAX];
+ struct dso *dso;
+ int anon;
+
+ anon = is_anon_memory(filename);
+
+ if (anon) {
+ snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
+ filename = newfilename;
+ }
+
+ dso = __dsos__findnew(dsos__list, filename);
+ if (dso == NULL)
+ goto out_delete;
+
+ map__init(self, type, start, start + len, pgoff, dso);
+
+ if (anon) {
+set_identity:
+ self->map_ip = self->unmap_ip = identity__map_ip;
+ } else if (strcmp(filename, "[vdso]") == 0) {
+ dso__set_loaded(dso, self->type);
+ goto set_identity;
+ }
+ }
+ return self;
+out_delete:
+ free(self);
+ return NULL;
+}
+
+void map__delete(struct map *self)
+{
+ free(self);
+}
+
+void map__fixup_start(struct map *self)
+{
+ struct rb_root *symbols = &self->dso->symbols[self->type];
+ struct rb_node *nd = rb_first(symbols);
+ if (nd != NULL) {
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ self->start = sym->start;
+ }
+}
+
+void map__fixup_end(struct map *self)
+{
+ struct rb_root *symbols = &self->dso->symbols[self->type];
+ struct rb_node *nd = rb_last(symbols);
+ if (nd != NULL) {
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ self->end = sym->end;
+ }
+}
+
+#define DSO__DELETED "(deleted)"
+
+int map__load(struct map *self, symbol_filter_t filter)
+{
+ const char *name = self->dso->long_name;
+ int nr;
+
+ if (dso__loaded(self->dso, self->type))
+ return 0;
+
+ nr = dso__load(self->dso, self, filter);
+ if (nr < 0) {
+ if (self->dso->has_build_id) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->dso->build_id,
+ sizeof(self->dso->build_id),
+ sbuild_id);
+ pr_warning("%s with build id %s not found",
+ name, sbuild_id);
+ } else
+ pr_warning("Failed to open %s", name);
+
+ pr_warning(", continuing without symbols\n");
+ return -1;
+ } else if (nr == 0) {
+ const size_t len = strlen(name);
+ const size_t real_len = len - sizeof(DSO__DELETED);
+
+ if (len > sizeof(DSO__DELETED) &&
+ strcmp(name + real_len + 1, DSO__DELETED) == 0) {
+ pr_warning("%.*s was updated, restart the long "
+ "running apps that use it!\n",
+ (int)real_len, name);
+ } else {
+ pr_warning("no symbols found in %s, maybe install "
+ "a debug package?\n", name);
+ }
+
+ return -1;
+ }
+ /*
+ * Only applies to the kernel, as its symtabs aren't relative like the
+ * module ones.
+ */
+ if (self->dso->kernel)
+ map__reloc_vmlinux(self);
+
+ return 0;
+}
+
+struct symbol *map__find_symbol(struct map *self, u64 addr,
+ symbol_filter_t filter)
+{
+ if (map__load(self, filter) < 0)
+ return NULL;
+
+ return dso__find_symbol(self->dso, self->type, addr);
+}
+
+struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
+ symbol_filter_t filter)
+{
+ if (map__load(self, filter) < 0)
+ return NULL;
+
+ if (!dso__sorted_by_name(self->dso, self->type))
+ dso__sort_by_name(self->dso, self->type);
+
+ return dso__find_symbol_by_name(self->dso, self->type, name);
+}
+
+struct map *map__clone(struct map *self)
+{
+ struct map *map = malloc(sizeof(*self));
+
+ if (!map)
+ return NULL;
+
+ memcpy(map, self, sizeof(*self));
+
+ return map;
+}
+
+int map__overlap(struct map *l, struct map *r)
+{
+ if (l->start > r->start) {
+ struct map *t = l;
+ l = r;
+ r = t;
+ }
+
+ if (l->end > r->start)
+ return 1;
+
+ return 0;
+}
+
+size_t map__fprintf(struct map *self, FILE *fp)
+{
+ return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n",
+ self->start, self->end, self->pgoff, self->dso->name);
+}
+
+/*
+ * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.
+ * map->dso->adjust_symbols==1 for ET_EXEC-like cases.
+ */
+u64 map__rip_2objdump(struct map *map, u64 rip)
+{
+ u64 addr = map->dso->adjust_symbols ?
+ map->unmap_ip(map, rip) : /* RIP -> IP */
+ rip;
+ return addr;
+}
+
+u64 map__objdump_2ip(struct map *map, u64 addr)
+{
+ u64 ip = map->dso->adjust_symbols ?
+ addr :
+ map->unmap_ip(map, addr); /* RIP -> IP */
+ return ip;
+}
+
+void map_groups__init(struct map_groups *self)
+{
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i) {
+ self->maps[i] = RB_ROOT;
+ INIT_LIST_HEAD(&self->removed_maps[i]);
+ }
+ self->machine = NULL;
+}
+
+static void maps__delete(struct rb_root *self)
+{
+ struct rb_node *next = rb_first(self);
+
+ while (next) {
+ struct map *pos = rb_entry(next, struct map, rb_node);
+
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, self);
+ map__delete(pos);
+ }
+}
+
+static void maps__delete_removed(struct list_head *self)
+{
+ struct map *pos, *n;
+
+ list_for_each_entry_safe(pos, n, self, node) {
+ list_del(&pos->node);
+ map__delete(pos);
+ }
+}
+
+void map_groups__exit(struct map_groups *self)
+{
+ int i;
+
+ for (i = 0; i < MAP__NR_TYPES; ++i) {
+ maps__delete(&self->maps[i]);
+ maps__delete_removed(&self->removed_maps[i]);
+ }
+}
+
+void map_groups__flush(struct map_groups *self)
+{
+ int type;
+
+ for (type = 0; type < MAP__NR_TYPES; type++) {
+ struct rb_root *root = &self->maps[type];
+ struct rb_node *next = rb_first(root);
+
+ while (next) {
+ struct map *pos = rb_entry(next, struct map, rb_node);
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, root);
+ /*
+ * We may have references to this map, for
+ * instance in some hist_entry instances, so
+ * just move them to a separate list.
+ */
+ list_add_tail(&pos->node, &self->removed_maps[pos->type]);
+ }
+ }
+}
+
+struct symbol *map_groups__find_symbol(struct map_groups *self,
+ enum map_type type, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ struct map *map = map_groups__find(self, type, addr);
+
+ if (map != NULL) {
+ if (mapp != NULL)
+ *mapp = map;
+ return map__find_symbol(map, map->map_ip(map, addr), filter);
+ }
+
+ return NULL;
+}
+
+struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
+ enum map_type type,
+ const char *name,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+ struct symbol *sym = map__find_symbol_by_name(pos, name, filter);
+
+ if (sym == NULL)
+ continue;
+ if (mapp != NULL)
+ *mapp = pos;
+ return sym;
+ }
+
+ return NULL;
+}
+
+size_t __map_groups__fprintf_maps(struct map_groups *self,
+ enum map_type type, int verbose, FILE *fp)
+{
+ size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+ printed += fprintf(fp, "Map:");
+ printed += map__fprintf(pos, fp);
+ if (verbose > 2) {
+ printed += dso__fprintf(pos->dso, type, fp);
+ printed += fprintf(fp, "--\n");
+ }
+ }
+
+ return printed;
+}
+
+size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp)
+{
+ size_t printed = 0, i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ printed += __map_groups__fprintf_maps(self, i, verbose, fp);
+ return printed;
+}
+
+static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
+ enum map_type type,
+ int verbose, FILE *fp)
+{
+ struct map *pos;
+ size_t printed = 0;
+
+ list_for_each_entry(pos, &self->removed_maps[type], node) {
+ printed += fprintf(fp, "Map:");
+ printed += map__fprintf(pos, fp);
+ if (verbose > 1) {
+ printed += dso__fprintf(pos->dso, type, fp);
+ printed += fprintf(fp, "--\n");
+ }
+ }
+ return printed;
+}
+
+static size_t map_groups__fprintf_removed_maps(struct map_groups *self,
+ int verbose, FILE *fp)
+{
+ size_t printed = 0, i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp);
+ return printed;
+}
+
+size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp)
+{
+ size_t printed = map_groups__fprintf_maps(self, verbose, fp);
+ printed += fprintf(fp, "Removed maps:\n");
+ return printed + map_groups__fprintf_removed_maps(self, verbose, fp);
+}
+
+int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
+ int verbose, FILE *fp)
+{
+ struct rb_root *root = &self->maps[map->type];
+ struct rb_node *next = rb_first(root);
+ int err = 0;
+
+ while (next) {
+ struct map *pos = rb_entry(next, struct map, rb_node);
+ next = rb_next(&pos->rb_node);
+
+ if (!map__overlap(pos, map))
+ continue;
+
+ if (verbose >= 2) {
+ fputs("overlapping maps:\n", fp);
+ map__fprintf(map, fp);
+ map__fprintf(pos, fp);
+ }
+
+ rb_erase(&pos->rb_node, root);
+ /*
+ * Now check if we need to create new maps for areas not
+ * overlapped by the new map:
+ */
+ if (map->start > pos->start) {
+ struct map *before = map__clone(pos);
+
+ if (before == NULL) {
+ err = -ENOMEM;
+ goto move_map;
+ }
+
+ before->end = map->start - 1;
+ map_groups__insert(self, before);
+ if (verbose >= 2)
+ map__fprintf(before, fp);
+ }
+
+ if (map->end < pos->end) {
+ struct map *after = map__clone(pos);
+
+ if (after == NULL) {
+ err = -ENOMEM;
+ goto move_map;
+ }
+
+ after->start = map->end + 1;
+ map_groups__insert(self, after);
+ if (verbose >= 2)
+ map__fprintf(after, fp);
+ }
+move_map:
+ /*
+ * If we have references, just move them to a separate list.
+ */
+ if (pos->referenced)
+ list_add_tail(&pos->node, &self->removed_maps[map->type]);
+ else
+ map__delete(pos);
+
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * XXX This should not really _copy_ te maps, but refcount them.
+ */
+int map_groups__clone(struct map_groups *self,
+ struct map_groups *parent, enum map_type type)
+{
+ struct rb_node *nd;
+ for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *map = rb_entry(nd, struct map, rb_node);
+ struct map *new = map__clone(map);
+ if (new == NULL)
+ return -ENOMEM;
+ map_groups__insert(self, new);
+ }
+ return 0;
+}
+
+static u64 map__reloc_map_ip(struct map *map, u64 ip)
+{
+ return ip + (s64)map->pgoff;
+}
+
+static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
+{
+ return ip - (s64)map->pgoff;
+}
+
+void map__reloc_vmlinux(struct map *self)
+{
+ struct kmap *kmap = map__kmap(self);
+ s64 reloc;
+
+ if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
+ return;
+
+ reloc = (kmap->ref_reloc_sym->unrelocated_addr -
+ kmap->ref_reloc_sym->addr);
+
+ if (!reloc)
+ return;
+
+ self->map_ip = map__reloc_map_ip;
+ self->unmap_ip = map__reloc_unmap_ip;
+ self->pgoff = reloc;
+}
+
+void maps__insert(struct rb_root *maps, struct map *map)
+{
+ struct rb_node **p = &maps->rb_node;
+ struct rb_node *parent = NULL;
+ const u64 ip = map->start;
+ struct map *m;
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct map, rb_node);
+ if (ip < m->start)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&map->rb_node, parent, p);
+ rb_insert_color(&map->rb_node, maps);
+}
+
+void maps__remove(struct rb_root *self, struct map *map)
+{
+ rb_erase(&map->rb_node, self);
+}
+
+struct map *maps__find(struct rb_root *maps, u64 ip)
+{
+ struct rb_node **p = &maps->rb_node;
+ struct rb_node *parent = NULL;
+ struct map *m;
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct map, rb_node);
+ if (ip < m->start)
+ p = &(*p)->rb_left;
+ else if (ip > m->end)
+ p = &(*p)->rb_right;
+ else
+ return m;
+ }
+
+ return NULL;
+}
+
+int machine__init(struct machine *self, const char *root_dir, pid_t pid)
+{
+ map_groups__init(&self->kmaps);
+ RB_CLEAR_NODE(&self->rb_node);
+ INIT_LIST_HEAD(&self->user_dsos);
+ INIT_LIST_HEAD(&self->kernel_dsos);
+
+ self->kmaps.machine = self;
+ self->pid = pid;
+ self->root_dir = strdup(root_dir);
+ return self->root_dir == NULL ? -ENOMEM : 0;
+}
+
+static void dsos__delete(struct list_head *self)
+{
+ struct dso *pos, *n;
+
+ list_for_each_entry_safe(pos, n, self, node) {
+ list_del(&pos->node);
+ dso__delete(pos);
+ }
+}
+
+void machine__exit(struct machine *self)
+{
+ map_groups__exit(&self->kmaps);
+ dsos__delete(&self->user_dsos);
+ dsos__delete(&self->kernel_dsos);
+ free(self->root_dir);
+ self->root_dir = NULL;
+}
+
+void machine__delete(struct machine *self)
+{
+ machine__exit(self);
+ free(self);
+}
+
+struct machine *machines__add(struct rb_root *self, pid_t pid,
+ const char *root_dir)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct machine *pos, *machine = malloc(sizeof(*machine));
+
+ if (!machine)
+ return NULL;
+
+ if (machine__init(machine, root_dir, pid) != 0) {
+ free(machine);
+ return NULL;
+ }
+
+ while (*p != NULL) {
+ parent = *p;
+ pos = rb_entry(parent, struct machine, rb_node);
+ if (pid < pos->pid)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&machine->rb_node, parent, p);
+ rb_insert_color(&machine->rb_node, self);
+
+ return machine;
+}
+
+struct machine *machines__find(struct rb_root *self, pid_t pid)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct machine *machine;
+ struct machine *default_machine = NULL;
+
+ while (*p != NULL) {
+ parent = *p;
+ machine = rb_entry(parent, struct machine, rb_node);
+ if (pid < machine->pid)
+ p = &(*p)->rb_left;
+ else if (pid > machine->pid)
+ p = &(*p)->rb_right;
+ else
+ return machine;
+ if (!machine->pid)
+ default_machine = machine;
+ }
+
+ return default_machine;
+}
+
+struct machine *machines__findnew(struct rb_root *self, pid_t pid)
+{
+ char path[PATH_MAX];
+ const char *root_dir;
+ struct machine *machine = machines__find(self, pid);
+
+ if (!machine || machine->pid != pid) {
+ if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID)
+ root_dir = "";
+ else {
+ if (!symbol_conf.guestmount)
+ goto out;
+ sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
+ if (access(path, R_OK)) {
+ pr_err("Can't access file %s\n", path);
+ goto out;
+ }
+ root_dir = path;
+ }
+ machine = machines__add(self, pid, root_dir);
+ }
+
+out:
+ return machine;
+}
+
+void machines__process(struct rb_root *self, machine__process_t process, void *data)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ process(pos, data);
+ }
+}
+
+char *machine__mmap_name(struct machine *self, char *bf, size_t size)
+{
+ if (machine__is_host(self))
+ snprintf(bf, size, "[%s]", "kernel.kallsyms");
+ else if (machine__is_default_guest(self))
+ snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
+ else
+ snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid);
+
+ return bf;
+}
diff --git a/smartt-perf/util/map.h b/smartt-perf/util/map.h
new file mode 100644
index 0000000..b397c03
--- /dev/null
+++ b/smartt-perf/util/map.h
@@ -0,0 +1,237 @@
+#ifndef __PERF_MAP_H
+#define __PERF_MAP_H
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "types.h"
+
+enum map_type {
+ MAP__FUNCTION = 0,
+ MAP__VARIABLE,
+};
+
+#define MAP__NR_TYPES (MAP__VARIABLE + 1)
+
+extern const char *map_type__name[MAP__NR_TYPES];
+
+struct dso;
+struct ref_reloc_sym;
+struct map_groups;
+struct machine;
+
+struct map {
+ union {
+ struct rb_node rb_node;
+ struct list_head node;
+ };
+ u64 start;
+ u64 end;
+ u8 /* enum map_type */ type;
+ bool referenced;
+ u32 priv;
+ u64 pgoff;
+
+ /* ip -> dso rip */
+ u64 (*map_ip)(struct map *, u64);
+ /* dso rip -> ip */
+ u64 (*unmap_ip)(struct map *, u64);
+
+ struct dso *dso;
+ struct map_groups *groups;
+};
+
+struct kmap {
+ struct ref_reloc_sym *ref_reloc_sym;
+ struct map_groups *kmaps;
+};
+
+struct map_groups {
+ struct rb_root maps[MAP__NR_TYPES];
+ struct list_head removed_maps[MAP__NR_TYPES];
+ struct machine *machine;
+};
+
+/* Native host kernel uses -1 as pid index in machine */
+#define HOST_KERNEL_ID (-1)
+#define DEFAULT_GUEST_KERNEL_ID (0)
+
+struct machine {
+ struct rb_node rb_node;
+ pid_t pid;
+ char *root_dir;
+ struct list_head user_dsos;
+ struct list_head kernel_dsos;
+ struct map_groups kmaps;
+ struct map *vmlinux_maps[MAP__NR_TYPES];
+};
+
+static inline
+struct map *machine__kernel_map(struct machine *self, enum map_type type)
+{
+ return self->vmlinux_maps[type];
+}
+
+static inline struct kmap *map__kmap(struct map *self)
+{
+ return (struct kmap *)(self + 1);
+}
+
+static inline u64 map__map_ip(struct map *map, u64 ip)
+{
+ return ip - map->start + map->pgoff;
+}
+
+static inline u64 map__unmap_ip(struct map *map, u64 ip)
+{
+ return ip + map->start - map->pgoff;
+}
+
+static inline u64 identity__map_ip(struct map *map __used, u64 ip)
+{
+ return ip;
+}
+
+
+/* rip/ip <-> addr suitable for passing to `objdump --start-address=` */
+u64 map__rip_2objdump(struct map *map, u64 rip);
+u64 map__objdump_2ip(struct map *map, u64 addr);
+
+struct symbol;
+
+typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
+
+void map__init(struct map *self, enum map_type type,
+ u64 start, u64 end, u64 pgoff, struct dso *dso);
+struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
+ u64 pgoff, u32 pid, char *filename,
+ enum map_type type);
+void map__delete(struct map *self);
+struct map *map__clone(struct map *self);
+int map__overlap(struct map *l, struct map *r);
+size_t map__fprintf(struct map *self, FILE *fp);
+
+int map__load(struct map *self, symbol_filter_t filter);
+struct symbol *map__find_symbol(struct map *self,
+ u64 addr, symbol_filter_t filter);
+struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
+ symbol_filter_t filter);
+void map__fixup_start(struct map *self);
+void map__fixup_end(struct map *self);
+
+void map__reloc_vmlinux(struct map *self);
+
+size_t __map_groups__fprintf_maps(struct map_groups *self,
+ enum map_type type, int verbose, FILE *fp);
+void maps__insert(struct rb_root *maps, struct map *map);
+void maps__remove(struct rb_root *self, struct map *map);
+struct map *maps__find(struct rb_root *maps, u64 addr);
+void map_groups__init(struct map_groups *self);
+void map_groups__exit(struct map_groups *self);
+int map_groups__clone(struct map_groups *self,
+ struct map_groups *parent, enum map_type type);
+size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
+size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp);
+
+typedef void (*machine__process_t)(struct machine *self, void *data);
+
+void machines__process(struct rb_root *self, machine__process_t process, void *data);
+struct machine *machines__add(struct rb_root *self, pid_t pid,
+ const char *root_dir);
+struct machine *machines__find_host(struct rb_root *self);
+struct machine *machines__find(struct rb_root *self, pid_t pid);
+struct machine *machines__findnew(struct rb_root *self, pid_t pid);
+char *machine__mmap_name(struct machine *self, char *bf, size_t size);
+int machine__init(struct machine *self, const char *root_dir, pid_t pid);
+void machine__exit(struct machine *self);
+void machine__delete(struct machine *self);
+
+/*
+ * Default guest kernel is defined by parameter --guestkallsyms
+ * and --guestmodules
+ */
+static inline bool machine__is_default_guest(struct machine *self)
+{
+ return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false;
+}
+
+static inline bool machine__is_host(struct machine *self)
+{
+ return self ? self->pid == HOST_KERNEL_ID : false;
+}
+
+static inline void map_groups__insert(struct map_groups *self, struct map *map)
+{
+ maps__insert(&self->maps[map->type], map);
+ map->groups = self;
+}
+
+static inline void map_groups__remove(struct map_groups *self, struct map *map)
+{
+ maps__remove(&self->maps[map->type], map);
+}
+
+static inline struct map *map_groups__find(struct map_groups *self,
+ enum map_type type, u64 addr)
+{
+ return maps__find(&self->maps[type], addr);
+}
+
+struct symbol *map_groups__find_symbol(struct map_groups *self,
+ enum map_type type, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter);
+
+struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
+ enum map_type type,
+ const char *name,
+ struct map **mapp,
+ symbol_filter_t filter);
+
+static inline
+struct symbol *machine__find_kernel_symbol(struct machine *self,
+ enum map_type type, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter);
+}
+
+static inline
+struct symbol *machine__find_kernel_function(struct machine *self, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter);
+}
+
+static inline
+struct symbol *map_groups__find_function_by_name(struct map_groups *self,
+ const char *name, struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
+}
+
+static inline
+struct symbol *machine__find_kernel_function_by_name(struct machine *self,
+ const char *name,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_function_by_name(&self->kmaps, name, mapp,
+ filter);
+}
+
+int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
+ int verbose, FILE *fp);
+
+struct map *map_groups__find_by_name(struct map_groups *self,
+ enum map_type type, const char *name);
+struct map *machine__new_module(struct machine *self, u64 start, const char *filename);
+
+void map_groups__flush(struct map_groups *self);
+
+#endif /* __PERF_MAP_H */
diff --git a/smartt-perf/util/newt.c b/smartt-perf/util/newt.c
new file mode 100644
index 0000000..7537ca1
--- /dev/null
+++ b/smartt-perf/util/newt.c
@@ -0,0 +1,1178 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#undef _GNU_SOURCE
+/*
+ * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
+ * the build if it isn't defined. Use the equivalent one that glibc
+ * has on features.h.
+ */
+#include <features.h>
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
+#endif
+#include <slang.h>
+#include <stdlib.h>
+#include <newt.h>
+#include <sys/ttydefaults.h>
+
+#include "cache.h"
+#include "hist.h"
+#include "pstack.h"
+#include "session.h"
+#include "sort.h"
+#include "symbol.h"
+
+#if SLANG_VERSION < 20104
+#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
+#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
+#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
+ (char *)fg, (char *)bg)
+#else
+#define slsmg_printf SLsmg_printf
+#define slsmg_write_nstring SLsmg_write_nstring
+#define sltt_set_color SLtt_set_color
+#endif
+
+struct ui_progress {
+ newtComponent form, scale;
+};
+
+struct ui_progress *ui_progress__new(const char *title, u64 total)
+{
+ struct ui_progress *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ int cols;
+
+ if (use_browser <= 0)
+ return self;
+ newtGetScreenSize(&cols, NULL);
+ cols -= 4;
+ newtCenteredWindow(cols, 1, title);
+ self->form = newtForm(NULL, NULL, 0);
+ if (self->form == NULL)
+ goto out_free_self;
+ self->scale = newtScale(0, 0, cols, total);
+ if (self->scale == NULL)
+ goto out_free_form;
+ newtFormAddComponent(self->form, self->scale);
+ newtRefresh();
+ }
+
+ return self;
+
+out_free_form:
+ newtFormDestroy(self->form);
+out_free_self:
+ free(self);
+ return NULL;
+}
+
+void ui_progress__update(struct ui_progress *self, u64 curr)
+{
+ /*
+ * FIXME: We should have a per UI backend way of showing progress,
+ * stdio will just show a percentage as NN%, etc.
+ */
+ if (use_browser <= 0)
+ return;
+ newtScaleSet(self->scale, curr);
+ newtRefresh();
+}
+
+void ui_progress__delete(struct ui_progress *self)
+{
+ if (use_browser > 0) {
+ newtFormDestroy(self->form);
+ newtPopWindow();
+ }
+ free(self);
+}
+
+static void ui_helpline__pop(void)
+{
+ newtPopHelpLine();
+}
+
+static void ui_helpline__push(const char *msg)
+{
+ newtPushHelpLine(msg);
+}
+
+static void ui_helpline__vpush(const char *fmt, va_list ap)
+{
+ char *s;
+
+ if (vasprintf(&s, fmt, ap) < 0)
+ vfprintf(stderr, fmt, ap);
+ else {
+ ui_helpline__push(s);
+ free(s);
+ }
+}
+
+static void ui_helpline__fpush(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ ui_helpline__vpush(fmt, ap);
+ va_end(ap);
+}
+
+static void ui_helpline__puts(const char *msg)
+{
+ ui_helpline__pop();
+ ui_helpline__push(msg);
+}
+
+static char browser__last_msg[1024];
+
+int browser__show_help(const char *format, va_list ap)
+{
+ int ret;
+ static int backlog;
+
+ ret = vsnprintf(browser__last_msg + backlog,
+ sizeof(browser__last_msg) - backlog, format, ap);
+ backlog += ret;
+
+ if (browser__last_msg[backlog - 1] == '\n') {
+ ui_helpline__puts(browser__last_msg);
+ newtRefresh();
+ backlog = 0;
+ }
+
+ return ret;
+}
+
+static void newt_form__set_exit_keys(newtComponent self)
+{
+ newtFormAddHotKey(self, NEWT_KEY_LEFT);
+ newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
+ newtFormAddHotKey(self, 'Q');
+ newtFormAddHotKey(self, 'q');
+ newtFormAddHotKey(self, CTRL('c'));
+}
+
+static newtComponent newt_form__new(void)
+{
+ newtComponent self = newtForm(NULL, NULL, 0);
+ if (self)
+ newt_form__set_exit_keys(self);
+ return self;
+}
+
+static int popup_menu(int argc, char * const argv[])
+{
+ struct newtExitStruct es;
+ int i, rc = -1, max_len = 5;
+ newtComponent listbox, form = newt_form__new();
+
+ if (form == NULL)
+ return -1;
+
+ listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
+ if (listbox == NULL)
+ goto out_destroy_form;
+
+ newtFormAddComponent(form, listbox);
+
+ for (i = 0; i < argc; ++i) {
+ int len = strlen(argv[i]);
+ if (len > max_len)
+ max_len = len;
+ if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
+ goto out_destroy_form;
+ }
+
+ newtCenteredWindow(max_len, argc, NULL);
+ newtFormRun(form, &es);
+ rc = newtListboxGetCurrent(listbox) - NULL;
+ if (es.reason == NEWT_EXIT_HOTKEY)
+ rc = -1;
+ newtPopWindow();
+out_destroy_form:
+ newtFormDestroy(form);
+ return rc;
+}
+
+static int ui__help_window(const char *text)
+{
+ struct newtExitStruct es;
+ newtComponent tb, form = newt_form__new();
+ int rc = -1;
+ int max_len = 0, nr_lines = 0;
+ const char *t;
+
+ if (form == NULL)
+ return -1;
+
+ t = text;
+ while (1) {
+ const char *sep = strchr(t, '\n');
+ int len;
+
+ if (sep == NULL)
+ sep = strchr(t, '\0');
+ len = sep - t;
+ if (max_len < len)
+ max_len = len;
+ ++nr_lines;
+ if (*sep == '\0')
+ break;
+ t = sep + 1;
+ }
+
+ tb = newtTextbox(0, 0, max_len, nr_lines, 0);
+ if (tb == NULL)
+ goto out_destroy_form;
+
+ newtTextboxSetText(tb, text);
+ newtFormAddComponent(form, tb);
+ newtCenteredWindow(max_len, nr_lines, NULL);
+ newtFormRun(form, &es);
+ newtPopWindow();
+ rc = 0;
+out_destroy_form:
+ newtFormDestroy(form);
+ return rc;
+}
+
+static bool dialog_yesno(const char *msg)
+{
+ /* newtWinChoice should really be accepting const char pointers... */
+ char yes[] = "Yes", no[] = "No";
+ return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
+}
+
+static void ui__error_window(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
+ va_end(ap);
+}
+
+#define HE_COLORSET_TOP 50
+#define HE_COLORSET_MEDIUM 51
+#define HE_COLORSET_NORMAL 52
+#define HE_COLORSET_SELECTED 53
+#define HE_COLORSET_CODE 54
+
+static int ui_browser__percent_color(double percent, bool current)
+{
+ if (current)
+ return HE_COLORSET_SELECTED;
+ if (percent >= MIN_RED)
+ return HE_COLORSET_TOP;
+ if (percent >= MIN_GREEN)
+ return HE_COLORSET_MEDIUM;
+ return HE_COLORSET_NORMAL;
+}
+
+struct ui_browser {
+ newtComponent form, sb;
+ u64 index, first_visible_entry_idx;
+ void *first_visible_entry, *entries;
+ u16 top, left, width, height;
+ void *priv;
+ u32 nr_entries;
+};
+
+static void ui_browser__refresh_dimensions(struct ui_browser *self)
+{
+ int cols, rows;
+ newtGetScreenSize(&cols, &rows);
+
+ if (self->width > cols - 4)
+ self->width = cols - 4;
+ self->height = rows - 5;
+ if (self->height > self->nr_entries)
+ self->height = self->nr_entries;
+ self->top = (rows - self->height) / 2;
+ self->left = (cols - self->width) / 2;
+}
+
+static void ui_browser__reset_index(struct ui_browser *self)
+{
+ self->index = self->first_visible_entry_idx = 0;
+ self->first_visible_entry = NULL;
+}
+
+static int objdump_line__show(struct objdump_line *self, struct list_head *head,
+ int width, struct hist_entry *he, int len,
+ bool current_entry)
+{
+ if (self->offset != -1) {
+ struct symbol *sym = he->ms.sym;
+ unsigned int hits = 0;
+ double percent = 0.0;
+ int color;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_ext *sym_ext = priv->ext;
+ struct sym_hist *h = priv->hist;
+ s64 offset = self->offset;
+ struct objdump_line *next = objdump__get_next_ip_line(head, self);
+
+ while (offset < (s64)len &&
+ (next == NULL || offset < next->offset)) {
+ if (sym_ext) {
+ percent += sym_ext[offset].percent;
+ } else
+ hits += h->ip[offset];
+
+ ++offset;
+ }
+
+ if (sym_ext == NULL && h->sum)
+ percent = 100.0 * hits / h->sum;
+
+ color = ui_browser__percent_color(percent, current_entry);
+ SLsmg_set_color(color);
+ slsmg_printf(" %7.2f ", percent);
+ if (!current_entry)
+ SLsmg_set_color(HE_COLORSET_CODE);
+ } else {
+ int color = ui_browser__percent_color(0, current_entry);
+ SLsmg_set_color(color);
+ slsmg_write_nstring(" ", 9);
+ }
+
+ SLsmg_write_char(':');
+ slsmg_write_nstring(" ", 8);
+ if (!*self->line)
+ slsmg_write_nstring(" ", width - 18);
+ else
+ slsmg_write_nstring(self->line, width - 18);
+
+ return 0;
+}
+
+static int ui_browser__refresh_entries(struct ui_browser *self)
+{
+ struct objdump_line *pos;
+ struct list_head *head = self->entries;
+ struct hist_entry *he = self->priv;
+ int row = 0;
+ int len = he->ms.sym->end - he->ms.sym->start;
+
+ if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
+ self->first_visible_entry = head->next;
+
+ pos = list_entry(self->first_visible_entry, struct objdump_line, node);
+
+ list_for_each_entry_from(pos, head, node) {
+ bool current_entry = (self->first_visible_entry_idx + row) == self->index;
+ SLsmg_gotorc(self->top + row, self->left);
+ objdump_line__show(pos, head, self->width,
+ he, len, current_entry);
+ if (++row == self->height)
+ break;
+ }
+
+ SLsmg_set_color(HE_COLORSET_NORMAL);
+ SLsmg_fill_region(self->top + row, self->left,
+ self->height - row, self->width, ' ');
+
+ return 0;
+}
+
+static int ui_browser__run(struct ui_browser *self, const char *title,
+ struct newtExitStruct *es)
+{
+ if (self->form) {
+ newtFormDestroy(self->form);
+ newtPopWindow();
+ }
+
+ ui_browser__refresh_dimensions(self);
+ newtCenteredWindow(self->width + 2, self->height, title);
+ self->form = newt_form__new();
+ if (self->form == NULL)
+ return -1;
+
+ self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
+ HE_COLORSET_NORMAL,
+ HE_COLORSET_SELECTED);
+ if (self->sb == NULL)
+ return -1;
+
+ newtFormAddHotKey(self->form, NEWT_KEY_UP);
+ newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
+ newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
+ newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
+ newtFormAddHotKey(self->form, ' ');
+ newtFormAddHotKey(self->form, NEWT_KEY_HOME);
+ newtFormAddHotKey(self->form, NEWT_KEY_END);
+ newtFormAddHotKey(self->form, NEWT_KEY_TAB);
+ newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
+
+ if (ui_browser__refresh_entries(self) < 0)
+ return -1;
+ newtFormAddComponent(self->form, self->sb);
+
+ while (1) {
+ unsigned int offset;
+
+ newtFormRun(self->form, es);
+
+ if (es->reason != NEWT_EXIT_HOTKEY)
+ break;
+ if (is_exit_key(es->u.key))
+ return es->u.key;
+ switch (es->u.key) {
+ case NEWT_KEY_DOWN:
+ if (self->index == self->nr_entries - 1)
+ break;
+ ++self->index;
+ if (self->index == self->first_visible_entry_idx + self->height) {
+ struct list_head *pos = self->first_visible_entry;
+ ++self->first_visible_entry_idx;
+ self->first_visible_entry = pos->next;
+ }
+ break;
+ case NEWT_KEY_UP:
+ if (self->index == 0)
+ break;
+ --self->index;
+ if (self->index < self->first_visible_entry_idx) {
+ struct list_head *pos = self->first_visible_entry;
+ --self->first_visible_entry_idx;
+ self->first_visible_entry = pos->prev;
+ }
+ break;
+ case NEWT_KEY_PGDN:
+ case ' ':
+ if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
+ break;
+
+ offset = self->height;
+ if (self->index + offset > self->nr_entries - 1)
+ offset = self->nr_entries - 1 - self->index;
+ self->index += offset;
+ self->first_visible_entry_idx += offset;
+
+ while (offset--) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->next;
+ }
+
+ break;
+ case NEWT_KEY_PGUP:
+ if (self->first_visible_entry_idx == 0)
+ break;
+
+ if (self->first_visible_entry_idx < self->height)
+ offset = self->first_visible_entry_idx;
+ else
+ offset = self->height;
+
+ self->index -= offset;
+ self->first_visible_entry_idx -= offset;
+
+ while (offset--) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->prev;
+ }
+ break;
+ case NEWT_KEY_HOME:
+ ui_browser__reset_index(self);
+ break;
+ case NEWT_KEY_END: {
+ struct list_head *head = self->entries;
+ offset = self->height - 1;
+
+ if (offset > self->nr_entries)
+ offset = self->nr_entries;
+
+ self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
+ self->first_visible_entry = head->prev;
+ while (offset-- != 0) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->prev;
+ }
+ }
+ break;
+ case NEWT_KEY_RIGHT:
+ case NEWT_KEY_LEFT:
+ case NEWT_KEY_TAB:
+ return es->u.key;
+ default:
+ continue;
+ }
+ if (ui_browser__refresh_entries(self) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * When debugging newt problems it was useful to be able to "unroll"
+ * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
+ * a source file with the sequence of calls to these methods, to then
+ * tweak the arrays to get the intended results, so I'm keeping this code
+ * here, may be useful again in the future.
+ */
+#undef NEWT_DEBUG
+
+static void newt_checkbox_tree__add(newtComponent tree, const char *str,
+ void *priv, int *indexes)
+{
+#ifdef NEWT_DEBUG
+ /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
+ int i = 0, len = 40 - strlen(str);
+
+ fprintf(stderr,
+ "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
+ len, len, " ", str, priv);
+ while (indexes[i] != NEWT_ARG_LAST) {
+ if (indexes[i] != NEWT_ARG_APPEND)
+ fprintf(stderr, " %d,", indexes[i]);
+ else
+ fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
+ ++i;
+ }
+ fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
+ fflush(stderr);
+#endif
+ newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
+}
+
+static char *callchain_list__sym_name(struct callchain_list *self,
+ char *bf, size_t bfsize)
+{
+ if (self->ms.sym)
+ return self->ms.sym->name;
+
+ snprintf(bf, bfsize, "%#Lx", self->ip);
+ return bf;
+}
+
+static void __callchain__append_graph_browser(struct callchain_node *self,
+ newtComponent tree, u64 total,
+ int *indexes, int depth)
+{
+ struct rb_node *node;
+ u64 new_total, remaining;
+ int idx = 0;
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = self->children_hit;
+ else
+ new_total = total;
+
+ remaining = new_total;
+ node = rb_first(&self->rb_root);
+ while (node) {
+ struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
+ struct rb_node *next = rb_next(node);
+ u64 cumul = cumul_hits(child);
+ struct callchain_list *chain;
+ int first = true, printed = 0;
+ int chain_idx = -1;
+ remaining -= cumul;
+
+ indexes[depth] = NEWT_ARG_APPEND;
+ indexes[depth + 1] = NEWT_ARG_LAST;
+
+ list_for_each_entry(chain, &child->val, list) {
+ char ipstr[BITS_PER_LONG / 4 + 1],
+ *alloc_str = NULL;
+ const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+
+ if (first) {
+ double percent = cumul * 100.0 / new_total;
+
+ first = false;
+ if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
+ str = "Not enough memory!";
+ else
+ str = alloc_str;
+ } else {
+ indexes[depth] = idx;
+ indexes[depth + 1] = NEWT_ARG_APPEND;
+ indexes[depth + 2] = NEWT_ARG_LAST;
+ ++chain_idx;
+ }
+ newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
+ free(alloc_str);
+ ++printed;
+ }
+
+ indexes[depth] = idx;
+ if (chain_idx != -1)
+ indexes[depth + 1] = chain_idx;
+ if (printed != 0)
+ ++idx;
+ __callchain__append_graph_browser(child, tree, new_total, indexes,
+ depth + (chain_idx != -1 ? 2 : 1));
+ node = next;
+ }
+}
+
+static void callchain__append_graph_browser(struct callchain_node *self,
+ newtComponent tree, u64 total,
+ int *indexes, int parent_idx)
+{
+ struct callchain_list *chain;
+ int i = 0;
+
+ indexes[1] = NEWT_ARG_APPEND;
+ indexes[2] = NEWT_ARG_LAST;
+
+ list_for_each_entry(chain, &self->val, list) {
+ char ipstr[BITS_PER_LONG / 4 + 1], *str;
+
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+
+ if (!i++ && sort__first_dimension == SORT_SYM)
+ continue;
+
+ str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
+ }
+
+ indexes[1] = parent_idx;
+ indexes[2] = NEWT_ARG_APPEND;
+ indexes[3] = NEWT_ARG_LAST;
+ __callchain__append_graph_browser(self, tree, total, indexes, 2);
+}
+
+static void hist_entry__append_callchain_browser(struct hist_entry *self,
+ newtComponent tree, u64 total, int parent_idx)
+{
+ struct rb_node *rb_node;
+ int indexes[1024] = { [0] = parent_idx, };
+ int idx = 0;
+ struct callchain_node *chain;
+
+ rb_node = rb_first(&self->sorted_chain);
+ while (rb_node) {
+ chain = rb_entry(rb_node, struct callchain_node, rb_node);
+ switch (callchain_param.mode) {
+ case CHAIN_FLAT:
+ break;
+ case CHAIN_GRAPH_ABS: /* falldown */
+ case CHAIN_GRAPH_REL:
+ callchain__append_graph_browser(chain, tree, total, indexes, idx++);
+ break;
+ case CHAIN_NONE:
+ default:
+ break;
+ }
+ rb_node = rb_next(rb_node);
+ }
+}
+
+static size_t hist_entry__append_browser(struct hist_entry *self,
+ newtComponent tree, u64 total)
+{
+ char s[256];
+ size_t ret;
+
+ if (symbol_conf.exclude_other && !self->parent)
+ return 0;
+
+ ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
+ false, 0, false, total);
+ if (symbol_conf.use_callchain) {
+ int indexes[2];
+
+ indexes[0] = NEWT_ARG_APPEND;
+ indexes[1] = NEWT_ARG_LAST;
+ newt_checkbox_tree__add(tree, s, &self->ms, indexes);
+ } else
+ newtListboxAppendEntry(tree, s, &self->ms);
+
+ return ret;
+}
+
+int hist_entry__tui_annotate(struct hist_entry *self)
+{
+ struct ui_browser browser;
+ struct newtExitStruct es;
+ struct objdump_line *pos, *n;
+ LIST_HEAD(head);
+ int ret;
+
+ if (self->ms.sym == NULL)
+ return -1;
+
+ if (self->ms.map->dso->annotate_warned)
+ return -1;
+
+ if (hist_entry__annotate(self, &head) < 0) {
+ ui__error_window(browser__last_msg);
+ return -1;
+ }
+
+ ui_helpline__push("Press <- or ESC to exit");
+
+ memset(&browser, 0, sizeof(browser));
+ browser.entries = &head;
+ browser.priv = self;
+ list_for_each_entry(pos, &head, node) {
+ size_t line_len = strlen(pos->line);
+ if (browser.width < line_len)
+ browser.width = line_len;
+ ++browser.nr_entries;
+ }
+
+ browser.width += 18; /* Percentage */
+ ret = ui_browser__run(&browser, self->ms.sym->name, &es);
+ newtFormDestroy(browser.form);
+ newtPopWindow();
+ list_for_each_entry_safe(pos, n, &head, node) {
+ list_del(&pos->node);
+ objdump_line__free(pos);
+ }
+ ui_helpline__pop();
+ return ret;
+}
+
+static const void *newt__symbol_tree_get_current(newtComponent self)
+{
+ if (symbol_conf.use_callchain)
+ return newtCheckboxTreeGetCurrent(self);
+ return newtListboxGetCurrent(self);
+}
+
+static void hist_browser__selection(newtComponent self, void *data)
+{
+ const struct map_symbol **symbol_ptr = data;
+ *symbol_ptr = newt__symbol_tree_get_current(self);
+}
+
+struct hist_browser {
+ newtComponent form, tree;
+ const struct map_symbol *selection;
+};
+
+static struct hist_browser *hist_browser__new(void)
+{
+ struct hist_browser *self = malloc(sizeof(*self));
+
+ if (self != NULL)
+ self->form = NULL;
+
+ return self;
+}
+
+static void hist_browser__delete(struct hist_browser *self)
+{
+ newtFormDestroy(self->form);
+ newtPopWindow();
+ free(self);
+}
+
+static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
+ const char *title)
+{
+ int max_len = 0, idx, cols, rows;
+ struct ui_progress *progress;
+ struct rb_node *nd;
+ u64 curr_hist = 0;
+ char seq[] = ".", unit;
+ char str[256];
+ unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
+
+ if (self->form) {
+ newtFormDestroy(self->form);
+ newtPopWindow();
+ }
+
+ nr_events = convert_unit(nr_events, &unit);
+ snprintf(str, sizeof(str), "Events: %lu%c ",
+ nr_events, unit);
+ newtDrawRootText(0, 0, str);
+
+ newtGetScreenSize(NULL, &rows);
+
+ if (symbol_conf.use_callchain)
+ self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
+ NEWT_FLAG_SCROLL);
+ else
+ self->tree = newtListbox(0, 0, rows - 5,
+ (NEWT_FLAG_SCROLL |
+ NEWT_FLAG_RETURNEXIT));
+
+ newtComponentAddCallback(self->tree, hist_browser__selection,
+ &self->selection);
+
+ progress = ui_progress__new("Adding entries to the browser...",
+ hists->nr_entries);
+ if (progress == NULL)
+ return -1;
+
+ idx = 0;
+ for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ int len;
+
+ if (h->filtered)
+ continue;
+
+ len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
+ if (len > max_len)
+ max_len = len;
+ if (symbol_conf.use_callchain)
+ hist_entry__append_callchain_browser(h, self->tree,
+ hists->stats.total_period, idx++);
+ ++curr_hist;
+ if (curr_hist % 5)
+ ui_progress__update(progress, curr_hist);
+ }
+
+ ui_progress__delete(progress);
+
+ newtGetScreenSize(&cols, &rows);
+
+ if (max_len > cols)
+ max_len = cols - 3;
+
+ if (!symbol_conf.use_callchain)
+ newtListboxSetWidth(self->tree, max_len);
+
+ newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
+ rows - 5, title);
+ self->form = newt_form__new();
+ if (self->form == NULL)
+ return -1;
+
+ newtFormAddHotKey(self->form, 'A');
+ newtFormAddHotKey(self->form, 'a');
+ newtFormAddHotKey(self->form, 'D');
+ newtFormAddHotKey(self->form, 'd');
+ newtFormAddHotKey(self->form, 'T');
+ newtFormAddHotKey(self->form, 't');
+ newtFormAddHotKey(self->form, '?');
+ newtFormAddHotKey(self->form, 'H');
+ newtFormAddHotKey(self->form, 'h');
+ newtFormAddHotKey(self->form, NEWT_KEY_F1);
+ newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
+ newtFormAddHotKey(self->form, NEWT_KEY_TAB);
+ newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
+ newtFormAddComponents(self->form, self->tree, NULL);
+ self->selection = newt__symbol_tree_get_current(self->tree);
+
+ return 0;
+}
+
+static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
+{
+ int *indexes;
+
+ if (!symbol_conf.use_callchain)
+ goto out;
+
+ indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
+ if (indexes) {
+ bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
+ free(indexes);
+ if (is_hist_entry)
+ goto out;
+ }
+ return NULL;
+out:
+ return container_of(self->selection, struct hist_entry, ms);
+}
+
+static struct thread *hist_browser__selected_thread(struct hist_browser *self)
+{
+ struct hist_entry *he = hist_browser__selected_entry(self);
+ return he ? he->thread : NULL;
+}
+
+static int hist_browser__title(char *bf, size_t size, const char *ev_name,
+ const struct dso *dso, const struct thread *thread)
+{
+ int printed = 0;
+
+ if (thread)
+ printed += snprintf(bf + printed, size - printed,
+ "Thread: %s(%d)",
+ (thread->comm_set ? thread->comm : ""),
+ thread->pid);
+ if (dso)
+ printed += snprintf(bf + printed, size - printed,
+ "%sDSO: %s", thread ? " " : "",
+ dso->short_name);
+ return printed ?: snprintf(bf, size, "Event: %s", ev_name);
+}
+
+int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
+{
+ struct hist_browser *browser = hist_browser__new();
+ struct pstack *fstack;
+ const struct thread *thread_filter = NULL;
+ const struct dso *dso_filter = NULL;
+ struct newtExitStruct es;
+ char msg[160];
+ int key = -1;
+
+ if (browser == NULL)
+ return -1;
+
+ fstack = pstack__new(2);
+ if (fstack == NULL)
+ goto out;
+
+ ui_helpline__push(helpline);
+
+ hist_browser__title(msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
+ if (hist_browser__populate(browser, self, msg) < 0)
+ goto out_free_stack;
+
+ while (1) {
+ const struct thread *thread;
+ const struct dso *dso;
+ char *options[16];
+ int nr_options = 0, choice = 0, i,
+ annotate = -2, zoom_dso = -2, zoom_thread = -2;
+
+ newtFormRun(browser->form, &es);
+
+ thread = hist_browser__selected_thread(browser);
+ dso = browser->selection->map ? browser->selection->map->dso : NULL;
+
+ if (es.reason == NEWT_EXIT_HOTKEY) {
+ key = es.u.key;
+
+ switch (key) {
+ case NEWT_KEY_F1:
+ goto do_help;
+ case NEWT_KEY_TAB:
+ case NEWT_KEY_UNTAB:
+ /*
+ * Exit the browser, let hists__browser_tree
+ * go to the next or previous
+ */
+ goto out_free_stack;
+ default:;
+ }
+
+ key = toupper(key);
+ switch (key) {
+ case 'A':
+ if (browser->selection->map == NULL &&
+ browser->selection->map->dso->annotate_warned)
+ continue;
+ goto do_annotate;
+ case 'D':
+ goto zoom_dso;
+ case 'T':
+ goto zoom_thread;
+ case 'H':
+ case '?':
+do_help:
+ ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
+ "<- Zoom out\n"
+ "a Annotate current symbol\n"
+ "h/?/F1 Show this window\n"
+ "d Zoom into current DSO\n"
+ "t Zoom into current Thread\n"
+ "q/CTRL+C Exit browser");
+ continue;
+ default:;
+ }
+ if (is_exit_key(key)) {
+ if (key == NEWT_KEY_ESCAPE) {
+ if (dialog_yesno("Do you really want to exit?"))
+ break;
+ else
+ continue;
+ } else
+ break;
+ }
+
+ if (es.u.key == NEWT_KEY_LEFT) {
+ const void *top;
+
+ if (pstack__empty(fstack))
+ continue;
+ top = pstack__pop(fstack);
+ if (top == &dso_filter)
+ goto zoom_out_dso;
+ if (top == &thread_filter)
+ goto zoom_out_thread;
+ continue;
+ }
+ }
+
+ if (browser->selection->sym != NULL &&
+ !browser->selection->map->dso->annotate_warned &&
+ asprintf(&options[nr_options], "Annotate %s",
+ browser->selection->sym->name) > 0)
+ annotate = nr_options++;
+
+ if (thread != NULL &&
+ asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
+ (thread_filter ? "out of" : "into"),
+ (thread->comm_set ? thread->comm : ""),
+ thread->pid) > 0)
+ zoom_thread = nr_options++;
+
+ if (dso != NULL &&
+ asprintf(&options[nr_options], "Zoom %s %s DSO",
+ (dso_filter ? "out of" : "into"),
+ (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
+ zoom_dso = nr_options++;
+
+ options[nr_options++] = (char *)"Exit";
+
+ choice = popup_menu(nr_options, options);
+
+ for (i = 0; i < nr_options - 1; ++i)
+ free(options[i]);
+
+ if (choice == nr_options - 1)
+ break;
+
+ if (choice == -1)
+ continue;
+
+ if (choice == annotate) {
+ struct hist_entry *he;
+do_annotate:
+ if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
+ browser->selection->map->dso->annotate_warned = 1;
+ ui_helpline__puts("No vmlinux file found, can't "
+ "annotate with just a "
+ "kallsyms file");
+ continue;
+ }
+
+ he = hist_browser__selected_entry(browser);
+ if (he == NULL)
+ continue;
+
+ hist_entry__tui_annotate(he);
+ } else if (choice == zoom_dso) {
+zoom_dso:
+ if (dso_filter) {
+ pstack__remove(fstack, &dso_filter);
+zoom_out_dso:
+ ui_helpline__pop();
+ dso_filter = NULL;
+ } else {
+ if (dso == NULL)
+ continue;
+ ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
+ dso->kernel ? "the Kernel" : dso->short_name);
+ dso_filter = dso;
+ pstack__push(fstack, &dso_filter);
+ }
+ hists__filter_by_dso(self, dso_filter);
+ hist_browser__title(msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
+ if (hist_browser__populate(browser, self, msg) < 0)
+ goto out;
+ } else if (choice == zoom_thread) {
+zoom_thread:
+ if (thread_filter) {
+ pstack__remove(fstack, &thread_filter);
+zoom_out_thread:
+ ui_helpline__pop();
+ thread_filter = NULL;
+ } else {
+ ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
+ thread->comm_set ? thread->comm : "",
+ thread->pid);
+ thread_filter = thread;
+ pstack__push(fstack, &thread_filter);
+ }
+ hists__filter_by_thread(self, thread_filter);
+ hist_browser__title(msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
+ if (hist_browser__populate(browser, self, msg) < 0)
+ goto out;
+ }
+ }
+out_free_stack:
+ pstack__delete(fstack);
+out:
+ hist_browser__delete(browser);
+ return key;
+}
+
+int hists__tui_browse_tree(struct rb_root *self, const char *help)
+{
+ struct rb_node *first = rb_first(self), *nd = first, *next;
+ int key = 0;
+
+ while (nd) {
+ struct hists *hists = rb_entry(nd, struct hists, rb_node);
+ const char *ev_name = __event_name(hists->type, hists->config);
+
+ key = hists__browse(hists, help, ev_name);
+
+ if (is_exit_key(key))
+ break;
+
+ switch (key) {
+ case NEWT_KEY_TAB:
+ next = rb_next(nd);
+ if (next)
+ nd = next;
+ break;
+ case NEWT_KEY_UNTAB:
+ if (nd == first)
+ continue;
+ nd = rb_prev(nd);
+ default:
+ break;
+ }
+ }
+
+ return key;
+}
+
+static struct newtPercentTreeColors {
+ const char *topColorFg, *topColorBg;
+ const char *mediumColorFg, *mediumColorBg;
+ const char *normalColorFg, *normalColorBg;
+ const char *selColorFg, *selColorBg;
+ const char *codeColorFg, *codeColorBg;
+} defaultPercentTreeColors = {
+ "red", "lightgray",
+ "green", "lightgray",
+ "black", "lightgray",
+ "lightgray", "magenta",
+ "blue", "lightgray",
+};
+
+void setup_browser(void)
+{
+ struct newtPercentTreeColors *c = &defaultPercentTreeColors;
+
+ if (!isatty(1) || !use_browser || dump_trace) {
+ use_browser = 0;
+ setup_pager();
+ return;
+ }
+
+ use_browser = 1;
+ newtInit();
+ newtCls();
+ ui_helpline__puts(" ");
+ sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
+ sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
+ sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
+ sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
+ sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
+}
+
+void exit_browser(bool wait_for_ok)
+{
+ if (use_browser > 0) {
+ if (wait_for_ok) {
+ char title[] = "Fatal Error", ok[] = "Ok";
+ newtWinMessage(title, ok, browser__last_msg);
+ }
+ newtFinished();
+ }
+}
diff --git a/smartt-perf/util/pager.c b/smartt-perf/util/pager.c
new file mode 100644
index 0000000..1915de2
--- /dev/null
+++ b/smartt-perf/util/pager.c
@@ -0,0 +1,96 @@
+#include "cache.h"
+#include "run-command.h"
+#include "sigchain.h"
+
+/*
+ * This is split up from the rest of git so that we can do
+ * something different on Windows.
+ */
+
+static int spawned_pager;
+
+static void pager_preexec(void)
+{
+ /*
+ * Work around bug in "less" by not starting it until we
+ * have real input
+ */
+ fd_set in;
+
+ FD_ZERO(&in);
+ FD_SET(0, &in);
+ select(1, &in, NULL, &in, NULL);
+
+ setenv("LESS", "FRSX", 0);
+}
+
+static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+static struct child_process pager_process;
+
+static void wait_for_pager(void)
+{
+ fflush(stdout);
+ fflush(stderr);
+ /* signal EOF to pager */
+ close(1);
+ close(2);
+ finish_command(&pager_process);
+}
+
+static void wait_for_pager_signal(int signo)
+{
+ wait_for_pager();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
+void setup_pager(void)
+{
+ const char *pager = getenv("PERF_PAGER");
+
+ if (!isatty(1))
+ return;
+ if (!pager) {
+ if (!pager_program)
+ perf_config(perf_default_config, NULL);
+ pager = pager_program;
+ }
+ if (!pager)
+ pager = getenv("PAGER");
+ if (!pager)
+ pager = "less";
+ else if (!*pager || !strcmp(pager, "cat"))
+ return;
+
+ spawned_pager = 1; /* means we are emitting to terminal */
+
+ /* spawn the pager */
+ pager_argv[2] = pager;
+ pager_process.argv = pager_argv;
+ pager_process.in = -1;
+ pager_process.preexec_cb = pager_preexec;
+
+ if (start_command(&pager_process))
+ return;
+
+ /* original process continues, but writes to the pipe */
+ dup2(pager_process.in, 1);
+ if (isatty(2))
+ dup2(pager_process.in, 2);
+ close(pager_process.in);
+
+ /* this makes sure that the parent terminates after the pager */
+ sigchain_push_common(wait_for_pager_signal);
+ atexit(wait_for_pager);
+}
+
+int pager_in_use(void)
+{
+ const char *env;
+
+ if (spawned_pager)
+ return 1;
+
+ env = getenv("PERF_PAGER_IN_USE");
+ return env ? perf_config_bool("PERF_PAGER_IN_USE", env) : 0;
+}
diff --git a/smartt-perf/util/parse-events.c b/smartt-perf/util/parse-events.c
new file mode 100644
index 0000000..1e4b9d6
--- /dev/null
+++ b/smartt-perf/util/parse-events.c
@@ -0,0 +1,1013 @@
+#include "linux/hw_breakpoint.h"
+#include "util.h"
+#include "../perf.h"
+#include "evsel.h"
+#include "parse-options.h"
+#include "parse-events.h"
+#include "exec_cmd.h"
+#include "string.h"
+#include "symbol.h"
+#include "cache.h"
+#include "header.h"
+#include "debugfs.h"
+
+int nr_counters;
+
+LIST_HEAD(evsel_list);
+
+struct event_symbol {
+ u8 type;
+ u64 config;
+ const char *symbol;
+ const char *alias;
+};
+
+enum event_result {
+ EVT_FAILED,
+ EVT_HANDLED,
+ EVT_HANDLED_ALL
+};
+
+char debugfs_path[MAXPATHLEN];
+
+#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
+#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
+
+static struct event_symbol event_symbols[] = {
+ { CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
+ { CHW(INSTRUCTIONS), "instructions", "" },
+ { CHW(CACHE_REFERENCES), "cache-references", "" },
+ { CHW(CACHE_MISSES), "cache-misses", "" },
+ { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
+ { CHW(BRANCH_MISSES), "branch-misses", "" },
+ { CHW(BUS_CYCLES), "bus-cycles", "" },
+
+ { CSW(CPU_CLOCK), "cpu-clock", "" },
+ { CSW(TASK_CLOCK), "task-clock", "" },
+ { CSW(PAGE_FAULTS), "page-faults", "faults" },
+ { CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
+ { CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
+ { CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
+ { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
+ { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" },
+ { CSW(EMULATION_FAULTS), "emulation-faults", "" },
+};
+
+#define __PERF_EVENT_FIELD(config, name) \
+ ((config & PERF_EVENT_##name##_MASK) >> PERF_EVENT_##name##_SHIFT)
+
+#define PERF_EVENT_RAW(config) __PERF_EVENT_FIELD(config, RAW)
+#define PERF_EVENT_CONFIG(config) __PERF_EVENT_FIELD(config, CONFIG)
+#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
+#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
+
+static const char *hw_event_names[] = {
+ "cycles",
+ "instructions",
+ "cache-references",
+ "cache-misses",
+ "branches",
+ "branch-misses",
+ "bus-cycles",
+};
+
+static const char *sw_event_names[] = {
+ "cpu-clock-msecs",
+ "task-clock-msecs",
+ "page-faults",
+ "context-switches",
+ "CPU-migrations",
+ "minor-faults",
+ "major-faults",
+ "alignment-faults",
+ "emulation-faults",
+};
+
+#define MAX_ALIASES 8
+
+static const char *hw_cache[][MAX_ALIASES] = {
+ { "L1-dcache", "l1-d", "l1d", "L1-data", },
+ { "L1-icache", "l1-i", "l1i", "L1-instruction", },
+ { "LLC", "L2" },
+ { "dTLB", "d-tlb", "Data-TLB", },
+ { "iTLB", "i-tlb", "Instruction-TLB", },
+ { "branch", "branches", "bpu", "btb", "bpc", },
+};
+
+static const char *hw_cache_op[][MAX_ALIASES] = {
+ { "load", "loads", "read", },
+ { "store", "stores", "write", },
+ { "prefetch", "prefetches", "speculative-read", "speculative-load", },
+};
+
+static const char *hw_cache_result[][MAX_ALIASES] = {
+ { "refs", "Reference", "ops", "access", },
+ { "misses", "miss", },
+};
+
+#define C(x) PERF_COUNT_HW_CACHE_##x
+#define CACHE_READ (1 << C(OP_READ))
+#define CACHE_WRITE (1 << C(OP_WRITE))
+#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
+#define COP(x) (1 << x)
+
+/*
+ * cache operartion stat
+ * L1I : Read and prefetch only
+ * ITLB and BPU : Read-only
+ */
+static unsigned long hw_cache_stat[C(MAX)] = {
+ [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
+ [C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
+ [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
+ [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
+ [C(ITLB)] = (CACHE_READ),
+ [C(BPU)] = (CACHE_READ),
+};
+
+#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
+ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
+ if (sys_dirent.d_type == DT_DIR && \
+ (strcmp(sys_dirent.d_name, ".")) && \
+ (strcmp(sys_dirent.d_name, "..")))
+
+static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
+{
+ char evt_path[MAXPATHLEN];
+ int fd;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
+ sys_dir->d_name, evt_dir->d_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ return -EINVAL;
+ close(fd);
+
+ return 0;
+}
+
+#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \
+ while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
+ if (evt_dirent.d_type == DT_DIR && \
+ (strcmp(evt_dirent.d_name, ".")) && \
+ (strcmp(evt_dirent.d_name, "..")) && \
+ (!tp_event_has_id(&sys_dirent, &evt_dirent)))
+
+#define MAX_EVENT_LENGTH 512
+
+
+struct tracepoint_path *tracepoint_id_to_path(u64 config)
+{
+ struct tracepoint_path *path = NULL;
+ DIR *sys_dir, *evt_dir;
+ struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ char id_buf[4];
+ int fd;
+ u64 id;
+ char evt_path[MAXPATHLEN];
+ char dir_path[MAXPATHLEN];
+
+ if (debugfs_valid_mountpoint(debugfs_path))
+ return NULL;
+
+ sys_dir = opendir(debugfs_path);
+ if (!sys_dir)
+ return NULL;
+
+ for_each_subsystem(sys_dir, sys_dirent, sys_next) {
+
+ snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
+ sys_dirent.d_name);
+ evt_dir = opendir(dir_path);
+ if (!evt_dir)
+ continue;
+
+ for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,
+ evt_dirent.d_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ continue;
+ if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+ close(fd);
+ continue;
+ }
+ close(fd);
+ id = atoll(id_buf);
+ if (id == config) {
+ closedir(evt_dir);
+ closedir(sys_dir);
+ path = zalloc(sizeof(*path));
+ path->system = malloc(MAX_EVENT_LENGTH);
+ if (!path->system) {
+ free(path);
+ return NULL;
+ }
+ path->name = malloc(MAX_EVENT_LENGTH);
+ if (!path->name) {
+ free(path->system);
+ free(path);
+ return NULL;
+ }
+ strncpy(path->system, sys_dirent.d_name,
+ MAX_EVENT_LENGTH);
+ strncpy(path->name, evt_dirent.d_name,
+ MAX_EVENT_LENGTH);
+ return path;
+ }
+ }
+ closedir(evt_dir);
+ }
+
+ closedir(sys_dir);
+ return NULL;
+}
+
+#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
+static const char *tracepoint_id_to_name(u64 config)
+{
+ static char buf[TP_PATH_LEN];
+ struct tracepoint_path *path;
+
+ path = tracepoint_id_to_path(config);
+ if (path) {
+ snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
+ free(path->name);
+ free(path->system);
+ free(path);
+ } else
+ snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
+
+ return buf;
+}
+
+static int is_cache_op_valid(u8 cache_type, u8 cache_op)
+{
+ if (hw_cache_stat[cache_type] & COP(cache_op))
+ return 1; /* valid */
+ else
+ return 0; /* invalid */
+}
+
+static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
+{
+ static char name[50];
+
+ if (cache_result) {
+ sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
+ hw_cache_op[cache_op][0],
+ hw_cache_result[cache_result][0]);
+ } else {
+ sprintf(name, "%s-%s", hw_cache[cache_type][0],
+ hw_cache_op[cache_op][1]);
+ }
+
+ return name;
+}
+
+const char *event_name(struct perf_evsel *evsel)
+{
+ u64 config = evsel->attr.config;
+ int type = evsel->attr.type;
+
+ return __event_name(type, config);
+}
+
+const char *__event_name(int type, u64 config)
+{
+ static char buf[32];
+
+ if (type == PERF_TYPE_RAW) {
+ sprintf(buf, "raw 0x%" PRIx64, config);
+ return buf;
+ }
+
+ switch (type) {
+ case PERF_TYPE_HARDWARE:
+ if (config < PERF_COUNT_HW_MAX)
+ return hw_event_names[config];
+ return "unknown-hardware";
+
+ case PERF_TYPE_HW_CACHE: {
+ u8 cache_type, cache_op, cache_result;
+
+ cache_type = (config >> 0) & 0xff;
+ if (cache_type > PERF_COUNT_HW_CACHE_MAX)
+ return "unknown-ext-hardware-cache-type";
+
+ cache_op = (config >> 8) & 0xff;
+ if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX)
+ return "unknown-ext-hardware-cache-op";
+
+ cache_result = (config >> 16) & 0xff;
+ if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
+ return "unknown-ext-hardware-cache-result";
+
+ if (!is_cache_op_valid(cache_type, cache_op))
+ return "invalid-cache";
+
+ return event_cache_name(cache_type, cache_op, cache_result);
+ }
+
+ case PERF_TYPE_SOFTWARE:
+ if (config < PERF_COUNT_SW_MAX)
+ return sw_event_names[config];
+ return "unknown-software";
+
+ case PERF_TYPE_TRACEPOINT:
+ return tracepoint_id_to_name(config);
+
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
+{
+ int i, j;
+ int n, longest = -1;
+
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
+ n = strlen(names[i][j]);
+ if (n > longest && !strncasecmp(*str, names[i][j], n))
+ longest = n;
+ }
+ if (longest > 0) {
+ *str += longest;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static enum event_result
+parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
+{
+ const char *s = *str;
+ int cache_type = -1, cache_op = -1, cache_result = -1;
+
+ cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
+ /*
+ * No fallback - if we cannot get a clear cache type
+ * then bail out:
+ */
+ if (cache_type == -1)
+ return EVT_FAILED;
+
+ while ((cache_op == -1 || cache_result == -1) && *s == '-') {
+ ++s;
+
+ if (cache_op == -1) {
+ cache_op = parse_aliases(&s, hw_cache_op,
+ PERF_COUNT_HW_CACHE_OP_MAX);
+ if (cache_op >= 0) {
+ if (!is_cache_op_valid(cache_type, cache_op))
+ return 0;
+ continue;
+ }
+ }
+
+ if (cache_result == -1) {
+ cache_result = parse_aliases(&s, hw_cache_result,
+ PERF_COUNT_HW_CACHE_RESULT_MAX);
+ if (cache_result >= 0)
+ continue;
+ }
+
+ /*
+ * Can't parse this as a cache op or result, so back up
+ * to the '-'.
+ */
+ --s;
+ break;
+ }
+
+ /*
+ * Fall back to reads:
+ */
+ if (cache_op == -1)
+ cache_op = PERF_COUNT_HW_CACHE_OP_READ;
+
+ /*
+ * Fall back to accesses:
+ */
+ if (cache_result == -1)
+ cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
+
+ attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
+ attr->type = PERF_TYPE_HW_CACHE;
+
+ *str = s;
+ return EVT_HANDLED;
+}
+
+static enum event_result
+parse_single_tracepoint_event(char *sys_name,
+ const char *evt_name,
+ unsigned int evt_length,
+ struct perf_event_attr *attr,
+ const char **strp)
+{
+ char evt_path[MAXPATHLEN];
+ char id_buf[4];
+ u64 id;
+ int fd;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
+ sys_name, evt_name);
+
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ return EVT_FAILED;
+
+ if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+ close(fd);
+ return EVT_FAILED;
+ }
+
+ close(fd);
+ id = atoll(id_buf);
+ attr->config = id;
+ attr->type = PERF_TYPE_TRACEPOINT;
+ *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
+
+ attr->sample_type |= PERF_SAMPLE_RAW;
+ attr->sample_type |= PERF_SAMPLE_TIME;
+ attr->sample_type |= PERF_SAMPLE_CPU;
+
+ attr->sample_period = 1;
+
+
+ return EVT_HANDLED;
+}
+
+/* sys + ':' + event + ':' + flags*/
+#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
+static enum event_result
+parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
+ char *flags)
+{
+ char evt_path[MAXPATHLEN];
+ struct dirent *evt_ent;
+ DIR *evt_dir;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name);
+ evt_dir = opendir(evt_path);
+
+ if (!evt_dir) {
+ perror("Can't open event dir");
+ return EVT_FAILED;
+ }
+
+ while ((evt_ent = readdir(evt_dir))) {
+ char event_opt[MAX_EVOPT_LEN + 1];
+ int len;
+
+ if (!strcmp(evt_ent->d_name, ".")
+ || !strcmp(evt_ent->d_name, "..")
+ || !strcmp(evt_ent->d_name, "enable")
+ || !strcmp(evt_ent->d_name, "filter"))
+ continue;
+
+ if (!strglobmatch(evt_ent->d_name, evt_exp))
+ continue;
+
+ len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
+ evt_ent->d_name, flags ? ":" : "",
+ flags ?: "");
+ if (len < 0)
+ return EVT_FAILED;
+
+ if (parse_events(NULL, event_opt, 0))
+ return EVT_FAILED;
+ }
+
+ return EVT_HANDLED_ALL;
+}
+
+static enum event_result parse_tracepoint_event(const char **strp,
+ struct perf_event_attr *attr)
+{
+ const char *evt_name;
+ char *flags = NULL, *comma_loc;
+ char sys_name[MAX_EVENT_LENGTH];
+ unsigned int sys_length, evt_length;
+
+ if (debugfs_valid_mountpoint(debugfs_path))
+ return 0;
+
+ evt_name = strchr(*strp, ':');
+ if (!evt_name)
+ return EVT_FAILED;
+
+ sys_length = evt_name - *strp;
+ if (sys_length >= MAX_EVENT_LENGTH)
+ return 0;
+
+ strncpy(sys_name, *strp, sys_length);
+ sys_name[sys_length] = '\0';
+ evt_name = evt_name + 1;
+
+ comma_loc = strchr(evt_name, ',');
+ if (comma_loc) {
+ /* take the event name up to the comma */
+ evt_name = strndup(evt_name, comma_loc - evt_name);
+ }
+ flags = strchr(evt_name, ':');
+ if (flags) {
+ /* split it out: */
+ evt_name = strndup(evt_name, flags - evt_name);
+ flags++;
+ }
+
+ evt_length = strlen(evt_name);
+ if (evt_length >= MAX_EVENT_LENGTH)
+ return EVT_FAILED;
+ if (strpbrk(evt_name, "*?")) {
+ *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
+ return parse_multiple_tracepoint_event(sys_name, evt_name,
+ flags);
+ } else {
+ return parse_single_tracepoint_event(sys_name, evt_name,
+ evt_length, attr, strp);
+ }
+}
+
+static enum event_result
+parse_breakpoint_type(const char *type, const char **strp,
+ struct perf_event_attr *attr)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (!type[i])
+ break;
+
+ switch (type[i]) {
+ case 'r':
+ attr->bp_type |= HW_BREAKPOINT_R;
+ break;
+ case 'w':
+ attr->bp_type |= HW_BREAKPOINT_W;
+ break;
+ case 'x':
+ attr->bp_type |= HW_BREAKPOINT_X;
+ break;
+ default:
+ return EVT_FAILED;
+ }
+ }
+ if (!attr->bp_type) /* Default */
+ attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
+
+ *strp = type + i;
+
+ return EVT_HANDLED;
+}
+
+static enum event_result
+parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *target;
+ const char *type;
+ char *endaddr;
+ u64 addr;
+ enum event_result err;
+
+ target = strchr(*strp, ':');
+ if (!target)
+ return EVT_FAILED;
+
+ if (strncmp(*strp, "mem", target - *strp) != 0)
+ return EVT_FAILED;
+
+ target++;
+
+ addr = strtoull(target, &endaddr, 0);
+ if (target == endaddr)
+ return EVT_FAILED;
+
+ attr->bp_addr = addr;
+ *strp = endaddr;
+
+ type = strchr(target, ':');
+
+ /* If no type is defined, just rw as default */
+ if (!type) {
+ attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
+ } else {
+ err = parse_breakpoint_type(++type, strp, attr);
+ if (err == EVT_FAILED)
+ return EVT_FAILED;
+ }
+
+ /*
+ * We should find a nice way to override the access length
+ * Provide some defaults for now
+ */
+ if (attr->bp_type == HW_BREAKPOINT_X)
+ attr->bp_len = sizeof(long);
+ else
+ attr->bp_len = HW_BREAKPOINT_LEN_4;
+
+ attr->type = PERF_TYPE_BREAKPOINT;
+
+ return EVT_HANDLED;
+}
+
+static int check_events(const char *str, unsigned int i)
+{
+ int n;
+
+ n = strlen(event_symbols[i].symbol);
+ if (!strncmp(str, event_symbols[i].symbol, n))
+ return n;
+
+ n = strlen(event_symbols[i].alias);
+ if (n)
+ if (!strncmp(str, event_symbols[i].alias, n))
+ return n;
+ return 0;
+}
+
+static enum event_result
+parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ unsigned int i;
+ int n;
+
+ for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+ n = check_events(str, i);
+ if (n > 0) {
+ attr->type = event_symbols[i].type;
+ attr->config = event_symbols[i].config;
+ *strp = str + n;
+ return EVT_HANDLED;
+ }
+ }
+ return EVT_FAILED;
+}
+
+static enum event_result
+parse_raw_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ u64 config;
+ int n;
+
+ if (*str != 'r')
+ return EVT_FAILED;
+ n = hex2u64(str + 1, &config);
+ if (n > 0) {
+ *strp = str + n + 1;
+ attr->type = PERF_TYPE_RAW;
+ attr->config = config;
+ return EVT_HANDLED;
+ }
+ return EVT_FAILED;
+}
+
+static enum event_result
+parse_numeric_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ char *endp;
+ unsigned long type;
+ u64 config;
+
+ type = strtoul(str, &endp, 0);
+ if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
+ str = endp + 1;
+ config = strtoul(str, &endp, 0);
+ if (endp > str) {
+ attr->type = type;
+ attr->config = config;
+ *strp = endp;
+ return EVT_HANDLED;
+ }
+ }
+ return EVT_FAILED;
+}
+
+static enum event_result
+parse_event_modifier(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ int exclude = 0;
+ int eu = 0, ek = 0, eh = 0, precise = 0;
+
+ if (*str++ != ':')
+ return 0;
+ while (*str) {
+ if (*str == 'u') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
+ eu = 0;
+ } else if (*str == 'k') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
+ ek = 0;
+ } else if (*str == 'h') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
+ eh = 0;
+ } else if (*str == 'p') {
+ precise++;
+ } else
+ break;
+
+ ++str;
+ }
+ if (str >= *strp + 2) {
+ *strp = str;
+ attr->exclude_user = eu;
+ attr->exclude_kernel = ek;
+ attr->exclude_hv = eh;
+ attr->precise_ip = precise;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Each event can have multiple symbolic names.
+ * Symbolic names are (almost) exactly matched.
+ */
+static enum event_result
+parse_event_symbols(const char **str, struct perf_event_attr *attr)
+{
+ enum event_result ret;
+
+ ret = parse_tracepoint_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_raw_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_numeric_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_symbolic_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_generic_hw_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_breakpoint_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
+ fprintf(stderr, "Run 'perf list' for a list of valid events\n");
+ return EVT_FAILED;
+
+modifier:
+ parse_event_modifier(str, attr);
+
+ return ret;
+}
+
+int parse_events(const struct option *opt __used, const char *str, int unset __used)
+{
+ struct perf_event_attr attr;
+ enum event_result ret;
+
+ for (;;) {
+ memset(&attr, 0, sizeof(attr));
+ ret = parse_event_symbols(&str, &attr);
+ if (ret == EVT_FAILED)
+ return -1;
+
+ if (!(*str == 0 || *str == ',' || isspace(*str)))
+ return -1;
+
+ if (ret != EVT_HANDLED_ALL) {
+ struct perf_evsel *evsel;
+ evsel = perf_evsel__new(&attr,
+ nr_counters);
+ if (evsel == NULL)
+ return -1;
+ list_add_tail(&evsel->node, &evsel_list);
+ ++nr_counters;
+ }
+
+ if (*str == 0)
+ break;
+ if (*str == ',')
+ ++str;
+ while (isspace(*str))
+ ++str;
+ }
+
+ return 0;
+}
+
+int parse_filter(const struct option *opt __used, const char *str,
+ int unset __used)
+{
+ struct perf_evsel *last = NULL;
+
+ if (!list_empty(&evsel_list))
+ last = list_entry(evsel_list.prev, struct perf_evsel, node);
+
+ if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
+ fprintf(stderr,
+ "-F option should follow a -e tracepoint option\n");
+ return -1;
+ }
+
+ last->filter = strdup(str);
+ if (last->filter == NULL) {
+ fprintf(stderr, "not enough memory to hold filter string\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static const char * const event_type_descriptors[] = {
+ "Hardware event",
+ "Software event",
+ "Tracepoint event",
+ "Hardware cache event",
+ "Raw hardware event descriptor",
+ "Hardware breakpoint",
+};
+
+/*
+ * Print the events from <debugfs_mount_point>/tracing/events
+ */
+
+static void print_tracepoint_events(void)
+{
+ DIR *sys_dir, *evt_dir;
+ struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ char evt_path[MAXPATHLEN];
+ char dir_path[MAXPATHLEN];
+
+ if (debugfs_valid_mountpoint(debugfs_path))
+ return;
+
+ sys_dir = opendir(debugfs_path);
+ if (!sys_dir)
+ return;
+
+ for_each_subsystem(sys_dir, sys_dirent, sys_next) {
+
+ snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
+ sys_dirent.d_name);
+ evt_dir = opendir(dir_path);
+ if (!evt_dir)
+ continue;
+
+ for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
+ snprintf(evt_path, MAXPATHLEN, "%s:%s",
+ sys_dirent.d_name, evt_dirent.d_name);
+ printf(" %-42s [%s]\n", evt_path,
+ event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+ }
+ closedir(evt_dir);
+ }
+ closedir(sys_dir);
+}
+
+/*
+ * Check whether event is in <debugfs_mount_point>/tracing/events
+ */
+
+int is_valid_tracepoint(const char *event_string)
+{
+ DIR *sys_dir, *evt_dir;
+ struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ char evt_path[MAXPATHLEN];
+ char dir_path[MAXPATHLEN];
+
+ if (debugfs_valid_mountpoint(debugfs_path))
+ return 0;
+
+ sys_dir = opendir(debugfs_path);
+ if (!sys_dir)
+ return 0;
+
+ for_each_subsystem(sys_dir, sys_dirent, sys_next) {
+
+ snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
+ sys_dirent.d_name);
+ evt_dir = opendir(dir_path);
+ if (!evt_dir)
+ continue;
+
+ for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
+ snprintf(evt_path, MAXPATHLEN, "%s:%s",
+ sys_dirent.d_name, evt_dirent.d_name);
+ if (!strcmp(evt_path, event_string)) {
+ closedir(evt_dir);
+ closedir(sys_dir);
+ return 1;
+ }
+ }
+ closedir(evt_dir);
+ }
+ closedir(sys_dir);
+ return 0;
+}
+
+/*
+ * Print the help text for the event symbols:
+ */
+void print_events(void)
+{
+ struct event_symbol *syms = event_symbols;
+ unsigned int i, type, op, prev_type = -1;
+ char name[40];
+
+ printf("\n");
+ printf("List of pre-defined events (to be used in -e):\n");
+
+ for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
+ type = syms->type;
+
+ if (type != prev_type)
+ printf("\n");
+
+ if (strlen(syms->alias))
+ sprintf(name, "%s OR %s", syms->symbol, syms->alias);
+ else
+ strcpy(name, syms->symbol);
+ printf(" %-42s [%s]\n", name,
+ event_type_descriptors[type]);
+
+ prev_type = type;
+ }
+
+ printf("\n");
+ for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
+ for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
+ /* skip invalid cache type */
+ if (!is_cache_op_valid(type, op))
+ continue;
+
+ for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
+ printf(" %-42s [%s]\n",
+ event_cache_name(type, op, i),
+ event_type_descriptors[PERF_TYPE_HW_CACHE]);
+ }
+ }
+ }
+
+ printf("\n");
+ printf(" %-42s [%s]\n",
+ "rNNN (see 'perf list --help' on how to encode it)",
+ event_type_descriptors[PERF_TYPE_RAW]);
+ printf("\n");
+
+ printf(" %-42s [%s]\n",
+ "mem:<addr>[:access]",
+ event_type_descriptors[PERF_TYPE_BREAKPOINT]);
+ printf("\n");
+
+ print_tracepoint_events();
+
+ exit(129);
+}
+
+int perf_evsel_list__create_default(void)
+{
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.type = PERF_TYPE_HARDWARE;
+ attr.config = PERF_COUNT_HW_CPU_CYCLES;
+
+ evsel = perf_evsel__new(&attr, 0);
+
+ if (evsel == NULL)
+ return -ENOMEM;
+
+ list_add(&evsel->node, &evsel_list);
+ ++nr_counters;
+ return 0;
+}
+
+void perf_evsel_list__delete(void)
+{
+ struct perf_evsel *pos, *n;
+
+ list_for_each_entry_safe(pos, n, &evsel_list, node) {
+ list_del_init(&pos->node);
+ perf_evsel__delete(pos);
+ }
+ nr_counters = 0;
+}
diff --git a/smartt-perf/util/parse-events.h b/smartt-perf/util/parse-events.h
new file mode 100644
index 0000000..061c8c0
--- /dev/null
+++ b/smartt-perf/util/parse-events.h
@@ -0,0 +1,44 @@
+#ifndef __PERF_PARSE_EVENTS_H
+#define __PERF_PARSE_EVENTS_H
+/*
+ * Parse symbolic events/counts passed in as options:
+ */
+
+#include "linux/perf_event.h"
+
+struct list_head;
+struct perf_evsel;
+
+extern struct list_head evsel_list;
+
+int perf_evsel_list__create_default(void);
+void perf_evsel_list__delete(void);
+
+struct option;
+
+struct tracepoint_path {
+ char *system;
+ char *name;
+ struct tracepoint_path *next;
+};
+
+extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
+extern bool have_tracepoints(struct list_head *evlist);
+
+extern int nr_counters;
+
+const char *event_name(struct perf_evsel *event);
+extern const char *__event_name(int type, u64 config);
+
+extern int parse_events(const struct option *opt, const char *str, int unset);
+extern int parse_filter(const struct option *opt, const char *str, int unset);
+
+#define EVENTS_HELP_MAX (128*1024)
+
+extern void print_events(void);
+extern int is_valid_tracepoint(const char *event_string);
+
+extern char debugfs_path[];
+extern int valid_debugfs_mount(const char *debugfs);
+
+#endif /* __PERF_PARSE_EVENTS_H */
diff --git a/smartt-perf/util/parse-options.c b/smartt-perf/util/parse-options.c
new file mode 100644
index 0000000..99d02aa
--- /dev/null
+++ b/smartt-perf/util/parse-options.c
@@ -0,0 +1,577 @@
+#include "util.h"
+#include "parse-options.h"
+#include "cache.h"
+
+#define OPT_SHORT 1
+#define OPT_UNSET 2
+
+static int opterror(const struct option *opt, const char *reason, int flags)
+{
+ if (flags & OPT_SHORT)
+ return error("switch `%c' %s", opt->short_name, reason);
+ if (flags & OPT_UNSET)
+ return error("option `no-%s' %s", opt->long_name, reason);
+ return error("option `%s' %s", opt->long_name, reason);
+}
+
+static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
+ int flags, const char **arg)
+{
+ if (p->opt) {
+ *arg = p->opt;
+ p->opt = NULL;
+ } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
+ **(p->argv + 1) == '-')) {
+ *arg = (const char *)opt->defval;
+ } else if (p->argc > 1) {
+ p->argc--;
+ *arg = *++p->argv;
+ } else
+ return opterror(opt, "requires a value", flags);
+ return 0;
+}
+
+static int get_value(struct parse_opt_ctx_t *p,
+ const struct option *opt, int flags)
+{
+ const char *s, *arg = NULL;
+ const int unset = flags & OPT_UNSET;
+
+ if (unset && p->opt)
+ return opterror(opt, "takes no value", flags);
+ if (unset && (opt->flags & PARSE_OPT_NONEG))
+ return opterror(opt, "isn't available", flags);
+
+ if (!(flags & OPT_SHORT) && p->opt) {
+ switch (opt->type) {
+ case OPTION_CALLBACK:
+ if (!(opt->flags & PARSE_OPT_NOARG))
+ break;
+ /* FALLTHROUGH */
+ case OPTION_BOOLEAN:
+ case OPTION_INCR:
+ case OPTION_BIT:
+ case OPTION_SET_UINT:
+ case OPTION_SET_PTR:
+ return opterror(opt, "takes no value", flags);
+ case OPTION_END:
+ case OPTION_ARGUMENT:
+ case OPTION_GROUP:
+ case OPTION_STRING:
+ case OPTION_INTEGER:
+ case OPTION_UINTEGER:
+ case OPTION_LONG:
+ case OPTION_U64:
+ default:
+ break;
+ }
+ }
+
+ switch (opt->type) {
+ case OPTION_BIT:
+ if (unset)
+ *(int *)opt->value &= ~opt->defval;
+ else
+ *(int *)opt->value |= opt->defval;
+ return 0;
+
+ case OPTION_BOOLEAN:
+ *(bool *)opt->value = unset ? false : true;
+ return 0;
+
+ case OPTION_INCR:
+ *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
+ return 0;
+
+ case OPTION_SET_UINT:
+ *(unsigned int *)opt->value = unset ? 0 : opt->defval;
+ return 0;
+
+ case OPTION_SET_PTR:
+ *(void **)opt->value = unset ? NULL : (void *)opt->defval;
+ return 0;
+
+ case OPTION_STRING:
+ if (unset)
+ *(const char **)opt->value = NULL;
+ else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+ *(const char **)opt->value = (const char *)opt->defval;
+ else
+ return get_arg(p, opt, flags, (const char **)opt->value);
+ return 0;
+
+ case OPTION_CALLBACK:
+ if (unset)
+ return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
+ if (opt->flags & PARSE_OPT_NOARG)
+ return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+ return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
+
+ case OPTION_INTEGER:
+ if (unset) {
+ *(int *)opt->value = 0;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+ *(int *)opt->value = opt->defval;
+ return 0;
+ }
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ *(int *)opt->value = strtol(arg, (char **)&s, 10);
+ if (*s)
+ return opterror(opt, "expects a numerical value", flags);
+ return 0;
+
+ case OPTION_UINTEGER:
+ if (unset) {
+ *(unsigned int *)opt->value = 0;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+ *(unsigned int *)opt->value = opt->defval;
+ return 0;
+ }
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
+ if (*s)
+ return opterror(opt, "expects a numerical value", flags);
+ return 0;
+
+ case OPTION_LONG:
+ if (unset) {
+ *(long *)opt->value = 0;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+ *(long *)opt->value = opt->defval;
+ return 0;
+ }
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ *(long *)opt->value = strtol(arg, (char **)&s, 10);
+ if (*s)
+ return opterror(opt, "expects a numerical value", flags);
+ return 0;
+
+ case OPTION_U64:
+ if (unset) {
+ *(u64 *)opt->value = 0;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+ *(u64 *)opt->value = opt->defval;
+ return 0;
+ }
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ *(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
+ if (*s)
+ return opterror(opt, "expects a numerical value", flags);
+ return 0;
+
+ case OPTION_END:
+ case OPTION_ARGUMENT:
+ case OPTION_GROUP:
+ default:
+ die("should not happen, someone must be hit on the forehead");
+ }
+}
+
+static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
+{
+ for (; options->type != OPTION_END; options++) {
+ if (options->short_name == *p->opt) {
+ p->opt = p->opt[1] ? p->opt + 1 : NULL;
+ return get_value(p, options, OPT_SHORT);
+ }
+ }
+ return -2;
+}
+
+static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
+ const struct option *options)
+{
+ const char *arg_end = strchr(arg, '=');
+ const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
+ int abbrev_flags = 0, ambiguous_flags = 0;
+
+ if (!arg_end)
+ arg_end = arg + strlen(arg);
+
+ for (; options->type != OPTION_END; options++) {
+ const char *rest;
+ int flags = 0;
+
+ if (!options->long_name)
+ continue;
+
+ rest = skip_prefix(arg, options->long_name);
+ if (options->type == OPTION_ARGUMENT) {
+ if (!rest)
+ continue;
+ if (*rest == '=')
+ return opterror(options, "takes no value", flags);
+ if (*rest)
+ continue;
+ p->out[p->cpidx++] = arg - 2;
+ return 0;
+ }
+ if (!rest) {
+ /* abbreviated? */
+ if (!strncmp(options->long_name, arg, arg_end - arg)) {
+is_abbreviated:
+ if (abbrev_option) {
+ /*
+ * If this is abbreviated, it is
+ * ambiguous. So when there is no
+ * exact match later, we need to
+ * error out.
+ */
+ ambiguous_option = abbrev_option;
+ ambiguous_flags = abbrev_flags;
+ }
+ if (!(flags & OPT_UNSET) && *arg_end)
+ p->opt = arg_end + 1;
+ abbrev_option = options;
+ abbrev_flags = flags;
+ continue;
+ }
+ /* negated and abbreviated very much? */
+ if (!prefixcmp("no-", arg)) {
+ flags |= OPT_UNSET;
+ goto is_abbreviated;
+ }
+ /* negated? */
+ if (strncmp(arg, "no-", 3))
+ continue;
+ flags |= OPT_UNSET;
+ rest = skip_prefix(arg + 3, options->long_name);
+ /* abbreviated and negated? */
+ if (!rest && !prefixcmp(options->long_name, arg + 3))
+ goto is_abbreviated;
+ if (!rest)
+ continue;
+ }
+ if (*rest) {
+ if (*rest != '=')
+ continue;
+ p->opt = rest + 1;
+ }
+ return get_value(p, options, flags);
+ }
+
+ if (ambiguous_option)
+ return error("Ambiguous option: %s "
+ "(could be --%s%s or --%s%s)",
+ arg,
+ (ambiguous_flags & OPT_UNSET) ? "no-" : "",
+ ambiguous_option->long_name,
+ (abbrev_flags & OPT_UNSET) ? "no-" : "",
+ abbrev_option->long_name);
+ if (abbrev_option)
+ return get_value(p, abbrev_option, abbrev_flags);
+ return -2;
+}
+
+static void check_typos(const char *arg, const struct option *options)
+{
+ if (strlen(arg) < 3)
+ return;
+
+ if (!prefixcmp(arg, "no-")) {
+ error ("did you mean `--%s` (with two dashes ?)", arg);
+ exit(129);
+ }
+
+ for (; options->type != OPTION_END; options++) {
+ if (!options->long_name)
+ continue;
+ if (!prefixcmp(options->long_name, arg)) {
+ error ("did you mean `--%s` (with two dashes ?)", arg);
+ exit(129);
+ }
+ }
+}
+
+void parse_options_start(struct parse_opt_ctx_t *ctx,
+ int argc, const char **argv, int flags)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->argc = argc - 1;
+ ctx->argv = argv + 1;
+ ctx->out = argv;
+ ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
+ ctx->flags = flags;
+ if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
+ (flags & PARSE_OPT_STOP_AT_NON_OPTION))
+ die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
+}
+
+static int usage_with_options_internal(const char * const *,
+ const struct option *, int);
+
+int parse_options_step(struct parse_opt_ctx_t *ctx,
+ const struct option *options,
+ const char * const usagestr[])
+{
+ int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
+
+ /* we must reset ->opt, unknown short option leave it dangling */
+ ctx->opt = NULL;
+
+ for (; ctx->argc; ctx->argc--, ctx->argv++) {
+ const char *arg = ctx->argv[0];
+
+ if (*arg != '-' || !arg[1]) {
+ if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
+ break;
+ ctx->out[ctx->cpidx++] = ctx->argv[0];
+ continue;
+ }
+
+ if (arg[1] != '-') {
+ ctx->opt = arg + 1;
+ if (internal_help && *ctx->opt == 'h')
+ return parse_options_usage(usagestr, options);
+ switch (parse_short_opt(ctx, options)) {
+ case -1:
+ return parse_options_usage(usagestr, options);
+ case -2:
+ goto unknown;
+ default:
+ break;
+ }
+ if (ctx->opt)
+ check_typos(arg + 1, options);
+ while (ctx->opt) {
+ if (internal_help && *ctx->opt == 'h')
+ return parse_options_usage(usagestr, options);
+ switch (parse_short_opt(ctx, options)) {
+ case -1:
+ return parse_options_usage(usagestr, options);
+ case -2:
+ /* fake a short option thing to hide the fact that we may have
+ * started to parse aggregated stuff
+ *
+ * This is leaky, too bad.
+ */
+ ctx->argv[0] = strdup(ctx->opt - 1);
+ *(char *)ctx->argv[0] = '-';
+ goto unknown;
+ default:
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (!arg[2]) { /* "--" */
+ if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
+ ctx->argc--;
+ ctx->argv++;
+ }
+ break;
+ }
+
+ if (internal_help && !strcmp(arg + 2, "help-all"))
+ return usage_with_options_internal(usagestr, options, 1);
+ if (internal_help && !strcmp(arg + 2, "help"))
+ return parse_options_usage(usagestr, options);
+ switch (parse_long_opt(ctx, arg + 2, options)) {
+ case -1:
+ return parse_options_usage(usagestr, options);
+ case -2:
+ goto unknown;
+ default:
+ break;
+ }
+ continue;
+unknown:
+ if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
+ return PARSE_OPT_UNKNOWN;
+ ctx->out[ctx->cpidx++] = ctx->argv[0];
+ ctx->opt = NULL;
+ }
+ return PARSE_OPT_DONE;
+}
+
+int parse_options_end(struct parse_opt_ctx_t *ctx)
+{
+ memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
+ ctx->out[ctx->cpidx + ctx->argc] = NULL;
+ return ctx->cpidx + ctx->argc;
+}
+
+int parse_options(int argc, const char **argv, const struct option *options,
+ const char * const usagestr[], int flags)
+{
+ struct parse_opt_ctx_t ctx;
+
+ parse_options_start(&ctx, argc, argv, flags);
+ switch (parse_options_step(&ctx, options, usagestr)) {
+ case PARSE_OPT_HELP:
+ exit(129);
+ case PARSE_OPT_DONE:
+ break;
+ default: /* PARSE_OPT_UNKNOWN */
+ if (ctx.argv[0][1] == '-') {
+ error("unknown option `%s'", ctx.argv[0] + 2);
+ } else {
+ error("unknown switch `%c'", *ctx.opt);
+ }
+ usage_with_options(usagestr, options);
+ }
+
+ return parse_options_end(&ctx);
+}
+
+#define USAGE_OPTS_WIDTH 24
+#define USAGE_GAP 2
+
+int usage_with_options_internal(const char * const *usagestr,
+ const struct option *opts, int full)
+{
+ if (!usagestr)
+ return PARSE_OPT_HELP;
+
+ fprintf(stderr, "\n usage: %s\n", *usagestr++);
+ while (*usagestr && **usagestr)
+ fprintf(stderr, " or: %s\n", *usagestr++);
+ while (*usagestr) {
+ fprintf(stderr, "%s%s\n",
+ **usagestr ? " " : "",
+ *usagestr);
+ usagestr++;
+ }
+
+ if (opts->type != OPTION_GROUP)
+ fputc('\n', stderr);
+
+ for (; opts->type != OPTION_END; opts++) {
+ size_t pos;
+ int pad;
+
+ if (opts->type == OPTION_GROUP) {
+ fputc('\n', stderr);
+ if (*opts->help)
+ fprintf(stderr, "%s\n", opts->help);
+ continue;
+ }
+ if (!full && (opts->flags & PARSE_OPT_HIDDEN))
+ continue;
+
+ pos = fprintf(stderr, " ");
+ if (opts->short_name)
+ pos += fprintf(stderr, "-%c", opts->short_name);
+ else
+ pos += fprintf(stderr, " ");
+
+ if (opts->long_name && opts->short_name)
+ pos += fprintf(stderr, ", ");
+ if (opts->long_name)
+ pos += fprintf(stderr, "--%s", opts->long_name);
+
+ switch (opts->type) {
+ case OPTION_ARGUMENT:
+ break;
+ case OPTION_LONG:
+ case OPTION_U64:
+ case OPTION_INTEGER:
+ case OPTION_UINTEGER:
+ if (opts->flags & PARSE_OPT_OPTARG)
+ if (opts->long_name)
+ pos += fprintf(stderr, "[=<n>]");
+ else
+ pos += fprintf(stderr, "[<n>]");
+ else
+ pos += fprintf(stderr, " <n>");
+ break;
+ case OPTION_CALLBACK:
+ if (opts->flags & PARSE_OPT_NOARG)
+ break;
+ /* FALLTHROUGH */
+ case OPTION_STRING:
+ if (opts->argh) {
+ if (opts->flags & PARSE_OPT_OPTARG)
+ if (opts->long_name)
+ pos += fprintf(stderr, "[=<%s>]", opts->argh);
+ else
+ pos += fprintf(stderr, "[<%s>]", opts->argh);
+ else
+ pos += fprintf(stderr, " <%s>", opts->argh);
+ } else {
+ if (opts->flags & PARSE_OPT_OPTARG)
+ if (opts->long_name)
+ pos += fprintf(stderr, "[=...]");
+ else
+ pos += fprintf(stderr, "[...]");
+ else
+ pos += fprintf(stderr, " ...");
+ }
+ break;
+ default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
+ case OPTION_END:
+ case OPTION_GROUP:
+ case OPTION_BIT:
+ case OPTION_BOOLEAN:
+ case OPTION_INCR:
+ case OPTION_SET_UINT:
+ case OPTION_SET_PTR:
+ break;
+ }
+
+ if (pos <= USAGE_OPTS_WIDTH)
+ pad = USAGE_OPTS_WIDTH - pos;
+ else {
+ fputc('\n', stderr);
+ pad = USAGE_OPTS_WIDTH;
+ }
+ fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+ }
+ fputc('\n', stderr);
+
+ return PARSE_OPT_HELP;
+}
+
+void usage_with_options(const char * const *usagestr,
+ const struct option *opts)
+{
+ exit_browser(false);
+ usage_with_options_internal(usagestr, opts, 0);
+ exit(129);
+}
+
+int parse_options_usage(const char * const *usagestr,
+ const struct option *opts)
+{
+ return usage_with_options_internal(usagestr, opts, 0);
+}
+
+
+int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used,
+ int unset)
+{
+ int *target = opt->value;
+
+ if (unset)
+ /* --no-quiet, --no-verbose */
+ *target = 0;
+ else if (opt->short_name == 'v') {
+ if (*target >= 0)
+ (*target)++;
+ else
+ *target = 1;
+ } else {
+ if (*target <= 0)
+ (*target)--;
+ else
+ *target = -1;
+ }
+ return 0;
+}
diff --git a/smartt-perf/util/parse-options.h b/smartt-perf/util/parse-options.h
new file mode 100644
index 0000000..abc31a1
--- /dev/null
+++ b/smartt-perf/util/parse-options.h
@@ -0,0 +1,192 @@
+#ifndef __PERF_PARSE_OPTIONS_H
+#define __PERF_PARSE_OPTIONS_H
+
+#include <linux/kernel.h>
+#include <stdbool.h>
+
+enum parse_opt_type {
+ /* special types */
+ OPTION_END,
+ OPTION_ARGUMENT,
+ OPTION_GROUP,
+ /* options with no arguments */
+ OPTION_BIT,
+ OPTION_BOOLEAN,
+ OPTION_INCR,
+ OPTION_SET_UINT,
+ OPTION_SET_PTR,
+ /* options with arguments (usually) */
+ OPTION_STRING,
+ OPTION_INTEGER,
+ OPTION_LONG,
+ OPTION_CALLBACK,
+ OPTION_U64,
+ OPTION_UINTEGER,
+};
+
+enum parse_opt_flags {
+ PARSE_OPT_KEEP_DASHDASH = 1,
+ PARSE_OPT_STOP_AT_NON_OPTION = 2,
+ PARSE_OPT_KEEP_ARGV0 = 4,
+ PARSE_OPT_KEEP_UNKNOWN = 8,
+ PARSE_OPT_NO_INTERNAL_HELP = 16,
+};
+
+enum parse_opt_option_flags {
+ PARSE_OPT_OPTARG = 1,
+ PARSE_OPT_NOARG = 2,
+ PARSE_OPT_NONEG = 4,
+ PARSE_OPT_HIDDEN = 8,
+ PARSE_OPT_LASTARG_DEFAULT = 16,
+};
+
+struct option;
+typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
+
+/*
+ * `type`::
+ * holds the type of the option, you must have an OPTION_END last in your
+ * array.
+ *
+ * `short_name`::
+ * the character to use as a short option name, '\0' if none.
+ *
+ * `long_name`::
+ * the long option name, without the leading dashes, NULL if none.
+ *
+ * `value`::
+ * stores pointers to the values to be filled.
+ *
+ * `argh`::
+ * token to explain the kind of argument this option wants. Keep it
+ * homogenous across the repository.
+ *
+ * `help`::
+ * the short help associated to what the option does.
+ * Must never be NULL (except for OPTION_END).
+ * OPTION_GROUP uses this pointer to store the group header.
+ *
+ * `flags`::
+ * mask of parse_opt_option_flags.
+ * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs)
+ * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
+ * PARSE_OPT_NONEG: says that this option cannot be negated
+ * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in
+ * the long one.
+ *
+ * `callback`::
+ * pointer to the callback to use for OPTION_CALLBACK.
+ *
+ * `defval`::
+ * default value to fill (*->value) with for PARSE_OPT_OPTARG.
+ * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in
+ * the value when met.
+ * CALLBACKS can use it like they want.
+ */
+struct option {
+ enum parse_opt_type type;
+ int short_name;
+ const char *long_name;
+ void *value;
+ const char *argh;
+ const char *help;
+
+ int flags;
+ parse_opt_cb *callback;
+ intptr_t defval;
+};
+
+#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v )
+
+#define OPT_END() { .type = OPTION_END }
+#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) }
+#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
+#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
+#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
+#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
+#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }
+#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) }
+#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
+#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) }
+#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) }
+#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) }
+#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) }
+#define OPT_DATE(s, l, v, h) \
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
+#define OPT_CALLBACK(s, l, v, a, h, f) \
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) }
+#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG }
+#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT }
+#define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\
+ .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\
+ .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG}
+
+/* parse_options() will filter out the processed options and leave the
+ * non-option argments in argv[].
+ * Returns the number of arguments left in argv[].
+ */
+extern int parse_options(int argc, const char **argv,
+ const struct option *options,
+ const char * const usagestr[], int flags);
+
+extern NORETURN void usage_with_options(const char * const *usagestr,
+ const struct option *options);
+
+/*----- incremantal advanced APIs -----*/
+
+enum {
+ PARSE_OPT_HELP = -1,
+ PARSE_OPT_DONE,
+ PARSE_OPT_UNKNOWN,
+};
+
+/*
+ * It's okay for the caller to consume argv/argc in the usual way.
+ * Other fields of that structure are private to parse-options and should not
+ * be modified in any way.
+ */
+struct parse_opt_ctx_t {
+ const char **argv;
+ const char **out;
+ int argc, cpidx;
+ const char *opt;
+ int flags;
+};
+
+extern int parse_options_usage(const char * const *usagestr,
+ const struct option *opts);
+
+extern void parse_options_start(struct parse_opt_ctx_t *ctx,
+ int argc, const char **argv, int flags);
+
+extern int parse_options_step(struct parse_opt_ctx_t *ctx,
+ const struct option *options,
+ const char * const usagestr[]);
+
+extern int parse_options_end(struct parse_opt_ctx_t *ctx);
+
+
+/*----- some often used options -----*/
+extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
+extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
+
+#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
+#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")
+#define OPT__VERBOSITY(var) \
+ { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
+ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
+ { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
+ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
+#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run")
+#define OPT__ABBREV(var) \
+ { OPTION_CALLBACK, 0, "abbrev", (var), "n", \
+ "use <n> digits to display SHA-1s", \
+ PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+
+extern const char *parse_options_fix_filename(const char *prefix, const char *file);
+
+#endif /* __PERF_PARSE_OPTIONS_H */
diff --git a/smartt-perf/util/path.c b/smartt-perf/util/path.c
new file mode 100644
index 0000000..bd74977
--- /dev/null
+++ b/smartt-perf/util/path.c
@@ -0,0 +1,157 @@
+/*
+ * I'm tired of doing "vsnprintf()" etc just to open a
+ * file, so here's a "return static buffer with printf"
+ * interface for paths.
+ *
+ * It's obviously not thread-safe. Sue me. But it's quite
+ * useful for doing things like
+ *
+ * f = open(mkpath("%s/%s.perf", base, name), O_RDONLY);
+ *
+ * which is what it's designed for.
+ */
+#include "cache.h"
+
+static char bad_path[] = "/bad-path/";
+/*
+ * Two hacks:
+ */
+
+static const char *get_perf_dir(void)
+{
+ return ".";
+}
+
+#ifdef NO_STRLCPY
+size_t strlcpy(char *dest, const char *src, size_t size)
+{
+ size_t ret = strlen(src);
+
+ if (size) {
+ size_t len = (ret >= size) ? size - 1 : ret;
+ memcpy(dest, src, len);
+ dest[len] = '\0';
+ }
+ return ret;
+}
+#endif
+
+static char *get_pathname(void)
+{
+ static char pathname_array[4][PATH_MAX];
+ static int idx;
+
+ return pathname_array[3 & ++idx];
+}
+
+static char *cleanup_path(char *path)
+{
+ /* Clean it up */
+ if (!memcmp(path, "./", 2)) {
+ path += 2;
+ while (*path == '/')
+ path++;
+ }
+ return path;
+}
+
+static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+{
+ const char *perf_dir = get_perf_dir();
+ size_t len;
+
+ len = strlen(perf_dir);
+ if (n < len + 1)
+ goto bad;
+ memcpy(buf, perf_dir, len);
+ if (len && !is_dir_sep(perf_dir[len-1]))
+ buf[len++] = '/';
+ len += vsnprintf(buf + len, n - len, fmt, args);
+ if (len >= n)
+ goto bad;
+ return cleanup_path(buf);
+bad:
+ strlcpy(buf, bad_path, n);
+ return buf;
+}
+
+char *perf_pathdup(const char *fmt, ...)
+{
+ char path[PATH_MAX];
+ va_list args;
+ va_start(args, fmt);
+ (void)perf_vsnpath(path, sizeof(path), fmt, args);
+ va_end(args);
+ return xstrdup(path);
+}
+
+char *mkpath(const char *fmt, ...)
+{
+ va_list args;
+ unsigned len;
+ char *pathname = get_pathname();
+
+ va_start(args, fmt);
+ len = vsnprintf(pathname, PATH_MAX, fmt, args);
+ va_end(args);
+ if (len >= PATH_MAX)
+ return bad_path;
+ return cleanup_path(pathname);
+}
+
+char *perf_path(const char *fmt, ...)
+{
+ const char *perf_dir = get_perf_dir();
+ char *pathname = get_pathname();
+ va_list args;
+ unsigned len;
+
+ len = strlen(perf_dir);
+ if (len > PATH_MAX-100)
+ return bad_path;
+ memcpy(pathname, perf_dir, len);
+ if (len && perf_dir[len-1] != '/')
+ pathname[len++] = '/';
+ va_start(args, fmt);
+ len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+ va_end(args);
+ if (len >= PATH_MAX)
+ return bad_path;
+ return cleanup_path(pathname);
+}
+
+/* strip arbitrary amount of directory separators at end of path */
+static inline int chomp_trailing_dir_sep(const char *path, int len)
+{
+ while (len && is_dir_sep(path[len - 1]))
+ len--;
+ return len;
+}
+
+/*
+ * If path ends with suffix (complete path components), returns the
+ * part before suffix (sans trailing directory separators).
+ * Otherwise returns NULL.
+ */
+char *strip_path_suffix(const char *path, const char *suffix)
+{
+ int path_len = strlen(path), suffix_len = strlen(suffix);
+
+ while (suffix_len) {
+ if (!path_len)
+ return NULL;
+
+ if (is_dir_sep(path[path_len - 1])) {
+ if (!is_dir_sep(suffix[suffix_len - 1]))
+ return NULL;
+ path_len = chomp_trailing_dir_sep(path, path_len);
+ suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
+ }
+ else if (path[--path_len] != suffix[--suffix_len])
+ return NULL;
+ }
+
+ if (path_len && !is_dir_sep(path[path_len - 1]))
+ return NULL;
+ return strndup(path, chomp_trailing_dir_sep(path, path_len));
+}
diff --git a/smartt-perf/util/probe-event.c b/smartt-perf/util/probe-event.c
new file mode 100644
index 0000000..6e29d9c
--- /dev/null
+++ b/smartt-perf/util/probe-event.c
@@ -0,0 +1,1915 @@
+/*
+ * probe-event.c : perf-probe definition to probe_events format converter
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#undef _GNU_SOURCE
+#include "util.h"
+#include "event.h"
+#include "string.h"
+#include "strlist.h"
+#include "debug.h"
+#include "cache.h"
+#include "color.h"
+#include "symbol.h"
+#include "thread.h"
+#include "debugfs.h"
+#include "trace-event.h" /* For __unused */
+#include "probe-event.h"
+#include "probe-finder.h"
+
+#define MAX_CMDLEN 256
+#define MAX_PROBE_ARGS 128
+#define PERFPROBE_GROUP "probe"
+
+bool probe_event_dry_run; /* Dry run flag */
+
+#define semantic_error(msg ...) pr_err("Semantic error :" msg)
+
+/* If there is no space to write, returns -E2BIG. */
+static int e_snprintf(char *str, size_t size, const char *format, ...)
+ __attribute__((format(printf, 3, 4)));
+
+static int e_snprintf(char *str, size_t size, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+ va_start(ap, format);
+ ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ if (ret >= (int)size)
+ ret = -E2BIG;
+ return ret;
+}
+
+static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
+static struct machine machine;
+
+/* Initialize symbol maps and path of vmlinux/modules */
+static int init_vmlinux(void)
+{
+ int ret;
+
+ symbol_conf.sort_by_name = true;
+ if (symbol_conf.vmlinux_name == NULL)
+ symbol_conf.try_vmlinux_path = true;
+ else
+ pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
+ ret = symbol__init();
+ if (ret < 0) {
+ pr_debug("Failed to init symbol map.\n");
+ goto out;
+ }
+
+ ret = machine__init(&machine, "", HOST_KERNEL_ID);
+ if (ret < 0)
+ goto out;
+
+ if (machine__create_kernel_maps(&machine) < 0) {
+ pr_debug("machine__create_kernel_maps() failed.\n");
+ goto out;
+ }
+out:
+ if (ret < 0)
+ pr_warning("Failed to init vmlinux path.\n");
+ return ret;
+}
+
+static struct symbol *__find_kernel_function_by_name(const char *name,
+ struct map **mapp)
+{
+ return machine__find_kernel_function_by_name(&machine, name, mapp,
+ NULL);
+}
+
+const char *kernel_get_module_path(const char *module)
+{
+ struct dso *dso;
+ struct map *map;
+ const char *vmlinux_name;
+
+ if (module) {
+ list_for_each_entry(dso, &machine.kernel_dsos, node) {
+ if (strncmp(dso->short_name + 1, module,
+ dso->short_name_len - 2) == 0)
+ goto found;
+ }
+ pr_debug("Failed to find module %s.\n", module);
+ return NULL;
+ }
+
+ map = machine.vmlinux_maps[MAP__FUNCTION];
+ dso = map->dso;
+
+ vmlinux_name = symbol_conf.vmlinux_name;
+ if (vmlinux_name) {
+ if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0)
+ return NULL;
+ } else {
+ if (dso__load_vmlinux_path(dso, map, NULL) <= 0) {
+ pr_debug("Failed to load kernel map.\n");
+ return NULL;
+ }
+ }
+found:
+ return dso->long_name;
+}
+
+#ifdef DWARF_SUPPORT
+static int open_vmlinux(const char *module)
+{
+ const char *path = kernel_get_module_path(module);
+ if (!path) {
+ pr_err("Failed to find path of %s module.\n",
+ module ?: "kernel");
+ return -ENOENT;
+ }
+ pr_debug("Try to open %s\n", path);
+ return open(path, O_RDONLY);
+}
+
+/*
+ * Convert trace point to probe point with debuginfo
+ * Currently only handles kprobes.
+ */
+static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
+ struct perf_probe_point *pp)
+{
+ struct symbol *sym;
+ struct map *map;
+ u64 addr;
+ int ret = -ENOENT;
+
+ sym = __find_kernel_function_by_name(tp->symbol, &map);
+ if (sym) {
+ addr = map->unmap_ip(map, sym->start + tp->offset);
+ pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol,
+ tp->offset, addr);
+ ret = find_perf_probe_point((unsigned long)addr, pp);
+ }
+ if (ret <= 0) {
+ pr_debug("Failed to find corresponding probes from "
+ "debuginfo. Use kprobe event information.\n");
+ pp->function = strdup(tp->symbol);
+ if (pp->function == NULL)
+ return -ENOMEM;
+ pp->offset = tp->offset;
+ }
+ pp->retprobe = tp->retprobe;
+
+ return 0;
+}
+
+/* Try to find perf_probe_event with debuginfo */
+static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
+ struct probe_trace_event **tevs,
+ int max_tevs, const char *module)
+{
+ bool need_dwarf = perf_probe_event_need_dwarf(pev);
+ int fd, ntevs;
+
+ fd = open_vmlinux(module);
+ if (fd < 0) {
+ if (need_dwarf) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
+ pr_debug("Could not open vmlinux. Try to use symbols.\n");
+ return 0;
+ }
+
+ /* Searching trace events corresponding to probe event */
+ ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs);
+ close(fd);
+
+ if (ntevs > 0) { /* Succeeded to find trace events */
+ pr_debug("find %d probe_trace_events.\n", ntevs);
+ return ntevs;
+ }
+
+ if (ntevs == 0) { /* No error but failed to find probe point. */
+ pr_warning("Probe point '%s' not found.\n",
+ synthesize_perf_probe_point(&pev->point));
+ return -ENOENT;
+ }
+ /* Error path : ntevs < 0 */
+ pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs);
+ if (ntevs == -EBADF) {
+ pr_warning("Warning: No dwarf info found in the vmlinux - "
+ "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n");
+ if (!need_dwarf) {
+ pr_debug("Trying to use symbols.\n");
+ return 0;
+ }
+ }
+ return ntevs;
+}
+
+/*
+ * Find a src file from a DWARF tag path. Prepend optional source path prefix
+ * and chop off leading directories that do not exist. Result is passed back as
+ * a newly allocated path on success.
+ * Return 0 if file was found and readable, -errno otherwise.
+ */
+static int get_real_path(const char *raw_path, const char *comp_dir,
+ char **new_path)
+{
+ const char *prefix = symbol_conf.source_prefix;
+
+ if (!prefix) {
+ if (raw_path[0] != '/' && comp_dir)
+ /* If not an absolute path, try to use comp_dir */
+ prefix = comp_dir;
+ else {
+ if (access(raw_path, R_OK) == 0) {
+ *new_path = strdup(raw_path);
+ return 0;
+ } else
+ return -errno;
+ }
+ }
+
+ *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2));
+ if (!*new_path)
+ return -ENOMEM;
+
+ for (;;) {
+ sprintf(*new_path, "%s/%s", prefix, raw_path);
+
+ if (access(*new_path, R_OK) == 0)
+ return 0;
+
+ if (!symbol_conf.source_prefix)
+ /* In case of searching comp_dir, don't retry */
+ return -errno;
+
+ switch (errno) {
+ case ENAMETOOLONG:
+ case ENOENT:
+ case EROFS:
+ case EFAULT:
+ raw_path = strchr(++raw_path, '/');
+ if (!raw_path) {
+ free(*new_path);
+ *new_path = NULL;
+ return -ENOENT;
+ }
+ continue;
+
+ default:
+ free(*new_path);
+ *new_path = NULL;
+ return -errno;
+ }
+ }
+}
+
+#define LINEBUF_SIZE 256
+#define NR_ADDITIONAL_LINES 2
+
+static int __show_one_line(FILE *fp, int l, bool skip, bool show_num)
+{
+ char buf[LINEBUF_SIZE];
+ const char *color = show_num ? "" : PERF_COLOR_BLUE;
+ const char *prefix = NULL;
+
+ do {
+ if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+ goto error;
+ if (skip)
+ continue;
+ if (!prefix) {
+ prefix = show_num ? "%7d " : " ";
+ color_fprintf(stdout, color, prefix, l);
+ }
+ color_fprintf(stdout, color, "%s", buf);
+
+ } while (strchr(buf, '\n') == NULL);
+
+ return 1;
+error:
+ if (ferror(fp)) {
+ pr_warning("File read error: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)
+{
+ int rv = __show_one_line(fp, l, skip, show_num);
+ if (rv == 0) {
+ pr_warning("Source file is shorter than expected.\n");
+ rv = -1;
+ }
+ return rv;
+}
+
+#define show_one_line_with_num(f,l) _show_one_line(f,l,false,true)
+#define show_one_line(f,l) _show_one_line(f,l,false,false)
+#define skip_one_line(f,l) _show_one_line(f,l,true,false)
+#define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false)
+
+/*
+ * Show line-range always requires debuginfo to find source file and
+ * line number.
+ */
+int show_line_range(struct line_range *lr, const char *module)
+{
+ int l = 1;
+ struct line_node *ln;
+ FILE *fp;
+ int fd, ret;
+ char *tmp;
+
+ /* Search a line range */
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;
+
+ fd = open_vmlinux(module);
+ if (fd < 0) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
+
+ ret = find_line_range(fd, lr);
+ close(fd);
+ if (ret == 0) {
+ pr_warning("Specified source line is not found.\n");
+ return -ENOENT;
+ } else if (ret < 0) {
+ pr_warning("Debuginfo analysis failed. (%d)\n", ret);
+ return ret;
+ }
+
+ /* Convert source file path */
+ tmp = lr->path;
+ ret = get_real_path(tmp, lr->comp_dir, &lr->path);
+ free(tmp); /* Free old path */
+ if (ret < 0) {
+ pr_warning("Failed to find source file. (%d)\n", ret);
+ return ret;
+ }
+
+ setup_pager();
+
+ if (lr->function)
+ fprintf(stdout, "<%s:%d>\n", lr->function,
+ lr->start - lr->offset);
+ else
+ fprintf(stdout, "<%s:%d>\n", lr->path, lr->start);
+
+ fp = fopen(lr->path, "r");
+ if (fp == NULL) {
+ pr_warning("Failed to open %s: %s\n", lr->path,
+ strerror(errno));
+ return -errno;
+ }
+ /* Skip to starting line number */
+ while (l < lr->start) {
+ ret = skip_one_line(fp, l++);
+ if (ret < 0)
+ goto end;
+ }
+
+ list_for_each_entry(ln, &lr->line_list, list) {
+ for (; ln->line > l; l++) {
+ ret = show_one_line(fp, l - lr->offset);
+ if (ret < 0)
+ goto end;
+ }
+ ret = show_one_line_with_num(fp, l++ - lr->offset);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (lr->end == INT_MAX)
+ lr->end = l + NR_ADDITIONAL_LINES;
+ while (l <= lr->end) {
+ ret = show_one_line_or_eof(fp, l++ - lr->offset);
+ if (ret <= 0)
+ break;
+ }
+end:
+ fclose(fp);
+ return ret;
+}
+
+static int show_available_vars_at(int fd, struct perf_probe_event *pev,
+ int max_vls, bool externs)
+{
+ char *buf;
+ int ret, i;
+ struct str_node *node;
+ struct variable_list *vls = NULL, *vl;
+
+ buf = synthesize_perf_probe_point(&pev->point);
+ if (!buf)
+ return -EINVAL;
+ pr_debug("Searching variables at %s\n", buf);
+
+ ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
+ if (ret > 0) {
+ /* Some variables were found */
+ fprintf(stdout, "Available variables at %s\n", buf);
+ for (i = 0; i < ret; i++) {
+ vl = &vls[i];
+ /*
+ * A probe point might be converted to
+ * several trace points.
+ */
+ fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
+ vl->point.offset);
+ free(vl->point.symbol);
+ if (vl->vars) {
+ strlist__for_each(node, vl->vars)
+ fprintf(stdout, "\t\t%s\n", node->s);
+ strlist__delete(vl->vars);
+ } else
+ fprintf(stdout, "(No variables)\n");
+ }
+ free(vls);
+ } else
+ pr_err("Failed to find variables at %s (%d)\n", buf, ret);
+
+ free(buf);
+ return ret;
+}
+
+/* Show available variables on given probe point */
+int show_available_vars(struct perf_probe_event *pevs, int npevs,
+ int max_vls, const char *module, bool externs)
+{
+ int i, fd, ret = 0;
+
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;
+
+ fd = open_vmlinux(module);
+ if (fd < 0) {
+ pr_warning("Failed to open debug information file.\n");
+ return fd;
+ }
+
+ setup_pager();
+
+ for (i = 0; i < npevs && ret >= 0; i++)
+ ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
+
+ close(fd);
+ return ret;
+}
+
+#else /* !DWARF_SUPPORT */
+
+static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
+ struct perf_probe_point *pp)
+{
+ struct symbol *sym;
+
+ sym = __find_kernel_function_by_name(tp->symbol, NULL);
+ if (!sym) {
+ pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
+ return -ENOENT;
+ }
+ pp->function = strdup(tp->symbol);
+ if (pp->function == NULL)
+ return -ENOMEM;
+ pp->offset = tp->offset;
+ pp->retprobe = tp->retprobe;
+
+ return 0;
+}
+
+static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
+ struct probe_trace_event **tevs __unused,
+ int max_tevs __unused, const char *mod __unused)
+{
+ if (perf_probe_event_need_dwarf(pev)) {
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
+ }
+ return 0;
+}
+
+int show_line_range(struct line_range *lr __unused, const char *module __unused)
+{
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
+}
+
+int show_available_vars(struct perf_probe_event *pevs __unused,
+ int npevs __unused, int max_vls __unused,
+ const char *module __unused, bool externs __unused)
+{
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
+}
+#endif
+
+static int parse_line_num(char **ptr, int *val, const char *what)
+{
+ const char *start = *ptr;
+
+ errno = 0;
+ *val = strtol(*ptr, ptr, 0);
+ if (errno || *ptr == start) {
+ semantic_error("'%s' is not a valid number.\n", what);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Stuff 'lr' according to the line range described by 'arg'.
+ * The line range syntax is described by:
+ *
+ * SRC[:SLN[+NUM|-ELN]]
+ * FNC[:SLN[+NUM|-ELN]]
+ */
+int parse_line_range_desc(const char *arg, struct line_range *lr)
+{
+ char *range, *name = strdup(arg);
+ int err;
+
+ if (!name)
+ return -ENOMEM;
+
+ lr->start = 0;
+ lr->end = INT_MAX;
+
+ range = strchr(name, ':');
+ if (range) {
+ *range++ = '\0';
+
+ err = parse_line_num(&range, &lr->start, "start line");
+ if (err)
+ goto err;
+
+ if (*range == '+' || *range == '-') {
+ const char c = *range++;
+
+ err = parse_line_num(&range, &lr->end, "end line");
+ if (err)
+ goto err;
+
+ if (c == '+') {
+ lr->end += lr->start;
+ /*
+ * Adjust the number of lines here.
+ * If the number of lines == 1, the
+ * the end of line should be equal to
+ * the start of line.
+ */
+ lr->end--;
+ }
+ }
+
+ pr_debug("Line range is %d to %d\n", lr->start, lr->end);
+
+ err = -EINVAL;
+ if (lr->start > lr->end) {
+ semantic_error("Start line must be smaller"
+ " than end line.\n");
+ goto err;
+ }
+ if (*range != '\0') {
+ semantic_error("Tailing with invalid str '%s'.\n", range);
+ goto err;
+ }
+ }
+
+ if (strchr(name, '.'))
+ lr->file = name;
+ else
+ lr->function = name;
+
+ return 0;
+err:
+ free(name);
+ return err;
+}
+
+/* Check the name is good for event/group */
+static bool check_event_name(const char *name)
+{
+ if (!isalpha(*name) && *name != '_')
+ return false;
+ while (*++name != '\0') {
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+ return false;
+ }
+ return true;
+}
+
+/* Parse probepoint definition. */
+static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
+{
+ struct perf_probe_point *pp = &pev->point;
+ char *ptr, *tmp;
+ char c, nc = 0;
+ /*
+ * <Syntax>
+ * perf probe [EVENT=]SRC[:LN|;PTN]
+ * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
+ *
+ * TODO:Group name support
+ */
+
+ ptr = strpbrk(arg, ";=@+%");
+ if (ptr && *ptr == '=') { /* Event name */
+ *ptr = '\0';
+ tmp = ptr + 1;
+ if (strchr(arg, ':')) {
+ semantic_error("Group name is not supported yet.\n");
+ return -ENOTSUP;
+ }
+ if (!check_event_name(arg)) {
+ semantic_error("%s is bad for event name -it must "
+ "follow C symbol-naming rule.\n", arg);
+ return -EINVAL;
+ }
+ pev->event = strdup(arg);
+ if (pev->event == NULL)
+ return -ENOMEM;
+ pev->group = NULL;
+ arg = tmp;
+ }
+
+ ptr = strpbrk(arg, ";:+@%");
+ if (ptr) {
+ nc = *ptr;
+ *ptr++ = '\0';
+ }
+
+ tmp = strdup(arg);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ /* Check arg is function or file and copy it */
+ if (strchr(tmp, '.')) /* File */
+ pp->file = tmp;
+ else /* Function */
+ pp->function = tmp;
+
+ /* Parse other options */
+ while (ptr) {
+ arg = ptr;
+ c = nc;
+ if (c == ';') { /* Lazy pattern must be the last part */
+ pp->lazy_line = strdup(arg);
+ if (pp->lazy_line == NULL)
+ return -ENOMEM;
+ break;
+ }
+ ptr = strpbrk(arg, ";:+@%");
+ if (ptr) {
+ nc = *ptr;
+ *ptr++ = '\0';
+ }
+ switch (c) {
+ case ':': /* Line number */
+ pp->line = strtoul(arg, &tmp, 0);
+ if (*tmp != '\0') {
+ semantic_error("There is non-digit char"
+ " in line number.\n");
+ return -EINVAL;
+ }
+ break;
+ case '+': /* Byte offset from a symbol */
+ pp->offset = strtoul(arg, &tmp, 0);
+ if (*tmp != '\0') {
+ semantic_error("There is non-digit character"
+ " in offset.\n");
+ return -EINVAL;
+ }
+ break;
+ case '@': /* File name */
+ if (pp->file) {
+ semantic_error("SRC@SRC is not allowed.\n");
+ return -EINVAL;
+ }
+ pp->file = strdup(arg);
+ if (pp->file == NULL)
+ return -ENOMEM;
+ break;
+ case '%': /* Probe places */
+ if (strcmp(arg, "return") == 0) {
+ pp->retprobe = 1;
+ } else { /* Others not supported yet */
+ semantic_error("%%%s is not supported.\n", arg);
+ return -ENOTSUP;
+ }
+ break;
+ default: /* Buggy case */
+ pr_err("This program has a bug at %s:%d.\n",
+ __FILE__, __LINE__);
+ return -ENOTSUP;
+ break;
+ }
+ }
+
+ /* Exclusion check */
+ if (pp->lazy_line && pp->line) {
+ semantic_error("Lazy pattern can't be used with"
+ " line number.\n");
+ return -EINVAL;
+ }
+
+ if (pp->lazy_line && pp->offset) {
+ semantic_error("Lazy pattern can't be used with offset.\n");
+ return -EINVAL;
+ }
+
+ if (pp->line && pp->offset) {
+ semantic_error("Offset can't be used with line number.\n");
+ return -EINVAL;
+ }
+
+ if (!pp->line && !pp->lazy_line && pp->file && !pp->function) {
+ semantic_error("File always requires line number or "
+ "lazy pattern.\n");
+ return -EINVAL;
+ }
+
+ if (pp->offset && !pp->function) {
+ semantic_error("Offset requires an entry function.\n");
+ return -EINVAL;
+ }
+
+ if (pp->retprobe && !pp->function) {
+ semantic_error("Return probe requires an entry function.\n");
+ return -EINVAL;
+ }
+
+ if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {
+ semantic_error("Offset/Line/Lazy pattern can't be used with "
+ "return probe.\n");
+ return -EINVAL;
+ }
+
+ pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n",
+ pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
+ pp->lazy_line);
+ return 0;
+}
+
+/* Parse perf-probe event argument */
+static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
+{
+ char *tmp, *goodname;
+ struct perf_probe_arg_field **fieldp;
+
+ pr_debug("parsing arg: %s into ", str);
+
+ tmp = strchr(str, '=');
+ if (tmp) {
+ arg->name = strndup(str, tmp - str);
+ if (arg->name == NULL)
+ return -ENOMEM;
+ pr_debug("name:%s ", arg->name);
+ str = tmp + 1;
+ }
+
+ tmp = strchr(str, ':');
+ if (tmp) { /* Type setting */
+ *tmp = '\0';
+ arg->type = strdup(tmp + 1);
+ if (arg->type == NULL)
+ return -ENOMEM;
+ pr_debug("type:%s ", arg->type);
+ }
+
+ tmp = strpbrk(str, "-.[");
+ if (!is_c_varname(str) || !tmp) {
+ /* A variable, register, symbol or special value */
+ arg->var = strdup(str);
+ if (arg->var == NULL)
+ return -ENOMEM;
+ pr_debug("%s\n", arg->var);
+ return 0;
+ }
+
+ /* Structure fields or array element */
+ arg->var = strndup(str, tmp - str);
+ if (arg->var == NULL)
+ return -ENOMEM;
+ goodname = arg->var;
+ pr_debug("%s, ", arg->var);
+ fieldp = &arg->field;
+
+ do {
+ *fieldp = zalloc(sizeof(struct perf_probe_arg_field));
+ if (*fieldp == NULL)
+ return -ENOMEM;
+ if (*tmp == '[') { /* Array */
+ str = tmp;
+ (*fieldp)->index = strtol(str + 1, &tmp, 0);
+ (*fieldp)->ref = true;
+ if (*tmp != ']' || tmp == str + 1) {
+ semantic_error("Array index must be a"
+ " number.\n");
+ return -EINVAL;
+ }
+ tmp++;
+ if (*tmp == '\0')
+ tmp = NULL;
+ } else { /* Structure */
+ if (*tmp == '.') {
+ str = tmp + 1;
+ (*fieldp)->ref = false;
+ } else if (tmp[1] == '>') {
+ str = tmp + 2;
+ (*fieldp)->ref = true;
+ } else {
+ semantic_error("Argument parse error: %s\n",
+ str);
+ return -EINVAL;
+ }
+ tmp = strpbrk(str, "-.[");
+ }
+ if (tmp) {
+ (*fieldp)->name = strndup(str, tmp - str);
+ if ((*fieldp)->name == NULL)
+ return -ENOMEM;
+ if (*str != '[')
+ goodname = (*fieldp)->name;
+ pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
+ fieldp = &(*fieldp)->next;
+ }
+ } while (tmp);
+ (*fieldp)->name = strdup(str);
+ if ((*fieldp)->name == NULL)
+ return -ENOMEM;
+ if (*str != '[')
+ goodname = (*fieldp)->name;
+ pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
+
+ /* If no name is specified, set the last field name (not array index)*/
+ if (!arg->name) {
+ arg->name = strdup(goodname);
+ if (arg->name == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* Parse perf-probe event command */
+int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
+{
+ char **argv;
+ int argc, i, ret = 0;
+
+ argv = argv_split(cmd, &argc);
+ if (!argv) {
+ pr_debug("Failed to split arguments.\n");
+ return -ENOMEM;
+ }
+ if (argc - 1 > MAX_PROBE_ARGS) {
+ semantic_error("Too many probe arguments (%d).\n", argc - 1);
+ ret = -ERANGE;
+ goto out;
+ }
+ /* Parse probe point */
+ ret = parse_perf_probe_point(argv[0], pev);
+ if (ret < 0)
+ goto out;
+
+ /* Copy arguments and ensure return probe has no C argument */
+ pev->nargs = argc - 1;
+ pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ if (pev->args == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < pev->nargs && ret >= 0; i++) {
+ ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
+ if (ret >= 0 &&
+ is_c_varname(pev->args[i].var) && pev->point.retprobe) {
+ semantic_error("You can't specify local variable for"
+ " kretprobe.\n");
+ ret = -EINVAL;
+ }
+ }
+out:
+ argv_free(argv);
+
+ return ret;
+}
+
+/* Return true if this perf_probe_event requires debuginfo */
+bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
+{
+ int i;
+
+ if (pev->point.file || pev->point.line || pev->point.lazy_line)
+ return true;
+
+ for (i = 0; i < pev->nargs; i++)
+ if (is_c_varname(pev->args[i].var))
+ return true;
+
+ return false;
+}
+
+/* Parse probe_events event into struct probe_point */
+static int parse_probe_trace_command(const char *cmd,
+ struct probe_trace_event *tev)
+{
+ struct probe_trace_point *tp = &tev->point;
+ char pr;
+ char *p;
+ int ret, i, argc;
+ char **argv;
+
+ pr_debug("Parsing probe_events: %s\n", cmd);
+ argv = argv_split(cmd, &argc);
+ if (!argv) {
+ pr_debug("Failed to split arguments.\n");
+ return -ENOMEM;
+ }
+ if (argc < 2) {
+ semantic_error("Too few probe arguments.\n");
+ ret = -ERANGE;
+ goto out;
+ }
+
+ /* Scan event and group name. */
+ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
+ &pr, (float *)(void *)&tev->group,
+ (float *)(void *)&tev->event);
+ if (ret != 3) {
+ semantic_error("Failed to parse event name: %s\n", argv[0]);
+ ret = -EINVAL;
+ goto out;
+ }
+ pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr);
+
+ tp->retprobe = (pr == 'r');
+
+ /* Scan function name and offset */
+ ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol,
+ &tp->offset);
+ if (ret == 1)
+ tp->offset = 0;
+
+ tev->nargs = argc - 2;
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
+ if (tev->args == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < tev->nargs; i++) {
+ p = strchr(argv[i + 2], '=');
+ if (p) /* We don't need which register is assigned. */
+ *p++ = '\0';
+ else
+ p = argv[i + 2];
+ tev->args[i].name = strdup(argv[i + 2]);
+ /* TODO: parse regs and offset */
+ tev->args[i].value = strdup(p);
+ if (tev->args[i].name == NULL || tev->args[i].value == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ ret = 0;
+out:
+ argv_free(argv);
+ return ret;
+}
+
+/* Compose only probe arg */
+int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
+{
+ struct perf_probe_arg_field *field = pa->field;
+ int ret;
+ char *tmp = buf;
+
+ if (pa->name && pa->var)
+ ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var);
+ else
+ ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var);
+ if (ret <= 0)
+ goto error;
+ tmp += ret;
+ len -= ret;
+
+ while (field) {
+ if (field->name[0] == '[')
+ ret = e_snprintf(tmp, len, "%s", field->name);
+ else
+ ret = e_snprintf(tmp, len, "%s%s",
+ field->ref ? "->" : ".", field->name);
+ if (ret <= 0)
+ goto error;
+ tmp += ret;
+ len -= ret;
+ field = field->next;
+ }
+
+ if (pa->type) {
+ ret = e_snprintf(tmp, len, ":%s", pa->type);
+ if (ret <= 0)
+ goto error;
+ tmp += ret;
+ len -= ret;
+ }
+
+ return tmp - buf;
+error:
+ pr_debug("Failed to synthesize perf probe argument: %s\n",
+ strerror(-ret));
+ return ret;
+}
+
+/* Compose only probe point (not argument) */
+static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
+{
+ char *buf, *tmp;
+ char offs[32] = "", line[32] = "", file[32] = "";
+ int ret, len;
+
+ buf = zalloc(MAX_CMDLEN);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ if (pp->offset) {
+ ret = e_snprintf(offs, 32, "+%lu", pp->offset);
+ if (ret <= 0)
+ goto error;
+ }
+ if (pp->line) {
+ ret = e_snprintf(line, 32, ":%d", pp->line);
+ if (ret <= 0)
+ goto error;
+ }
+ if (pp->file) {
+ tmp = pp->file;
+ len = strlen(tmp);
+ if (len > 30) {
+ tmp = strchr(pp->file + len - 30, '/');
+ tmp = tmp ? tmp + 1 : pp->file + len - 30;
+ }
+ ret = e_snprintf(file, 32, "@%s", tmp);
+ if (ret <= 0)
+ goto error;
+ }
+
+ if (pp->function)
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function,
+ offs, pp->retprobe ? "%return" : "", line,
+ file);
+ else
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line);
+ if (ret <= 0)
+ goto error;
+
+ return buf;
+error:
+ pr_debug("Failed to synthesize perf probe point: %s\n",
+ strerror(-ret));
+ if (buf)
+ free(buf);
+ return NULL;
+}
+
+#if 0
+char *synthesize_perf_probe_command(struct perf_probe_event *pev)
+{
+ char *buf;
+ int i, len, ret;
+
+ buf = synthesize_perf_probe_point(&pev->point);
+ if (!buf)
+ return NULL;
+
+ len = strlen(buf);
+ for (i = 0; i < pev->nargs; i++) {
+ ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
+ pev->args[i].name);
+ if (ret <= 0) {
+ free(buf);
+ return NULL;
+ }
+ len += ret;
+ }
+
+ return buf;
+}
+#endif
+
+static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
+ char **buf, size_t *buflen,
+ int depth)
+{
+ int ret;
+ if (ref->next) {
+ depth = __synthesize_probe_trace_arg_ref(ref->next, buf,
+ buflen, depth + 1);
+ if (depth < 0)
+ goto out;
+ }
+
+ ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset);
+ if (ret < 0)
+ depth = ret;
+ else {
+ *buf += ret;
+ *buflen -= ret;
+ }
+out:
+ return depth;
+
+}
+
+static int synthesize_probe_trace_arg(struct probe_trace_arg *arg,
+ char *buf, size_t buflen)
+{
+ struct probe_trace_arg_ref *ref = arg->ref;
+ int ret, depth = 0;
+ char *tmp = buf;
+
+ /* Argument name or separator */
+ if (arg->name)
+ ret = e_snprintf(buf, buflen, " %s=", arg->name);
+ else
+ ret = e_snprintf(buf, buflen, " ");
+ if (ret < 0)
+ return ret;
+ buf += ret;
+ buflen -= ret;
+
+ /* Special case: @XXX */
+ if (arg->value[0] == '@' && arg->ref)
+ ref = ref->next;
+
+ /* Dereferencing arguments */
+ if (ref) {
+ depth = __synthesize_probe_trace_arg_ref(ref, &buf,
+ &buflen, 1);
+ if (depth < 0)
+ return depth;
+ }
+
+ /* Print argument value */
+ if (arg->value[0] == '@' && arg->ref)
+ ret = e_snprintf(buf, buflen, "%s%+ld", arg->value,
+ arg->ref->offset);
+ else
+ ret = e_snprintf(buf, buflen, "%s", arg->value);
+ if (ret < 0)
+ return ret;
+ buf += ret;
+ buflen -= ret;
+
+ /* Closing */
+ while (depth--) {
+ ret = e_snprintf(buf, buflen, ")");
+ if (ret < 0)
+ return ret;
+ buf += ret;
+ buflen -= ret;
+ }
+ /* Print argument type */
+ if (arg->type) {
+ ret = e_snprintf(buf, buflen, ":%s", arg->type);
+ if (ret <= 0)
+ return ret;
+ buf += ret;
+ }
+
+ return buf - tmp;
+}
+
+char *synthesize_probe_trace_command(struct probe_trace_event *tev)
+{
+ struct probe_trace_point *tp = &tev->point;
+ char *buf;
+ int i, len, ret;
+
+ buf = zalloc(MAX_CMDLEN);
+ if (buf == NULL)
+ return NULL;
+
+ len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
+ tp->retprobe ? 'r' : 'p',
+ tev->group, tev->event,
+ tp->symbol, tp->offset);
+ if (len <= 0)
+ goto error;
+
+ for (i = 0; i < tev->nargs; i++) {
+ ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
+ MAX_CMDLEN - len);
+ if (ret <= 0)
+ goto error;
+ len += ret;
+ }
+
+ return buf;
+error:
+ free(buf);
+ return NULL;
+}
+
+static int convert_to_perf_probe_event(struct probe_trace_event *tev,
+ struct perf_probe_event *pev)
+{
+ char buf[64] = "";
+ int i, ret;
+
+ /* Convert event/group name */
+ pev->event = strdup(tev->event);
+ pev->group = strdup(tev->group);
+ if (pev->event == NULL || pev->group == NULL)
+ return -ENOMEM;
+
+ /* Convert trace_point to probe_point */
+ ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
+ if (ret < 0)
+ return ret;
+
+ /* Convert trace_arg to probe_arg */
+ pev->nargs = tev->nargs;
+ pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ if (pev->args == NULL)
+ return -ENOMEM;
+ for (i = 0; i < tev->nargs && ret >= 0; i++) {
+ if (tev->args[i].name)
+ pev->args[i].name = strdup(tev->args[i].name);
+ else {
+ ret = synthesize_probe_trace_arg(&tev->args[i],
+ buf, 64);
+ pev->args[i].name = strdup(buf);
+ }
+ if (pev->args[i].name == NULL && ret >= 0)
+ ret = -ENOMEM;
+ }
+
+ if (ret < 0)
+ clear_perf_probe_event(pev);
+
+ return ret;
+}
+
+void clear_perf_probe_event(struct perf_probe_event *pev)
+{
+ struct perf_probe_point *pp = &pev->point;
+ struct perf_probe_arg_field *field, *next;
+ int i;
+
+ if (pev->event)
+ free(pev->event);
+ if (pev->group)
+ free(pev->group);
+ if (pp->file)
+ free(pp->file);
+ if (pp->function)
+ free(pp->function);
+ if (pp->lazy_line)
+ free(pp->lazy_line);
+ for (i = 0; i < pev->nargs; i++) {
+ if (pev->args[i].name)
+ free(pev->args[i].name);
+ if (pev->args[i].var)
+ free(pev->args[i].var);
+ if (pev->args[i].type)
+ free(pev->args[i].type);
+ field = pev->args[i].field;
+ while (field) {
+ next = field->next;
+ if (field->name)
+ free(field->name);
+ free(field);
+ field = next;
+ }
+ }
+ if (pev->args)
+ free(pev->args);
+ memset(pev, 0, sizeof(*pev));
+}
+
+static void clear_probe_trace_event(struct probe_trace_event *tev)
+{
+ struct probe_trace_arg_ref *ref, *next;
+ int i;
+
+ if (tev->event)
+ free(tev->event);
+ if (tev->group)
+ free(tev->group);
+ if (tev->point.symbol)
+ free(tev->point.symbol);
+ for (i = 0; i < tev->nargs; i++) {
+ if (tev->args[i].name)
+ free(tev->args[i].name);
+ if (tev->args[i].value)
+ free(tev->args[i].value);
+ if (tev->args[i].type)
+ free(tev->args[i].type);
+ ref = tev->args[i].ref;
+ while (ref) {
+ next = ref->next;
+ free(ref);
+ ref = next;
+ }
+ }
+ if (tev->args)
+ free(tev->args);
+ memset(tev, 0, sizeof(*tev));
+}
+
+static int open_kprobe_events(bool readwrite)
+{
+ char buf[PATH_MAX];
+ const char *__debugfs;
+ int ret;
+
+ __debugfs = debugfs_find_mountpoint();
+ if (__debugfs == NULL) {
+ pr_warning("Debugfs is not mounted.\n");
+ return -ENOENT;
+ }
+
+ ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs);
+ if (ret >= 0) {
+ pr_debug("Opening %s write=%d\n", buf, readwrite);
+ if (readwrite && !probe_event_dry_run)
+ ret = open(buf, O_RDWR, O_APPEND);
+ else
+ ret = open(buf, O_RDONLY, 0);
+ }
+
+ if (ret < 0) {
+ if (errno == ENOENT)
+ pr_warning("kprobe_events file does not exist - please"
+ " rebuild kernel with CONFIG_KPROBE_EVENT.\n");
+ else
+ pr_warning("Failed to open kprobe_events file: %s\n",
+ strerror(errno));
+ }
+ return ret;
+}
+
+/* Get raw string list of current kprobe_events */
+static struct strlist *get_probe_trace_command_rawlist(int fd)
+{
+ int ret, idx;
+ FILE *fp;
+ char buf[MAX_CMDLEN];
+ char *p;
+ struct strlist *sl;
+
+ sl = strlist__new(true, NULL);
+
+ fp = fdopen(dup(fd), "r");
+ while (!feof(fp)) {
+ p = fgets(buf, MAX_CMDLEN, fp);
+ if (!p)
+ break;
+
+ idx = strlen(p) - 1;
+ if (p[idx] == '\n')
+ p[idx] = '\0';
+ ret = strlist__add(sl, buf);
+ if (ret < 0) {
+ pr_debug("strlist__add failed: %s\n", strerror(-ret));
+ strlist__delete(sl);
+ return NULL;
+ }
+ }
+ fclose(fp);
+
+ return sl;
+}
+
+/* Show an event */
+static int show_perf_probe_event(struct perf_probe_event *pev)
+{
+ int i, ret;
+ char buf[128];
+ char *place;
+
+ /* Synthesize only event probe point */
+ place = synthesize_perf_probe_point(&pev->point);
+ if (!place)
+ return -EINVAL;
+
+ ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event);
+ if (ret < 0)
+ return ret;
+
+ printf(" %-20s (on %s", buf, place);
+
+ if (pev->nargs > 0) {
+ printf(" with");
+ for (i = 0; i < pev->nargs; i++) {
+ ret = synthesize_perf_probe_arg(&pev->args[i],
+ buf, 128);
+ if (ret < 0)
+ break;
+ printf(" %s", buf);
+ }
+ }
+ printf(")\n");
+ free(place);
+ return ret;
+}
+
+/* List up current perf-probe events */
+int show_perf_probe_events(void)
+{
+ int fd, ret;
+ struct probe_trace_event tev;
+ struct perf_probe_event pev;
+ struct strlist *rawlist;
+ struct str_node *ent;
+
+ setup_pager();
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;
+
+ memset(&tev, 0, sizeof(tev));
+ memset(&pev, 0, sizeof(pev));
+
+ fd = open_kprobe_events(false);
+ if (fd < 0)
+ return fd;
+
+ rawlist = get_probe_trace_command_rawlist(fd);
+ close(fd);
+ if (!rawlist)
+ return -ENOENT;
+
+ strlist__for_each(ent, rawlist) {
+ ret = parse_probe_trace_command(ent->s, &tev);
+ if (ret >= 0) {
+ ret = convert_to_perf_probe_event(&tev, &pev);
+ if (ret >= 0)
+ ret = show_perf_probe_event(&pev);
+ }
+ clear_perf_probe_event(&pev);
+ clear_probe_trace_event(&tev);
+ if (ret < 0)
+ break;
+ }
+ strlist__delete(rawlist);
+
+ return ret;
+}
+
+/* Get current perf-probe event names */
+static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
+{
+ char buf[128];
+ struct strlist *sl, *rawlist;
+ struct str_node *ent;
+ struct probe_trace_event tev;
+ int ret = 0;
+
+ memset(&tev, 0, sizeof(tev));
+ rawlist = get_probe_trace_command_rawlist(fd);
+ sl = strlist__new(true, NULL);
+ strlist__for_each(ent, rawlist) {
+ ret = parse_probe_trace_command(ent->s, &tev);
+ if (ret < 0)
+ break;
+ if (include_group) {
+ ret = e_snprintf(buf, 128, "%s:%s", tev.group,
+ tev.event);
+ if (ret >= 0)
+ ret = strlist__add(sl, buf);
+ } else
+ ret = strlist__add(sl, tev.event);
+ clear_probe_trace_event(&tev);
+ if (ret < 0)
+ break;
+ }
+ strlist__delete(rawlist);
+
+ if (ret < 0) {
+ strlist__delete(sl);
+ return NULL;
+ }
+ return sl;
+}
+
+static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
+{
+ int ret = 0;
+ char *buf = synthesize_probe_trace_command(tev);
+
+ if (!buf) {
+ pr_debug("Failed to synthesize probe trace event.\n");
+ return -EINVAL;
+ }
+
+ pr_debug("Writing event: %s\n", buf);
+ if (!probe_event_dry_run) {
+ ret = write(fd, buf, strlen(buf));
+ if (ret <= 0)
+ pr_warning("Failed to write event: %s\n",
+ strerror(errno));
+ }
+ free(buf);
+ return ret;
+}
+
+static int get_new_event_name(char *buf, size_t len, const char *base,
+ struct strlist *namelist, bool allow_suffix)
+{
+ int i, ret;
+
+ /* Try no suffix */
+ ret = e_snprintf(buf, len, "%s", base);
+ if (ret < 0) {
+ pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ return ret;
+ }
+ if (!strlist__has_entry(namelist, buf))
+ return 0;
+
+ if (!allow_suffix) {
+ pr_warning("Error: event \"%s\" already exists. "
+ "(Use -f to force duplicates.)\n", base);
+ return -EEXIST;
+ }
+
+ /* Try to add suffix */
+ for (i = 1; i < MAX_EVENT_INDEX; i++) {
+ ret = e_snprintf(buf, len, "%s_%d", base, i);
+ if (ret < 0) {
+ pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ return ret;
+ }
+ if (!strlist__has_entry(namelist, buf))
+ break;
+ }
+ if (i == MAX_EVENT_INDEX) {
+ pr_warning("Too many events are on the same function.\n");
+ ret = -ERANGE;
+ }
+
+ return ret;
+}
+
+static int __add_probe_trace_events(struct perf_probe_event *pev,
+ struct probe_trace_event *tevs,
+ int ntevs, bool allow_suffix)
+{
+ int i, fd, ret;
+ struct probe_trace_event *tev = NULL;
+ char buf[64];
+ const char *event, *group;
+ struct strlist *namelist;
+
+ fd = open_kprobe_events(true);
+ if (fd < 0)
+ return fd;
+ /* Get current event names */
+ namelist = get_probe_trace_event_names(fd, false);
+ if (!namelist) {
+ pr_debug("Failed to get current event list.\n");
+ return -EIO;
+ }
+
+ ret = 0;
+ printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
+ for (i = 0; i < ntevs; i++) {
+ tev = &tevs[i];
+ if (pev->event)
+ event = pev->event;
+ else
+ if (pev->point.function)
+ event = pev->point.function;
+ else
+ event = tev->point.symbol;
+ if (pev->group)
+ group = pev->group;
+ else
+ group = PERFPROBE_GROUP;
+
+ /* Get an unused new event name */
+ ret = get_new_event_name(buf, 64, event,
+ namelist, allow_suffix);
+ if (ret < 0)
+ break;
+ event = buf;
+
+ tev->event = strdup(event);
+ tev->group = strdup(group);
+ if (tev->event == NULL || tev->group == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+ ret = write_probe_trace_event(fd, tev);
+ if (ret < 0)
+ break;
+ /* Add added event name to namelist */
+ strlist__add(namelist, event);
+
+ /* Trick here - save current event/group */
+ event = pev->event;
+ group = pev->group;
+ pev->event = tev->event;
+ pev->group = tev->group;
+ show_perf_probe_event(pev);
+ /* Trick here - restore current event/group */
+ pev->event = (char *)event;
+ pev->group = (char *)group;
+
+ /*
+ * Probes after the first probe which comes from same
+ * user input are always allowed to add suffix, because
+ * there might be several addresses corresponding to
+ * one code line.
+ */
+ allow_suffix = true;
+ }
+
+ if (ret >= 0) {
+ /* Show how to use the event. */
+ printf("\nYou can now use it on all perf tools, such as:\n\n");
+ printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
+ tev->event);
+ }
+
+ strlist__delete(namelist);
+ close(fd);
+ return ret;
+}
+
+static int convert_to_probe_trace_events(struct perf_probe_event *pev,
+ struct probe_trace_event **tevs,
+ int max_tevs, const char *module)
+{
+ struct symbol *sym;
+ int ret = 0, i;
+ struct probe_trace_event *tev;
+
+ /* Convert perf_probe_event with debuginfo */
+ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
+ if (ret != 0)
+ return ret;
+
+ /* Allocate trace event buffer */
+ tev = *tevs = zalloc(sizeof(struct probe_trace_event));
+ if (tev == NULL)
+ return -ENOMEM;
+
+ /* Copy parameters */
+ tev->point.symbol = strdup(pev->point.function);
+ if (tev->point.symbol == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ tev->point.offset = pev->point.offset;
+ tev->point.retprobe = pev->point.retprobe;
+ tev->nargs = pev->nargs;
+ if (tev->nargs) {
+ tev->args = zalloc(sizeof(struct probe_trace_arg)
+ * tev->nargs);
+ if (tev->args == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ for (i = 0; i < tev->nargs; i++) {
+ if (pev->args[i].name) {
+ tev->args[i].name = strdup(pev->args[i].name);
+ if (tev->args[i].name == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+ tev->args[i].value = strdup(pev->args[i].var);
+ if (tev->args[i].value == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ if (pev->args[i].type) {
+ tev->args[i].type = strdup(pev->args[i].type);
+ if (tev->args[i].type == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+ }
+ }
+
+ /* Currently just checking function name from symbol map */
+ sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
+ if (!sym) {
+ pr_warning("Kernel symbol \'%s\' not found.\n",
+ tev->point.symbol);
+ ret = -ENOENT;
+ goto error;
+ }
+
+ return 1;
+error:
+ clear_probe_trace_event(tev);
+ free(tev);
+ *tevs = NULL;
+ return ret;
+}
+
+struct __event_package {
+ struct perf_probe_event *pev;
+ struct probe_trace_event *tevs;
+ int ntevs;
+};
+
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
+ int max_tevs, const char *module, bool force_add)
+{
+ int i, j, ret;
+ struct __event_package *pkgs;
+
+ pkgs = zalloc(sizeof(struct __event_package) * npevs);
+ if (pkgs == NULL)
+ return -ENOMEM;
+
+ /* Init vmlinux path */
+ ret = init_vmlinux();
+ if (ret < 0) {
+ free(pkgs);
+ return ret;
+ }
+
+ /* Loop 1: convert all events */
+ for (i = 0; i < npevs; i++) {
+ pkgs[i].pev = &pevs[i];
+ /* Convert with or without debuginfo */
+ ret = convert_to_probe_trace_events(pkgs[i].pev,
+ &pkgs[i].tevs,
+ max_tevs,
+ module);
+ if (ret < 0)
+ goto end;
+ pkgs[i].ntevs = ret;
+ }
+
+ /* Loop 2: add all events */
+ for (i = 0; i < npevs && ret >= 0; i++)
+ ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
+ pkgs[i].ntevs, force_add);
+end:
+ /* Loop 3: cleanup and free trace events */
+ for (i = 0; i < npevs; i++) {
+ for (j = 0; j < pkgs[i].ntevs; j++)
+ clear_probe_trace_event(&pkgs[i].tevs[j]);
+ free(pkgs[i].tevs);
+ }
+ free(pkgs);
+
+ return ret;
+}
+
+static int __del_trace_probe_event(int fd, struct str_node *ent)
+{
+ char *p;
+ char buf[128];
+ int ret;
+
+ /* Convert from perf-probe event to trace-probe event */
+ ret = e_snprintf(buf, 128, "-:%s", ent->s);
+ if (ret < 0)
+ goto error;
+
+ p = strchr(buf + 2, ':');
+ if (!p) {
+ pr_debug("Internal error: %s should have ':' but not.\n",
+ ent->s);
+ ret = -ENOTSUP;
+ goto error;
+ }
+ *p = '/';
+
+ pr_debug("Writing event: %s\n", buf);
+ ret = write(fd, buf, strlen(buf));
+ if (ret < 0)
+ goto error;
+
+ printf("Remove event: %s\n", ent->s);
+ return 0;
+error:
+ pr_warning("Failed to delete event: %s\n", strerror(-ret));
+ return ret;
+}
+
+static int del_trace_probe_event(int fd, const char *group,
+ const char *event, struct strlist *namelist)
+{
+ char buf[128];
+ struct str_node *ent, *n;
+ int found = 0, ret = 0;
+
+ ret = e_snprintf(buf, 128, "%s:%s", group, event);
+ if (ret < 0) {
+ pr_err("Failed to copy event.\n");
+ return ret;
+ }
+
+ if (strpbrk(buf, "*?")) { /* Glob-exp */
+ strlist__for_each_safe(ent, n, namelist)
+ if (strglobmatch(ent->s, buf)) {
+ found++;
+ ret = __del_trace_probe_event(fd, ent);
+ if (ret < 0)
+ break;
+ strlist__remove(namelist, ent);
+ }
+ } else {
+ ent = strlist__find(namelist, buf);
+ if (ent) {
+ found++;
+ ret = __del_trace_probe_event(fd, ent);
+ if (ret >= 0)
+ strlist__remove(namelist, ent);
+ }
+ }
+ if (found == 0 && ret >= 0)
+ pr_info("Info: Event \"%s\" does not exist.\n", buf);
+
+ return ret;
+}
+
+int del_perf_probe_events(struct strlist *dellist)
+{
+ int fd, ret = 0;
+ const char *group, *event;
+ char *p, *str;
+ struct str_node *ent;
+ struct strlist *namelist;
+
+ fd = open_kprobe_events(true);
+ if (fd < 0)
+ return fd;
+
+ /* Get current event names */
+ namelist = get_probe_trace_event_names(fd, true);
+ if (namelist == NULL)
+ return -EINVAL;
+
+ strlist__for_each(ent, dellist) {
+ str = strdup(ent->s);
+ if (str == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+ pr_debug("Parsing: %s\n", str);
+ p = strchr(str, ':');
+ if (p) {
+ group = str;
+ *p = '\0';
+ event = p + 1;
+ } else {
+ group = "*";
+ event = str;
+ }
+ pr_debug("Group: %s, Event: %s\n", group, event);
+ ret = del_trace_probe_event(fd, group, event, namelist);
+ free(str);
+ if (ret < 0)
+ break;
+ }
+ strlist__delete(namelist);
+ close(fd);
+
+ return ret;
+}
+
diff --git a/smartt-perf/util/probe-event.h b/smartt-perf/util/probe-event.h
new file mode 100644
index 0000000..5accbed
--- /dev/null
+++ b/smartt-perf/util/probe-event.h
@@ -0,0 +1,135 @@
+#ifndef _PROBE_EVENT_H
+#define _PROBE_EVENT_H
+
+#include <stdbool.h>
+#include "strlist.h"
+
+extern bool probe_event_dry_run;
+
+/* kprobe-tracer tracing point */
+struct probe_trace_point {
+ char *symbol; /* Base symbol */
+ unsigned long offset; /* Offset from symbol */
+ bool retprobe; /* Return probe flag */
+};
+
+/* probe-tracer tracing argument referencing offset */
+struct probe_trace_arg_ref {
+ struct probe_trace_arg_ref *next; /* Next reference */
+ long offset; /* Offset value */
+};
+
+/* kprobe-tracer tracing argument */
+struct probe_trace_arg {
+ char *name; /* Argument name */
+ char *value; /* Base value */
+ char *type; /* Type name */
+ struct probe_trace_arg_ref *ref; /* Referencing offset */
+};
+
+/* kprobe-tracer tracing event (point + arg) */
+struct probe_trace_event {
+ char *event; /* Event name */
+ char *group; /* Group name */
+ struct probe_trace_point point; /* Trace point */
+ int nargs; /* Number of args */
+ struct probe_trace_arg *args; /* Arguments */
+};
+
+/* Perf probe probing point */
+struct perf_probe_point {
+ char *file; /* File path */
+ char *function; /* Function name */
+ int line; /* Line number */
+ bool retprobe; /* Return probe flag */
+ char *lazy_line; /* Lazy matching pattern */
+ unsigned long offset; /* Offset from function entry */
+};
+
+/* Perf probe probing argument field chain */
+struct perf_probe_arg_field {
+ struct perf_probe_arg_field *next; /* Next field */
+ char *name; /* Name of the field */
+ long index; /* Array index number */
+ bool ref; /* Referencing flag */
+};
+
+/* Perf probe probing argument */
+struct perf_probe_arg {
+ char *name; /* Argument name */
+ char *var; /* Variable name */
+ char *type; /* Type name */
+ struct perf_probe_arg_field *field; /* Structure fields */
+};
+
+/* Perf probe probing event (point + arg) */
+struct perf_probe_event {
+ char *event; /* Event name */
+ char *group; /* Group name */
+ struct perf_probe_point point; /* Probe point */
+ int nargs; /* Number of arguments */
+ struct perf_probe_arg *args; /* Arguments */
+};
+
+
+/* Line number container */
+struct line_node {
+ struct list_head list;
+ int line;
+};
+
+/* Line range */
+struct line_range {
+ char *file; /* File name */
+ char *function; /* Function name */
+ int start; /* Start line number */
+ int end; /* End line number */
+ int offset; /* Start line offset */
+ char *path; /* Real path name */
+ char *comp_dir; /* Compile directory */
+ struct list_head line_list; /* Visible lines */
+};
+
+/* List of variables */
+struct variable_list {
+ struct probe_trace_point point; /* Actual probepoint */
+ struct strlist *vars; /* Available variables */
+};
+
+/* Command string to events */
+extern int parse_perf_probe_command(const char *cmd,
+ struct perf_probe_event *pev);
+
+/* Events to command string */
+extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
+extern char *synthesize_probe_trace_command(struct probe_trace_event *tev);
+extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
+ size_t len);
+
+/* Check the perf_probe_event needs debuginfo */
+extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
+
+/* Release event contents */
+extern void clear_perf_probe_event(struct perf_probe_event *pev);
+
+/* Command string to line-range */
+extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
+
+/* Internal use: Return kernel/module path */
+extern const char *kernel_get_module_path(const char *module);
+
+extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
+ int max_probe_points, const char *module,
+ bool force_add);
+extern int del_perf_probe_events(struct strlist *dellist);
+extern int show_perf_probe_events(void);
+extern int show_line_range(struct line_range *lr, const char *module);
+extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
+ int max_probe_points, const char *module,
+ bool externs);
+
+
+/* Maximum index number of event-name postfix */
+#define MAX_EVENT_INDEX 1024
+
+#endif /*_PROBE_EVENT_H */
diff --git a/smartt-perf/util/probe-finder.c b/smartt-perf/util/probe-finder.c
new file mode 100644
index 0000000..ab83b6a
--- /dev/null
+++ b/smartt-perf/util/probe-finder.c
@@ -0,0 +1,1861 @@
+/*
+ * probe-finder.c : C expression to kprobe event converter
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <dwarf-regs.h>
+
+#include "event.h"
+#include "debug.h"
+#include "util.h"
+#include "symbol.h"
+#include "probe-finder.h"
+
+/* Kprobe tracer basic type is up to u64 */
+#define MAX_BASIC_TYPE_BITS 64
+
+/*
+ * Compare the tail of two strings.
+ * Return 0 if whole of either string is same as another's tail part.
+ */
+static int strtailcmp(const char *s1, const char *s2)
+{
+ int i1 = strlen(s1);
+ int i2 = strlen(s2);
+ while (--i1 >= 0 && --i2 >= 0) {
+ if (s1[i1] != s2[i2])
+ return s1[i1] - s2[i2];
+ }
+ return 0;
+}
+
+/* Line number list operations */
+
+/* Add a line to line number list */
+static int line_list__add_line(struct list_head *head, int line)
+{
+ struct line_node *ln;
+ struct list_head *p;
+
+ /* Reverse search, because new line will be the last one */
+ list_for_each_entry_reverse(ln, head, list) {
+ if (ln->line < line) {
+ p = &ln->list;
+ goto found;
+ } else if (ln->line == line) /* Already exist */
+ return 1;
+ }
+ /* List is empty, or the smallest entry */
+ p = head;
+found:
+ pr_debug("line list: add a line %u\n", line);
+ ln = zalloc(sizeof(struct line_node));
+ if (ln == NULL)
+ return -ENOMEM;
+ ln->line = line;
+ INIT_LIST_HEAD(&ln->list);
+ list_add(&ln->list, p);
+ return 0;
+}
+
+/* Check if the line in line number list */
+static int line_list__has_line(struct list_head *head, int line)
+{
+ struct line_node *ln;
+
+ /* Reverse search, because new line will be the last one */
+ list_for_each_entry(ln, head, list)
+ if (ln->line == line)
+ return 1;
+
+ return 0;
+}
+
+/* Init line number list */
+static void line_list__init(struct list_head *head)
+{
+ INIT_LIST_HEAD(head);
+}
+
+/* Free line number list */
+static void line_list__free(struct list_head *head)
+{
+ struct line_node *ln;
+ while (!list_empty(head)) {
+ ln = list_first_entry(head, struct line_node, list);
+ list_del(&ln->list);
+ free(ln);
+ }
+}
+
+/* Dwarf FL wrappers */
+static char *debuginfo_path; /* Currently dummy */
+
+static const Dwfl_Callbacks offline_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .section_address = dwfl_offline_section_address,
+
+ /* We use this table for core files too. */
+ .find_elf = dwfl_build_id_find_elf,
+};
+
+/* Get a Dwarf from offline image */
+static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
+{
+ Dwfl_Module *mod;
+ Dwarf *dbg = NULL;
+
+ if (!dwflp)
+ return NULL;
+
+ *dwflp = dwfl_begin(&offline_callbacks);
+ if (!*dwflp)
+ return NULL;
+
+ mod = dwfl_report_offline(*dwflp, "", "", fd);
+ if (!mod)
+ goto error;
+
+ dbg = dwfl_module_getdwarf(mod, bias);
+ if (!dbg) {
+error:
+ dwfl_end(*dwflp);
+ *dwflp = NULL;
+ }
+ return dbg;
+}
+
+#if _ELFUTILS_PREREQ(0, 148)
+/* This method is buggy if elfutils is older than 0.148 */
+static int __linux_kernel_find_elf(Dwfl_Module *mod,
+ void **userdata,
+ const char *module_name,
+ Dwarf_Addr base,
+ char **file_name, Elf **elfp)
+{
+ int fd;
+ const char *path = kernel_get_module_path(module_name);
+
+ pr_debug2("Use file %s for %s\n", path, module_name);
+ if (path) {
+ fd = open(path, O_RDONLY);
+ if (fd >= 0) {
+ *file_name = strdup(path);
+ return fd;
+ }
+ }
+ /* If failed, try to call standard method */
+ return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
+ file_name, elfp);
+}
+
+static const Dwfl_Callbacks kernel_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = __linux_kernel_find_elf,
+ .section_address = dwfl_linux_kernel_module_section_address,
+};
+
+/* Get a Dwarf from live kernel image */
+static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
+ Dwarf_Addr *bias)
+{
+ Dwarf *dbg;
+
+ if (!dwflp)
+ return NULL;
+
+ *dwflp = dwfl_begin(&kernel_callbacks);
+ if (!*dwflp)
+ return NULL;
+
+ /* Load the kernel dwarves: Don't care the result here */
+ dwfl_linux_kernel_report_kernel(*dwflp);
+ dwfl_linux_kernel_report_modules(*dwflp);
+
+ dbg = dwfl_addrdwarf(*dwflp, addr, bias);
+ /* Here, check whether we could get a real dwarf */
+ if (!dbg) {
+ pr_debug("Failed to find kernel dwarf at %lx\n",
+ (unsigned long)addr);
+ dwfl_end(*dwflp);
+ *dwflp = NULL;
+ }
+ return dbg;
+}
+#else
+/* With older elfutils, this just support kernel module... */
+static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp,
+ Dwarf_Addr *bias)
+{
+ int fd;
+ const char *path = kernel_get_module_path("kernel");
+
+ if (!path) {
+ pr_err("Failed to find vmlinux path\n");
+ return NULL;
+ }
+
+ pr_debug2("Use file %s for debuginfo\n", path);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ return dwfl_init_offline_dwarf(fd, dwflp, bias);
+}
+#endif
+
+/* Dwarf wrappers */
+
+/* Find the realpath of the target file. */
+static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
+{
+ Dwarf_Files *files;
+ size_t nfiles, i;
+ const char *src = NULL;
+ int ret;
+
+ if (!fname)
+ return NULL;
+
+ ret = dwarf_getsrcfiles(cu_die, &files, &nfiles);
+ if (ret != 0)
+ return NULL;
+
+ for (i = 0; i < nfiles; i++) {
+ src = dwarf_filesrc(files, i, NULL, NULL);
+ if (strtailcmp(src, fname) == 0)
+ break;
+ }
+ if (i == nfiles)
+ return NULL;
+ return src;
+}
+
+/* Get DW_AT_comp_dir (should be NULL with older gcc) */
+static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
+{
+ Dwarf_Attribute attr;
+ if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
+ return NULL;
+ return dwarf_formstring(&attr);
+}
+
+/* Compare diename and tname */
+static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
+{
+ const char *name;
+ name = dwarf_diename(dw_die);
+ return name ? (strcmp(tname, name) == 0) : false;
+}
+
+/* Get type die */
+static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+ Dwarf_Attribute attr;
+
+ if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, die_mem))
+ return die_mem;
+ else
+ return NULL;
+}
+
+/* Get a type die, but skip qualifiers */
+static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+ int tag;
+
+ do {
+ vr_die = die_get_type(vr_die, die_mem);
+ if (!vr_die)
+ break;
+ tag = dwarf_tag(vr_die);
+ } while (tag == DW_TAG_const_type ||
+ tag == DW_TAG_restrict_type ||
+ tag == DW_TAG_volatile_type ||
+ tag == DW_TAG_shared_type);
+
+ return vr_die;
+}
+
+/* Get a type die, but skip qualifiers and typedef */
+static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+ do {
+ vr_die = __die_get_real_type(vr_die, die_mem);
+ } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
+
+ return vr_die;
+}
+
+static bool die_is_signed_type(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return false;
+
+ return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
+ ret == DW_ATE_signed_fixed);
+}
+
+static int die_get_byte_size(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return 0;
+
+ return (int)ret;
+}
+
+/* Get data_member_location offset */
+static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Op *expr;
+ size_t nexpr;
+ int ret;
+
+ if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
+ return -ENOENT;
+
+ if (dwarf_formudata(&attr, offs) != 0) {
+ /* DW_AT_data_member_location should be DW_OP_plus_uconst */
+ ret = dwarf_getlocation(&attr, &expr, &nexpr);
+ if (ret < 0 || nexpr == 0)
+ return -ENOENT;
+
+ if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
+ pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
+ expr[0].atom, nexpr);
+ return -ENOTSUP;
+ }
+ *offs = (Dwarf_Word)expr[0].number;
+ }
+ return 0;
+}
+
+/* Return values for die_find callbacks */
+enum {
+ DIE_FIND_CB_FOUND = 0, /* End of Search */
+ DIE_FIND_CB_CHILD = 1, /* Search only children */
+ DIE_FIND_CB_SIBLING = 2, /* Search only siblings */
+ DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */
+};
+
+/* Search a child die */
+static Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
+ int (*callback)(Dwarf_Die *, void *),
+ void *data, Dwarf_Die *die_mem)
+{
+ Dwarf_Die child_die;
+ int ret;
+
+ ret = dwarf_child(rt_die, die_mem);
+ if (ret != 0)
+ return NULL;
+
+ do {
+ ret = callback(die_mem, data);
+ if (ret == DIE_FIND_CB_FOUND)
+ return die_mem;
+
+ if ((ret & DIE_FIND_CB_CHILD) &&
+ die_find_child(die_mem, callback, data, &child_die)) {
+ memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
+ return die_mem;
+ }
+ } while ((ret & DIE_FIND_CB_SIBLING) &&
+ dwarf_siblingof(die_mem, die_mem) == 0);
+
+ return NULL;
+}
+
+struct __addr_die_search_param {
+ Dwarf_Addr addr;
+ Dwarf_Die *die_mem;
+};
+
+static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
+{
+ struct __addr_die_search_param *ad = data;
+
+ if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
+ dwarf_haspc(fn_die, ad->addr)) {
+ memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
+ return DWARF_CB_ABORT;
+ }
+ return DWARF_CB_OK;
+}
+
+/* Search a real subprogram including this line, */
+static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr,
+ Dwarf_Die *die_mem)
+{
+ struct __addr_die_search_param ad;
+ ad.addr = addr;
+ ad.die_mem = die_mem;
+ /* dwarf_getscopes can't find subprogram. */
+ if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
+ return NULL;
+ else
+ return die_mem;
+}
+
+/* die_find callback for inline function search */
+static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
+{
+ Dwarf_Addr *addr = data;
+
+ if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
+ dwarf_haspc(die_mem, *addr))
+ return DIE_FIND_CB_FOUND;
+
+ return DIE_FIND_CB_CONTINUE;
+}
+
+/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */
+static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
+ Dwarf_Die *die_mem)
+{
+ return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
+}
+
+struct __find_variable_param {
+ const char *name;
+ Dwarf_Addr addr;
+};
+
+static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
+{
+ struct __find_variable_param *fvp = data;
+ int tag;
+
+ tag = dwarf_tag(die_mem);
+ if ((tag == DW_TAG_formal_parameter ||
+ tag == DW_TAG_variable) &&
+ die_compare_name(die_mem, fvp->name))
+ return DIE_FIND_CB_FOUND;
+
+ if (dwarf_haspc(die_mem, fvp->addr))
+ return DIE_FIND_CB_CONTINUE;
+ else
+ return DIE_FIND_CB_SIBLING;
+}
+
+/* Find a variable called 'name' at given address */
+static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
+ Dwarf_Addr addr, Dwarf_Die *die_mem)
+{
+ struct __find_variable_param fvp = { .name = name, .addr = addr};
+
+ return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
+ die_mem);
+}
+
+static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
+{
+ const char *name = data;
+
+ if ((dwarf_tag(die_mem) == DW_TAG_member) &&
+ die_compare_name(die_mem, name))
+ return DIE_FIND_CB_FOUND;
+
+ return DIE_FIND_CB_SIBLING;
+}
+
+/* Find a member called 'name' */
+static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
+ Dwarf_Die *die_mem)
+{
+ return die_find_child(st_die, __die_find_member_cb, (void *)name,
+ die_mem);
+}
+
+/* Get the name of given variable DIE */
+static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
+{
+ Dwarf_Die type;
+ int tag, ret, ret2;
+ const char *tmp = "";
+
+ if (__die_get_real_type(vr_die, &type) == NULL)
+ return -ENOENT;
+
+ tag = dwarf_tag(&type);
+ if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
+ tmp = "*";
+ else if (tag == DW_TAG_subroutine_type) {
+ /* Function pointer */
+ ret = snprintf(buf, len, "(function_type)");
+ return (ret >= len) ? -E2BIG : ret;
+ } else {
+ if (!dwarf_diename(&type))
+ return -ENOENT;
+ if (tag == DW_TAG_union_type)
+ tmp = "union ";
+ else if (tag == DW_TAG_structure_type)
+ tmp = "struct ";
+ /* Write a base name */
+ ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
+ return (ret >= len) ? -E2BIG : ret;
+ }
+ ret = die_get_typename(&type, buf, len);
+ if (ret > 0) {
+ ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+ }
+ return ret;
+}
+
+/* Get the name and type of given variable DIE, stored as "type\tname" */
+static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+{
+ int ret, ret2;
+
+ ret = die_get_typename(vr_die, buf, len);
+ if (ret < 0) {
+ pr_debug("Failed to get type, make it unknown.\n");
+ ret = snprintf(buf, len, "(unknown_type)");
+ }
+ if (ret > 0) {
+ ret2 = snprintf(buf + ret, len - ret, "\t%s",
+ dwarf_diename(vr_die));
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+ }
+ return ret;
+}
+
+/*
+ * Probe finder related functions
+ */
+
+static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
+{
+ struct probe_trace_arg_ref *ref;
+ ref = zalloc(sizeof(struct probe_trace_arg_ref));
+ if (ref != NULL)
+ ref->offset = offs;
+ return ref;
+}
+
+/*
+ * Convert a location into trace_arg.
+ * If tvar == NULL, this just checks variable can be converted.
+ */
+static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
+ Dwarf_Op *fb_ops,
+ struct probe_trace_arg *tvar)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Op *op;
+ size_t nops;
+ unsigned int regn;
+ Dwarf_Word offs = 0;
+ bool ref = false;
+ const char *regs;
+ int ret;
+
+ if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
+ goto static_var;
+
+ /* TODO: handle more than 1 exprs */
+ if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
+ dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
+ nops == 0) {
+ /* TODO: Support const_value */
+ return -ENOENT;
+ }
+
+ if (op->atom == DW_OP_addr) {
+static_var:
+ if (!tvar)
+ return 0;
+ /* Static variables on memory (not stack), make @varname */
+ ret = strlen(dwarf_diename(vr_die));
+ tvar->value = zalloc(ret + 2);
+ if (tvar->value == NULL)
+ return -ENOMEM;
+ snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
+ tvar->ref = alloc_trace_arg_ref((long)offs);
+ if (tvar->ref == NULL)
+ return -ENOMEM;
+ return 0;
+ }
+
+ /* If this is based on frame buffer, set the offset */
+ if (op->atom == DW_OP_fbreg) {
+ if (fb_ops == NULL)
+ return -ENOTSUP;
+ ref = true;
+ offs = op->number;
+ op = &fb_ops[0];
+ }
+
+ if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
+ regn = op->atom - DW_OP_breg0;
+ offs += op->number;
+ ref = true;
+ } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) {
+ regn = op->atom - DW_OP_reg0;
+ } else if (op->atom == DW_OP_bregx) {
+ regn = op->number;
+ offs += op->number2;
+ ref = true;
+ } else if (op->atom == DW_OP_regx) {
+ regn = op->number;
+ } else {
+ pr_debug("DW_OP %x is not supported.\n", op->atom);
+ return -ENOTSUP;
+ }
+
+ if (!tvar)
+ return 0;
+
+ regs = get_arch_regstr(regn);
+ if (!regs) {
+ /* This should be a bug in DWARF or this tool */
+ pr_warning("Mapping for the register number %u "
+ "missing on this architecture.\n", regn);
+ return -ERANGE;
+ }
+
+ tvar->value = strdup(regs);
+ if (tvar->value == NULL)
+ return -ENOMEM;
+
+ if (ref) {
+ tvar->ref = alloc_trace_arg_ref((long)offs);
+ if (tvar->ref == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int convert_variable_type(Dwarf_Die *vr_die,
+ struct probe_trace_arg *tvar,
+ const char *cast)
+{
+ struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
+ Dwarf_Die type;
+ char buf[16];
+ int ret;
+
+ /* TODO: check all types */
+ if (cast && strcmp(cast, "string") != 0) {
+ /* Non string type is OK */
+ tvar->type = strdup(cast);
+ return (tvar->type == NULL) ? -ENOMEM : 0;
+ }
+
+ if (die_get_real_type(vr_die, &type) == NULL) {
+ pr_warning("Failed to get a type information of %s.\n",
+ dwarf_diename(vr_die));
+ return -ENOENT;
+ }
+
+ pr_debug("%s type is %s.\n",
+ dwarf_diename(vr_die), dwarf_diename(&type));
+
+ if (cast && strcmp(cast, "string") == 0) { /* String type */
+ ret = dwarf_tag(&type);
+ if (ret != DW_TAG_pointer_type &&
+ ret != DW_TAG_array_type) {
+ pr_warning("Failed to cast into string: "
+ "%s(%s) is not a pointer nor array.\n",
+ dwarf_diename(vr_die), dwarf_diename(&type));
+ return -EINVAL;
+ }
+ if (ret == DW_TAG_pointer_type) {
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get a type"
+ " information.\n");
+ return -ENOENT;
+ }
+ while (*ref_ptr)
+ ref_ptr = &(*ref_ptr)->next;
+ /* Add new reference with offset +0 */
+ *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref));
+ if (*ref_ptr == NULL) {
+ pr_warning("Out of memory error\n");
+ return -ENOMEM;
+ }
+ }
+ if (!die_compare_name(&type, "char") &&
+ !die_compare_name(&type, "unsigned char")) {
+ pr_warning("Failed to cast into string: "
+ "%s is not (unsigned) char *.\n",
+ dwarf_diename(vr_die));
+ return -EINVAL;
+ }
+ tvar->type = strdup(cast);
+ return (tvar->type == NULL) ? -ENOMEM : 0;
+ }
+
+ ret = die_get_byte_size(&type) * 8;
+ if (ret) {
+ /* Check the bitwidth */
+ if (ret > MAX_BASIC_TYPE_BITS) {
+ pr_info("%s exceeds max-bitwidth."
+ " Cut down to %d bits.\n",
+ dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
+ ret = MAX_BASIC_TYPE_BITS;
+ }
+
+ ret = snprintf(buf, 16, "%c%d",
+ die_is_signed_type(&type) ? 's' : 'u', ret);
+ if (ret < 0 || ret >= 16) {
+ if (ret >= 16)
+ ret = -E2BIG;
+ pr_warning("Failed to convert variable type: %s\n",
+ strerror(-ret));
+ return ret;
+ }
+ tvar->type = strdup(buf);
+ if (tvar->type == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
+ struct perf_probe_arg_field *field,
+ struct probe_trace_arg_ref **ref_ptr,
+ Dwarf_Die *die_mem)
+{
+ struct probe_trace_arg_ref *ref = *ref_ptr;
+ Dwarf_Die type;
+ Dwarf_Word offs;
+ int ret, tag;
+
+ pr_debug("converting %s in %s\n", field->name, varname);
+ if (die_get_real_type(vr_die, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }
+ pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type));
+ tag = dwarf_tag(&type);
+
+ if (field->name[0] == '[' &&
+ (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
+ if (field->next)
+ /* Save original type for next field */
+ memcpy(die_mem, &type, sizeof(*die_mem));
+ /* Get the type of this array */
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }
+ pr_debug2("Array real type: (%x)\n",
+ (unsigned)dwarf_dieoffset(&type));
+ if (tag == DW_TAG_pointer_type) {
+ ref = zalloc(sizeof(struct probe_trace_arg_ref));
+ if (ref == NULL)
+ return -ENOMEM;
+ if (*ref_ptr)
+ (*ref_ptr)->next = ref;
+ else
+ *ref_ptr = ref;
+ }
+ ref->offset += die_get_byte_size(&type) * field->index;
+ if (!field->next)
+ /* Save vr_die for converting types */
+ memcpy(die_mem, vr_die, sizeof(*die_mem));
+ goto next;
+ } else if (tag == DW_TAG_pointer_type) {
+ /* Check the pointer and dereference */
+ if (!field->ref) {
+ pr_err("Semantic error: %s must be referred by '->'\n",
+ field->name);
+ return -EINVAL;
+ }
+ /* Get the type pointed by this pointer */
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }
+ /* Verify it is a data structure */
+ if (dwarf_tag(&type) != DW_TAG_structure_type) {
+ pr_warning("%s is not a data structure.\n", varname);
+ return -EINVAL;
+ }
+
+ ref = zalloc(sizeof(struct probe_trace_arg_ref));
+ if (ref == NULL)
+ return -ENOMEM;
+ if (*ref_ptr)
+ (*ref_ptr)->next = ref;
+ else
+ *ref_ptr = ref;
+ } else {
+ /* Verify it is a data structure */
+ if (tag != DW_TAG_structure_type) {
+ pr_warning("%s is not a data structure.\n", varname);
+ return -EINVAL;
+ }
+ if (field->name[0] == '[') {
+ pr_err("Semantic error: %s is not a pointor"
+ " nor array.\n", varname);
+ return -EINVAL;
+ }
+ if (field->ref) {
+ pr_err("Semantic error: %s must be referred by '.'\n",
+ field->name);
+ return -EINVAL;
+ }
+ if (!ref) {
+ pr_warning("Structure on a register is not "
+ "supported yet.\n");
+ return -ENOTSUP;
+ }
+ }
+
+ if (die_find_member(&type, field->name, die_mem) == NULL) {
+ pr_warning("%s(tyep:%s) has no member %s.\n", varname,
+ dwarf_diename(&type), field->name);
+ return -EINVAL;
+ }
+
+ /* Get the offset of the field */
+ ret = die_get_data_member_location(die_mem, &offs);
+ if (ret < 0) {
+ pr_warning("Failed to get the offset of %s.\n", field->name);
+ return ret;
+ }
+ ref->offset += (long)offs;
+
+next:
+ /* Converting next field */
+ if (field->next)
+ return convert_variable_fields(die_mem, field->name,
+ field->next, &ref, die_mem);
+ else
+ return 0;
+}
+
+/* Show a variables in kprobe event format */
+static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
+{
+ Dwarf_Die die_mem;
+ int ret;
+
+ pr_debug("Converting variable %s into trace event.\n",
+ dwarf_diename(vr_die));
+
+ ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
+ pf->tvar);
+ if (ret == -ENOENT)
+ pr_err("Failed to find the location of %s at this address.\n"
+ " Perhaps, it has been optimized out.\n", pf->pvar->var);
+ else if (ret == -ENOTSUP)
+ pr_err("Sorry, we don't support this variable location yet.\n");
+ else if (pf->pvar->field) {
+ ret = convert_variable_fields(vr_die, pf->pvar->var,
+ pf->pvar->field, &pf->tvar->ref,
+ &die_mem);
+ vr_die = &die_mem;
+ }
+ if (ret == 0)
+ ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
+ /* *expr will be cached in libdw. Don't free it. */
+ return ret;
+}
+
+/* Find a variable in a subprogram die */
+static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ Dwarf_Die vr_die, *scopes;
+ char buf[32], *ptr;
+ int ret, nscopes;
+
+ if (!is_c_varname(pf->pvar->var)) {
+ /* Copy raw parameters */
+ pf->tvar->value = strdup(pf->pvar->var);
+ if (pf->tvar->value == NULL)
+ return -ENOMEM;
+ if (pf->pvar->type) {
+ pf->tvar->type = strdup(pf->pvar->type);
+ if (pf->tvar->type == NULL)
+ return -ENOMEM;
+ }
+ if (pf->pvar->name) {
+ pf->tvar->name = strdup(pf->pvar->name);
+ if (pf->tvar->name == NULL)
+ return -ENOMEM;
+ } else
+ pf->tvar->name = NULL;
+ return 0;
+ }
+
+ if (pf->pvar->name)
+ pf->tvar->name = strdup(pf->pvar->name);
+ else {
+ ret = synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ if (ret < 0)
+ return ret;
+ ptr = strchr(buf, ':'); /* Change type separator to _ */
+ if (ptr)
+ *ptr = '_';
+ pf->tvar->name = strdup(buf);
+ }
+ if (pf->tvar->name == NULL)
+ return -ENOMEM;
+
+ pr_debug("Searching '%s' variable in context.\n",
+ pf->pvar->var);
+ /* Search child die for local variables and parameters. */
+ if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
+ ret = convert_variable(&vr_die, pf);
+ else {
+ /* Search upper class */
+ nscopes = dwarf_getscopes_die(sp_die, &scopes);
+ while (nscopes-- > 1) {
+ pr_debug("Searching variables in %s\n",
+ dwarf_diename(&scopes[nscopes]));
+ /* We should check this scope, so give dummy address */
+ if (die_find_variable_at(&scopes[nscopes],
+ pf->pvar->var, 0,
+ &vr_die)) {
+ ret = convert_variable(&vr_die, pf);
+ goto found;
+ }
+ }
+ if (scopes)
+ free(scopes);
+ ret = -ENOENT;
+ }
+found:
+ if (ret < 0)
+ pr_warning("Failed to find '%s' in this function.\n",
+ pf->pvar->var);
+ return ret;
+}
+
+/* Convert subprogram DIE to trace point */
+static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
+ bool retprobe, struct probe_trace_point *tp)
+{
+ Dwarf_Addr eaddr;
+ const char *name;
+
+ /* Copy the name of probe point */
+ name = dwarf_diename(sp_die);
+ if (name) {
+ if (dwarf_entrypc(sp_die, &eaddr) != 0) {
+ pr_warning("Failed to get entry address of %s\n",
+ dwarf_diename(sp_die));
+ return -ENOENT;
+ }
+ tp->symbol = strdup(name);
+ if (tp->symbol == NULL)
+ return -ENOMEM;
+ tp->offset = (unsigned long)(paddr - eaddr);
+ } else
+ /* This function has no name. */
+ tp->offset = (unsigned long)paddr;
+
+ /* Return probe must be on the head of a subprogram */
+ if (retprobe) {
+ if (eaddr != paddr) {
+ pr_warning("Return probe must be on the head of"
+ " a real function.\n");
+ return -EINVAL;
+ }
+ tp->retprobe = true;
+ }
+
+ return 0;
+}
+
+/* Call probe_finder callback with real subprogram DIE */
+static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ Dwarf_Die die_mem;
+ Dwarf_Attribute fb_attr;
+ size_t nops;
+ int ret;
+
+ /* If no real subprogram, find a real one */
+ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
+ sp_die = die_find_real_subprogram(&pf->cu_die,
+ pf->addr, &die_mem);
+ if (!sp_die) {
+ pr_warning("Failed to find probe point in any "
+ "functions.\n");
+ return -ENOENT;
+ }
+ }
+
+ /* Get the frame base attribute/ops */
+ dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
+ ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
+ if (ret <= 0 || nops == 0) {
+ pf->fb_ops = NULL;
+#if _ELFUTILS_PREREQ(0, 142)
+ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
+ pf->cfi != NULL) {
+ Dwarf_Frame *frame;
+ if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 ||
+ dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
+ pr_warning("Failed to get call frame on 0x%jx\n",
+ (uintmax_t)pf->addr);
+ return -ENOENT;
+ }
+#endif
+ }
+
+ /* Call finder's callback handler */
+ ret = pf->callback(sp_die, pf);
+
+ /* *pf->fb_ops will be cached in libdw. Don't free it. */
+ pf->fb_ops = NULL;
+
+ return ret;
+}
+
+/* Find probe point from its line number */
+static int find_probe_point_by_line(struct probe_finder *pf)
+{
+ Dwarf_Lines *lines;
+ Dwarf_Line *line;
+ size_t nlines, i;
+ Dwarf_Addr addr;
+ int lineno;
+ int ret = 0;
+
+ if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found.\n");
+ return -ENOENT;
+ }
+
+ for (i = 0; i < nlines && ret == 0; i++) {
+ line = dwarf_onesrcline(lines, i);
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ lineno != pf->lno)
+ continue;
+
+ /* TODO: Get fileno from line, but how? */
+ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
+ continue;
+
+ if (dwarf_lineaddr(line, &addr) != 0) {
+ pr_warning("Failed to get the address of the line.\n");
+ return -ENOENT;
+ }
+ pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
+ (int)i, lineno, (uintmax_t)addr);
+ pf->addr = addr;
+
+ ret = call_probe_finder(NULL, pf);
+ /* Continuing, because target line might be inlined. */
+ }
+ return ret;
+}
+
+/* Find lines which match lazy pattern */
+static int find_lazy_match_lines(struct list_head *head,
+ const char *fname, const char *pat)
+{
+ char *fbuf, *p1, *p2;
+ int fd, line, nlines = -1;
+ struct stat st;
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ pr_warning("Failed to open %s: %s\n", fname, strerror(-fd));
+ return -errno;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ pr_warning("Failed to get the size of %s: %s\n",
+ fname, strerror(errno));
+ nlines = -errno;
+ goto out_close;
+ }
+
+ nlines = -ENOMEM;
+ fbuf = malloc(st.st_size + 2);
+ if (fbuf == NULL)
+ goto out_close;
+ if (read(fd, fbuf, st.st_size) < 0) {
+ pr_warning("Failed to read %s: %s\n", fname, strerror(errno));
+ nlines = -errno;
+ goto out_free_fbuf;
+ }
+ fbuf[st.st_size] = '\n'; /* Dummy line */
+ fbuf[st.st_size + 1] = '\0';
+ p1 = fbuf;
+ line = 1;
+ nlines = 0;
+ while ((p2 = strchr(p1, '\n')) != NULL) {
+ *p2 = '\0';
+ if (strlazymatch(p1, pat)) {
+ line_list__add_line(head, line);
+ nlines++;
+ }
+ line++;
+ p1 = p2 + 1;
+ }
+out_free_fbuf:
+ free(fbuf);
+out_close:
+ close(fd);
+ return nlines;
+}
+
+/* Find probe points from lazy pattern */
+static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ Dwarf_Lines *lines;
+ Dwarf_Line *line;
+ size_t nlines, i;
+ Dwarf_Addr addr;
+ Dwarf_Die die_mem;
+ int lineno;
+ int ret = 0;
+
+ if (list_empty(&pf->lcache)) {
+ /* Matching lazy line pattern */
+ ret = find_lazy_match_lines(&pf->lcache, pf->fname,
+ pf->pev->point.lazy_line);
+ if (ret == 0) {
+ pr_debug("No matched lines found in %s.\n", pf->fname);
+ return 0;
+ } else if (ret < 0)
+ return ret;
+ }
+
+ if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found.\n");
+ return -ENOENT;
+ }
+
+ for (i = 0; i < nlines && ret >= 0; i++) {
+ line = dwarf_onesrcline(lines, i);
+
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ !line_list__has_line(&pf->lcache, lineno))
+ continue;
+
+ /* TODO: Get fileno from line, but how? */
+ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
+ continue;
+
+ if (dwarf_lineaddr(line, &addr) != 0) {
+ pr_debug("Failed to get the address of line %d.\n",
+ lineno);
+ continue;
+ }
+ if (sp_die) {
+ /* Address filtering 1: does sp_die include addr? */
+ if (!dwarf_haspc(sp_die, addr))
+ continue;
+ /* Address filtering 2: No child include addr? */
+ if (die_find_inlinefunc(sp_die, addr, &die_mem))
+ continue;
+ }
+
+ pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n",
+ (int)i, lineno, (unsigned long long)addr);
+ pf->addr = addr;
+
+ ret = call_probe_finder(sp_die, pf);
+ /* Continuing, because target line might be inlined. */
+ }
+ /* TODO: deallocate lines, but how? */
+ return ret;
+}
+
+/* Callback parameter with return value */
+struct dwarf_callback_param {
+ void *data;
+ int retval;
+};
+
+static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
+{
+ struct dwarf_callback_param *param = data;
+ struct probe_finder *pf = param->data;
+ struct perf_probe_point *pp = &pf->pev->point;
+ Dwarf_Addr addr;
+
+ if (pp->lazy_line)
+ param->retval = find_probe_point_lazy(in_die, pf);
+ else {
+ /* Get probe address */
+ if (dwarf_entrypc(in_die, &addr) != 0) {
+ pr_warning("Failed to get entry address of %s.\n",
+ dwarf_diename(in_die));
+ param->retval = -ENOENT;
+ return DWARF_CB_ABORT;
+ }
+ pf->addr = addr;
+ pf->addr += pp->offset;
+ pr_debug("found inline addr: 0x%jx\n",
+ (uintmax_t)pf->addr);
+
+ param->retval = call_probe_finder(in_die, pf);
+ if (param->retval < 0)
+ return DWARF_CB_ABORT;
+ }
+
+ return DWARF_CB_OK;
+}
+
+/* Search function from function name */
+static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
+{
+ struct dwarf_callback_param *param = data;
+ struct probe_finder *pf = param->data;
+ struct perf_probe_point *pp = &pf->pev->point;
+
+ /* Check tag and diename */
+ if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
+ !die_compare_name(sp_die, pp->function))
+ return DWARF_CB_OK;
+
+ pf->fname = dwarf_decl_file(sp_die);
+ if (pp->line) { /* Function relative line */
+ dwarf_decl_line(sp_die, &pf->lno);
+ pf->lno += pp->line;
+ param->retval = find_probe_point_by_line(pf);
+ } else if (!dwarf_func_inline(sp_die)) {
+ /* Real function */
+ if (pp->lazy_line)
+ param->retval = find_probe_point_lazy(sp_die, pf);
+ else {
+ if (dwarf_entrypc(sp_die, &pf->addr) != 0) {
+ pr_warning("Failed to get entry address of "
+ "%s.\n", dwarf_diename(sp_die));
+ param->retval = -ENOENT;
+ return DWARF_CB_ABORT;
+ }
+ pf->addr += pp->offset;
+ /* TODO: Check the address in this function */
+ param->retval = call_probe_finder(sp_die, pf);
+ }
+ } else {
+ struct dwarf_callback_param _param = {.data = (void *)pf,
+ .retval = 0};
+ /* Inlined function: search instances */
+ dwarf_func_inline_instances(sp_die, probe_point_inline_cb,
+ &_param);
+ param->retval = _param.retval;
+ }
+
+ return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
+}
+
+static int find_probe_point_by_func(struct probe_finder *pf)
+{
+ struct dwarf_callback_param _param = {.data = (void *)pf,
+ .retval = 0};
+ dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
+ return _param.retval;
+}
+
+/* Find probe points from debuginfo */
+static int find_probes(int fd, struct probe_finder *pf)
+{
+ struct perf_probe_point *pp = &pf->pev->point;
+ Dwarf_Off off, noff;
+ size_t cuhl;
+ Dwarf_Die *diep;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl;
+ Dwarf_Addr bias; /* Currently ignored */
+ int ret = 0;
+
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
+ if (!dbg) {
+ pr_warning("No debug information found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ return -EBADF;
+ }
+
+#if _ELFUTILS_PREREQ(0, 142)
+ /* Get the call frame information from this dwarf */
+ pf->cfi = dwarf_getcfi(dbg);
+#endif
+
+ off = 0;
+ line_list__init(&pf->lcache);
+ /* Loop on CUs (Compilation Unit) */
+ while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
+ ret >= 0) {
+ /* Get the DIE(Debugging Information Entry) of this CU */
+ diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
+ if (!diep)
+ continue;
+
+ /* Check if target file is included. */
+ if (pp->file)
+ pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
+ else
+ pf->fname = NULL;
+
+ if (!pp->file || pf->fname) {
+ if (pp->function)
+ ret = find_probe_point_by_func(pf);
+ else if (pp->lazy_line)
+ ret = find_probe_point_lazy(NULL, pf);
+ else {
+ pf->lno = pp->line;
+ ret = find_probe_point_by_line(pf);
+ }
+ }
+ off = noff;
+ }
+ line_list__free(&pf->lcache);
+ if (dwfl)
+ dwfl_end(dwfl);
+
+ return ret;
+}
+
+/* Add a found probe point into trace event list */
+static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ struct trace_event_finder *tf =
+ container_of(pf, struct trace_event_finder, pf);
+ struct probe_trace_event *tev;
+ int ret, i;
+
+ /* Check number of tevs */
+ if (tf->ntevs == tf->max_tevs) {
+ pr_warning("Too many( > %d) probe point found.\n",
+ tf->max_tevs);
+ return -ERANGE;
+ }
+ tev = &tf->tevs[tf->ntevs++];
+
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+ &tev->point);
+ if (ret < 0)
+ return ret;
+
+ pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
+ tev->point.offset);
+
+ /* Find each argument */
+ tev->nargs = pf->pev->nargs;
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
+ if (tev->args == NULL)
+ return -ENOMEM;
+ for (i = 0; i < pf->pev->nargs; i++) {
+ pf->pvar = &pf->pev->args[i];
+ pf->tvar = &tev->args[i];
+ ret = find_variable(sp_die, pf);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Find probe_trace_events specified by perf_probe_event from debuginfo */
+int find_probe_trace_events(int fd, struct perf_probe_event *pev,
+ struct probe_trace_event **tevs, int max_tevs)
+{
+ struct trace_event_finder tf = {
+ .pf = {.pev = pev, .callback = add_probe_trace_event},
+ .max_tevs = max_tevs};
+ int ret;
+
+ /* Allocate result tevs array */
+ *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
+ if (*tevs == NULL)
+ return -ENOMEM;
+
+ tf.tevs = *tevs;
+ tf.ntevs = 0;
+
+ ret = find_probes(fd, &tf.pf);
+ if (ret < 0) {
+ free(*tevs);
+ *tevs = NULL;
+ return ret;
+ }
+
+ return (ret < 0) ? ret : tf.ntevs;
+}
+
+#define MAX_VAR_LEN 64
+
+/* Collect available variables in this scope */
+static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
+{
+ struct available_var_finder *af = data;
+ struct variable_list *vl;
+ char buf[MAX_VAR_LEN];
+ int tag, ret;
+
+ vl = &af->vls[af->nvls - 1];
+
+ tag = dwarf_tag(die_mem);
+ if (tag == DW_TAG_formal_parameter ||
+ tag == DW_TAG_variable) {
+ ret = convert_variable_location(die_mem, af->pf.addr,
+ af->pf.fb_ops, NULL);
+ if (ret == 0) {
+ ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
+ pr_debug2("Add new var: %s\n", buf);
+ if (ret > 0)
+ strlist__add(vl->vars, buf);
+ }
+ }
+
+ if (af->child && dwarf_haspc(die_mem, af->pf.addr))
+ return DIE_FIND_CB_CONTINUE;
+ else
+ return DIE_FIND_CB_SIBLING;
+}
+
+/* Add a found vars into available variables list */
+static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ struct available_var_finder *af =
+ container_of(pf, struct available_var_finder, pf);
+ struct variable_list *vl;
+ Dwarf_Die die_mem, *scopes = NULL;
+ int ret, nscopes;
+
+ /* Check number of tevs */
+ if (af->nvls == af->max_vls) {
+ pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
+ return -ERANGE;
+ }
+ vl = &af->vls[af->nvls++];
+
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+ &vl->point);
+ if (ret < 0)
+ return ret;
+
+ pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
+ vl->point.offset);
+
+ /* Find local variables */
+ vl->vars = strlist__new(true, NULL);
+ if (vl->vars == NULL)
+ return -ENOMEM;
+ af->child = true;
+ die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
+
+ /* Find external variables */
+ if (!af->externs)
+ goto out;
+ /* Don't need to search child DIE for externs. */
+ af->child = false;
+ nscopes = dwarf_getscopes_die(sp_die, &scopes);
+ while (nscopes-- > 1)
+ die_find_child(&scopes[nscopes], collect_variables_cb,
+ (void *)af, &die_mem);
+ if (scopes)
+ free(scopes);
+
+out:
+ if (strlist__empty(vl->vars)) {
+ strlist__delete(vl->vars);
+ vl->vars = NULL;
+ }
+
+ return ret;
+}
+
+/* Find available variables at given probe point */
+int find_available_vars_at(int fd, struct perf_probe_event *pev,
+ struct variable_list **vls, int max_vls,
+ bool externs)
+{
+ struct available_var_finder af = {
+ .pf = {.pev = pev, .callback = add_available_vars},
+ .max_vls = max_vls, .externs = externs};
+ int ret;
+
+ /* Allocate result vls array */
+ *vls = zalloc(sizeof(struct variable_list) * max_vls);
+ if (*vls == NULL)
+ return -ENOMEM;
+
+ af.vls = *vls;
+ af.nvls = 0;
+
+ ret = find_probes(fd, &af.pf);
+ if (ret < 0) {
+ /* Free vlist for error */
+ while (af.nvls--) {
+ if (af.vls[af.nvls].point.symbol)
+ free(af.vls[af.nvls].point.symbol);
+ if (af.vls[af.nvls].vars)
+ strlist__delete(af.vls[af.nvls].vars);
+ }
+ free(af.vls);
+ *vls = NULL;
+ return ret;
+ }
+
+ return (ret < 0) ? ret : af.nvls;
+}
+
+/* Reverse search */
+int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
+{
+ Dwarf_Die cudie, spdie, indie;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl = NULL;
+ Dwarf_Line *line;
+ Dwarf_Addr laddr, eaddr, bias = 0;
+ const char *tmp;
+ int lineno, ret = 0;
+ bool found = false;
+
+ /* Open the live linux kernel */
+ dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
+ if (!dbg) {
+ pr_warning("No debug information found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Adjust address with bias */
+ addr += bias;
+ /* Find cu die */
+ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
+ pr_warning("Failed to find debug information for address %lx\n",
+ addr);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Find a corresponding line */
+ line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr);
+ if (line) {
+ if (dwarf_lineaddr(line, &laddr) == 0 &&
+ (Dwarf_Addr)addr == laddr &&
+ dwarf_lineno(line, &lineno) == 0) {
+ tmp = dwarf_linesrc(line, NULL, NULL);
+ if (tmp) {
+ ppt->line = lineno;
+ ppt->file = strdup(tmp);
+ if (ppt->file == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ found = true;
+ }
+ }
+ }
+
+ /* Find a corresponding function */
+ if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) {
+ tmp = dwarf_diename(&spdie);
+ if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0)
+ goto end;
+
+ if (ppt->line) {
+ if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
+ &indie)) {
+ /* addr in an inline function */
+ tmp = dwarf_diename(&indie);
+ if (!tmp)
+ goto end;
+ ret = dwarf_decl_line(&indie, &lineno);
+ } else {
+ if (eaddr == addr) { /* Function entry */
+ lineno = ppt->line;
+ ret = 0;
+ } else
+ ret = dwarf_decl_line(&spdie, &lineno);
+ }
+ if (ret == 0) {
+ /* Make a relative line number */
+ ppt->line -= lineno;
+ goto found;
+ }
+ }
+ /* We don't have a line number, let's use offset */
+ ppt->offset = addr - (unsigned long)eaddr;
+found:
+ ppt->function = strdup(tmp);
+ if (ppt->function == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ found = true;
+ }
+
+end:
+ if (dwfl)
+ dwfl_end(dwfl);
+ if (ret >= 0)
+ ret = found ? 1 : 0;
+ return ret;
+}
+
+/* Add a line and store the src path */
+static int line_range_add_line(const char *src, unsigned int lineno,
+ struct line_range *lr)
+{
+ /* Copy source path */
+ if (!lr->path) {
+ lr->path = strdup(src);
+ if (lr->path == NULL)
+ return -ENOMEM;
+ }
+ return line_list__add_line(&lr->line_list, lineno);
+}
+
+/* Search function declaration lines */
+static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data)
+{
+ struct dwarf_callback_param *param = data;
+ struct line_finder *lf = param->data;
+ const char *src;
+ int lineno;
+
+ src = dwarf_decl_file(sp_die);
+ if (src && strtailcmp(src, lf->fname) != 0)
+ return DWARF_CB_OK;
+
+ if (dwarf_decl_line(sp_die, &lineno) != 0 ||
+ (lf->lno_s > lineno || lf->lno_e < lineno))
+ return DWARF_CB_OK;
+
+ param->retval = line_range_add_line(src, lineno, lf->lr);
+ if (param->retval < 0)
+ return DWARF_CB_ABORT;
+ return DWARF_CB_OK;
+}
+
+static int find_line_range_func_decl_lines(struct line_finder *lf)
+{
+ struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
+ dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, &param, 0);
+ return param.retval;
+}
+
+/* Find line range from its line number */
+static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
+{
+ Dwarf_Lines *lines;
+ Dwarf_Line *line;
+ size_t nlines, i;
+ Dwarf_Addr addr;
+ int lineno, ret = 0;
+ const char *src;
+ Dwarf_Die die_mem;
+
+ line_list__init(&lf->lr->line_list);
+ if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found.\n");
+ return -ENOENT;
+ }
+
+ /* Search probable lines on lines list */
+ for (i = 0; i < nlines; i++) {
+ line = dwarf_onesrcline(lines, i);
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ (lf->lno_s > lineno || lf->lno_e < lineno))
+ continue;
+
+ if (sp_die) {
+ /* Address filtering 1: does sp_die include addr? */
+ if (dwarf_lineaddr(line, &addr) != 0 ||
+ !dwarf_haspc(sp_die, addr))
+ continue;
+
+ /* Address filtering 2: No child include addr? */
+ if (die_find_inlinefunc(sp_die, addr, &die_mem))
+ continue;
+ }
+
+ /* TODO: Get fileno from line, but how? */
+ src = dwarf_linesrc(line, NULL, NULL);
+ if (strtailcmp(src, lf->fname) != 0)
+ continue;
+
+ ret = line_range_add_line(src, lineno, lf->lr);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * Dwarf lines doesn't include function declarations. We have to
+ * check functions list or given function.
+ */
+ if (sp_die) {
+ src = dwarf_decl_file(sp_die);
+ if (src && dwarf_decl_line(sp_die, &lineno) == 0 &&
+ (lf->lno_s <= lineno && lf->lno_e >= lineno))
+ ret = line_range_add_line(src, lineno, lf->lr);
+ } else
+ ret = find_line_range_func_decl_lines(lf);
+
+ /* Update status */
+ if (ret >= 0)
+ if (!list_empty(&lf->lr->line_list))
+ ret = lf->found = 1;
+ else
+ ret = 0; /* Lines are not found */
+ else {
+ free(lf->lr->path);
+ lf->lr->path = NULL;
+ }
+ return ret;
+}
+
+static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
+{
+ struct dwarf_callback_param *param = data;
+
+ param->retval = find_line_range_by_line(in_die, param->data);
+ return DWARF_CB_ABORT; /* No need to find other instances */
+}
+
+/* Search function from function name */
+static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
+{
+ struct dwarf_callback_param *param = data;
+ struct line_finder *lf = param->data;
+ struct line_range *lr = lf->lr;
+
+ pr_debug("find (%llx) %s\n",
+ (unsigned long long)dwarf_dieoffset(sp_die),
+ dwarf_diename(sp_die));
+ if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
+ die_compare_name(sp_die, lr->function)) {
+ lf->fname = dwarf_decl_file(sp_die);
+ dwarf_decl_line(sp_die, &lr->offset);
+ pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
+ lf->lno_s = lr->offset + lr->start;
+ if (lf->lno_s < 0) /* Overflow */
+ lf->lno_s = INT_MAX;
+ lf->lno_e = lr->offset + lr->end;
+ if (lf->lno_e < 0) /* Overflow */
+ lf->lno_e = INT_MAX;
+ pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e);
+ lr->start = lf->lno_s;
+ lr->end = lf->lno_e;
+ if (dwarf_func_inline(sp_die)) {
+ struct dwarf_callback_param _param;
+ _param.data = (void *)lf;
+ _param.retval = 0;
+ dwarf_func_inline_instances(sp_die,
+ line_range_inline_cb,
+ &_param);
+ param->retval = _param.retval;
+ } else
+ param->retval = find_line_range_by_line(sp_die, lf);
+ return DWARF_CB_ABORT;
+ }
+ return DWARF_CB_OK;
+}
+
+static int find_line_range_by_func(struct line_finder *lf)
+{
+ struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
+ dwarf_getfuncs(&lf->cu_die, line_range_search_cb, &param, 0);
+ return param.retval;
+}
+
+int find_line_range(int fd, struct line_range *lr)
+{
+ struct line_finder lf = {.lr = lr, .found = 0};
+ int ret = 0;
+ Dwarf_Off off = 0, noff;
+ size_t cuhl;
+ Dwarf_Die *diep;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl;
+ Dwarf_Addr bias; /* Currently ignored */
+ const char *comp_dir;
+
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
+ if (!dbg) {
+ pr_warning("No debug information found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ return -EBADF;
+ }
+
+ /* Loop on CUs (Compilation Unit) */
+ while (!lf.found && ret >= 0) {
+ if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0)
+ break;
+
+ /* Get the DIE(Debugging Information Entry) of this CU */
+ diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die);
+ if (!diep)
+ continue;
+
+ /* Check if target file is included. */
+ if (lr->file)
+ lf.fname = cu_find_realpath(&lf.cu_die, lr->file);
+ else
+ lf.fname = 0;
+
+ if (!lr->file || lf.fname) {
+ if (lr->function)
+ ret = find_line_range_by_func(&lf);
+ else {
+ lf.lno_s = lr->start;
+ lf.lno_e = lr->end;
+ ret = find_line_range_by_line(NULL, &lf);
+ }
+ }
+ off = noff;
+ }
+
+ /* Store comp_dir */
+ if (lf.found) {
+ comp_dir = cu_get_comp_dir(&lf.cu_die);
+ if (comp_dir) {
+ lr->comp_dir = strdup(comp_dir);
+ if (!lr->comp_dir)
+ ret = -ENOMEM;
+ }
+ }
+
+ pr_debug("path: %s\n", lr->path);
+ dwfl_end(dwfl);
+ return (ret < 0) ? ret : lf.found;
+}
+
diff --git a/smartt-perf/util/probe-finder.h b/smartt-perf/util/probe-finder.h
new file mode 100644
index 0000000..beaefc3
--- /dev/null
+++ b/smartt-perf/util/probe-finder.h
@@ -0,0 +1,91 @@
+#ifndef _PROBE_FINDER_H
+#define _PROBE_FINDER_H
+
+#include <stdbool.h>
+#include "util.h"
+#include "probe-event.h"
+
+#define MAX_PATH_LEN 256
+#define MAX_PROBE_BUFFER 1024
+#define MAX_PROBES 128
+
+static inline int is_c_varname(const char *name)
+{
+ /* TODO */
+ return isalpha(name[0]) || name[0] == '_';
+}
+
+#ifdef DWARF_SUPPORT
+/* Find probe_trace_events specified by perf_probe_event from debuginfo */
+extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
+ struct probe_trace_event **tevs,
+ int max_tevs);
+
+/* Find a perf_probe_point from debuginfo */
+extern int find_perf_probe_point(unsigned long addr,
+ struct perf_probe_point *ppt);
+
+/* Find a line range */
+extern int find_line_range(int fd, struct line_range *lr);
+
+/* Find available variables */
+extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
+ struct variable_list **vls, int max_points,
+ bool externs);
+
+#include <dwarf.h>
+#include <elfutils/libdw.h>
+#include <elfutils/libdwfl.h>
+#include <elfutils/version.h>
+
+struct probe_finder {
+ struct perf_probe_event *pev; /* Target probe event */
+
+ /* Callback when a probe point is found */
+ int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
+
+ /* For function searching */
+ int lno; /* Line number */
+ Dwarf_Addr addr; /* Address */
+ const char *fname; /* Real file name */
+ Dwarf_Die cu_die; /* Current CU */
+ struct list_head lcache; /* Line cache for lazy match */
+
+ /* For variable searching */
+#if _ELFUTILS_PREREQ(0, 142)
+ Dwarf_CFI *cfi; /* Call Frame Information */
+#endif
+ Dwarf_Op *fb_ops; /* Frame base attribute */
+ struct perf_probe_arg *pvar; /* Current target variable */
+ struct probe_trace_arg *tvar; /* Current result variable */
+};
+
+struct trace_event_finder {
+ struct probe_finder pf;
+ struct probe_trace_event *tevs; /* Found trace events */
+ int ntevs; /* Number of trace events */
+ int max_tevs; /* Max number of trace events */
+};
+
+struct available_var_finder {
+ struct probe_finder pf;
+ struct variable_list *vls; /* Found variable lists */
+ int nvls; /* Number of variable lists */
+ int max_vls; /* Max no. of variable lists */
+ bool externs; /* Find external vars too */
+ bool child; /* Search child scopes */
+};
+
+struct line_finder {
+ struct line_range *lr; /* Target line range */
+
+ const char *fname; /* File name */
+ int lno_s; /* Start line number */
+ int lno_e; /* End line number */
+ Dwarf_Die cu_die; /* Current CU */
+ int found;
+};
+
+#endif /* DWARF_SUPPORT */
+
+#endif /*_PROBE_FINDER_H */
diff --git a/smartt-perf/util/pstack.c b/smartt-perf/util/pstack.c
new file mode 100644
index 0000000..13d36fa
--- /dev/null
+++ b/smartt-perf/util/pstack.c
@@ -0,0 +1,75 @@
+/*
+ * Simple pointer stack
+ *
+ * (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include "util.h"
+#include "pstack.h"
+#include <linux/kernel.h>
+#include <stdlib.h>
+
+struct pstack {
+ unsigned short top;
+ unsigned short max_nr_entries;
+ void *entries[0];
+};
+
+struct pstack *pstack__new(unsigned short max_nr_entries)
+{
+ struct pstack *self = zalloc((sizeof(*self) +
+ max_nr_entries * sizeof(void *)));
+ if (self != NULL)
+ self->max_nr_entries = max_nr_entries;
+ return self;
+}
+
+void pstack__delete(struct pstack *self)
+{
+ free(self);
+}
+
+bool pstack__empty(const struct pstack *self)
+{
+ return self->top == 0;
+}
+
+void pstack__remove(struct pstack *self, void *key)
+{
+ unsigned short i = self->top, last_index = self->top - 1;
+
+ while (i-- != 0) {
+ if (self->entries[i] == key) {
+ if (i < last_index)
+ memmove(self->entries + i,
+ self->entries + i + 1,
+ (last_index - i) * sizeof(void *));
+ --self->top;
+ return;
+ }
+ }
+ pr_err("%s: %p not on the pstack!\n", __func__, key);
+}
+
+void pstack__push(struct pstack *self, void *key)
+{
+ if (self->top == self->max_nr_entries) {
+ pr_err("%s: top=%d, overflow!\n", __func__, self->top);
+ return;
+ }
+ self->entries[self->top++] = key;
+}
+
+void *pstack__pop(struct pstack *self)
+{
+ void *ret;
+
+ if (self->top == 0) {
+ pr_err("%s: underflow!\n", __func__);
+ return NULL;
+ }
+
+ ret = self->entries[--self->top];
+ self->entries[self->top] = NULL;
+ return ret;
+}
diff --git a/smartt-perf/util/pstack.h b/smartt-perf/util/pstack.h
new file mode 100644
index 0000000..4cedea5
--- /dev/null
+++ b/smartt-perf/util/pstack.h
@@ -0,0 +1,14 @@
+#ifndef _PERF_PSTACK_
+#define _PERF_PSTACK_
+
+#include <stdbool.h>
+
+struct pstack;
+struct pstack *pstack__new(unsigned short max_nr_entries);
+void pstack__delete(struct pstack *self);
+bool pstack__empty(const struct pstack *self);
+void pstack__remove(struct pstack *self, void *key);
+void pstack__push(struct pstack *self, void *key);
+void *pstack__pop(struct pstack *self);
+
+#endif /* _PERF_PSTACK_ */
diff --git a/smartt-perf/util/quote.c b/smartt-perf/util/quote.c
new file mode 100644
index 0000000..01f0324
--- /dev/null
+++ b/smartt-perf/util/quote.c
@@ -0,0 +1,54 @@
+#include "cache.h"
+#include "quote.h"
+
+/* Help to copy the thing properly quoted for the shell safety.
+ * any single quote is replaced with '\'', any exclamation point
+ * is replaced with '\!', and the whole thing is enclosed in a
+ *
+ * E.g.
+ * original sq_quote result
+ * name ==> name ==> 'name'
+ * a b ==> a b ==> 'a b'
+ * a'b ==> a'\''b ==> 'a'\''b'
+ * a!b ==> a'\!'b ==> 'a'\!'b'
+ */
+static inline int need_bs_quote(char c)
+{
+ return (c == '\'' || c == '!');
+}
+
+static void sq_quote_buf(struct strbuf *dst, const char *src)
+{
+ char *to_free = NULL;
+
+ if (dst->buf == src)
+ to_free = strbuf_detach(dst, NULL);
+
+ strbuf_addch(dst, '\'');
+ while (*src) {
+ size_t len = strcspn(src, "'!");
+ strbuf_add(dst, src, len);
+ src += len;
+ while (need_bs_quote(*src)) {
+ strbuf_addstr(dst, "'\\");
+ strbuf_addch(dst, *src++);
+ strbuf_addch(dst, '\'');
+ }
+ }
+ strbuf_addch(dst, '\'');
+ free(to_free);
+}
+
+void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
+{
+ int i;
+
+ /* Copy into destination buffer. */
+ strbuf_grow(dst, 255);
+ for (i = 0; argv[i]; ++i) {
+ strbuf_addch(dst, ' ');
+ sq_quote_buf(dst, argv[i]);
+ if (maxlen && dst->len > maxlen)
+ die("Too many or long arguments");
+ }
+}
diff --git a/smartt-perf/util/quote.h b/smartt-perf/util/quote.h
new file mode 100644
index 0000000..172889e
--- /dev/null
+++ b/smartt-perf/util/quote.h
@@ -0,0 +1,29 @@
+#ifndef __PERF_QUOTE_H
+#define __PERF_QUOTE_H
+
+#include <stddef.h>
+#include <stdio.h>
+
+/* Help to copy the thing properly quoted for the shell safety.
+ * any single quote is replaced with '\'', any exclamation point
+ * is replaced with '\!', and the whole thing is enclosed in a
+ * single quote pair.
+ *
+ * For example, if you are passing the result to system() as an
+ * argument:
+ *
+ * sprintf(cmd, "foobar %s %s", sq_quote(arg0), sq_quote(arg1))
+ *
+ * would be appropriate. If the system() is going to call ssh to
+ * run the command on the other side:
+ *
+ * sprintf(cmd, "git-diff-tree %s %s", sq_quote(arg0), sq_quote(arg1));
+ * sprintf(rcmd, "ssh %s %s", sq_util/quote.host), sq_quote(cmd));
+ *
+ * Note that the above examples leak memory! Remember to free result from
+ * sq_quote() in a real application.
+ */
+
+extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
+
+#endif /* __PERF_QUOTE_H */
diff --git a/smartt-perf/util/rbtree.c b/smartt-perf/util/rbtree.c
new file mode 100644
index 0000000..a16be19
--- /dev/null
+++ b/smartt-perf/util/rbtree.c
@@ -0,0 +1,462 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+ (C) 2002 David Woodhouse <dwmw2@infradead.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ linux/lib/rbtree.c
+*/
+
+#include <linux/rbtree.h>
+#include <linux/module.h>
+
+static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *right = node->rb_right;
+ struct rb_node *parent = rb_parent(node);
+
+ if ((node->rb_right = right->rb_left))
+ rb_set_parent(right->rb_left, node);
+ right->rb_left = node;
+
+ rb_set_parent(right, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_left)
+ parent->rb_left = right;
+ else
+ parent->rb_right = right;
+ }
+ else
+ root->rb_node = right;
+ rb_set_parent(node, right);
+}
+
+static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *left = node->rb_left;
+ struct rb_node *parent = rb_parent(node);
+
+ if ((node->rb_left = left->rb_right))
+ rb_set_parent(left->rb_right, node);
+ left->rb_right = node;
+
+ rb_set_parent(left, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_right)
+ parent->rb_right = left;
+ else
+ parent->rb_left = left;
+ }
+ else
+ root->rb_node = left;
+ rb_set_parent(node, left);
+}
+
+void rb_insert_color(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *parent, *gparent;
+
+ while ((parent = rb_parent(node)) && rb_is_red(parent))
+ {
+ gparent = rb_parent(parent);
+
+ if (parent == gparent->rb_left)
+ {
+ {
+ register struct rb_node *uncle = gparent->rb_right;
+ if (uncle && rb_is_red(uncle))
+ {
+ rb_set_black(uncle);
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_right == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_left(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ __rb_rotate_right(gparent, root);
+ } else {
+ {
+ register struct rb_node *uncle = gparent->rb_left;
+ if (uncle && rb_is_red(uncle))
+ {
+ rb_set_black(uncle);
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_left == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_right(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ __rb_rotate_left(gparent, root);
+ }
+ }
+
+ rb_set_black(root->rb_node);
+}
+EXPORT_SYMBOL(rb_insert_color);
+
+static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
+ struct rb_root *root)
+{
+ struct rb_node *other;
+
+ while ((!node || rb_is_black(node)) && node != root->rb_node)
+ {
+ if (parent->rb_left == node)
+ {
+ other = parent->rb_right;
+ if (rb_is_red(other))
+ {
+ rb_set_black(other);
+ rb_set_red(parent);
+ __rb_rotate_left(parent, root);
+ other = parent->rb_right;
+ }
+ if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+ (!other->rb_right || rb_is_black(other->rb_right)))
+ {
+ rb_set_red(other);
+ node = parent;
+ parent = rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_right || rb_is_black(other->rb_right))
+ {
+ rb_set_black(other->rb_left);
+ rb_set_red(other);
+ __rb_rotate_right(other, root);
+ other = parent->rb_right;
+ }
+ rb_set_color(other, rb_color(parent));
+ rb_set_black(parent);
+ rb_set_black(other->rb_right);
+ __rb_rotate_left(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ else
+ {
+ other = parent->rb_left;
+ if (rb_is_red(other))
+ {
+ rb_set_black(other);
+ rb_set_red(parent);
+ __rb_rotate_right(parent, root);
+ other = parent->rb_left;
+ }
+ if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+ (!other->rb_right || rb_is_black(other->rb_right)))
+ {
+ rb_set_red(other);
+ node = parent;
+ parent = rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_left || rb_is_black(other->rb_left))
+ {
+ rb_set_black(other->rb_right);
+ rb_set_red(other);
+ __rb_rotate_left(other, root);
+ other = parent->rb_left;
+ }
+ rb_set_color(other, rb_color(parent));
+ rb_set_black(parent);
+ rb_set_black(other->rb_left);
+ __rb_rotate_right(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ }
+ if (node)
+ rb_set_black(node);
+}
+
+void rb_erase(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *child, *parent;
+ int color;
+
+ if (!node->rb_left)
+ child = node->rb_right;
+ else if (!node->rb_right)
+ child = node->rb_left;
+ else
+ {
+ struct rb_node *old = node, *left;
+
+ node = node->rb_right;
+ while ((left = node->rb_left) != NULL)
+ node = left;
+
+ if (rb_parent(old)) {
+ if (rb_parent(old)->rb_left == old)
+ rb_parent(old)->rb_left = node;
+ else
+ rb_parent(old)->rb_right = node;
+ } else
+ root->rb_node = node;
+
+ child = node->rb_right;
+ parent = rb_parent(node);
+ color = rb_color(node);
+
+ if (parent == old) {
+ parent = node;
+ } else {
+ if (child)
+ rb_set_parent(child, parent);
+ parent->rb_left = child;
+
+ node->rb_right = old->rb_right;
+ rb_set_parent(old->rb_right, node);
+ }
+
+ node->rb_parent_color = old->rb_parent_color;
+ node->rb_left = old->rb_left;
+ rb_set_parent(old->rb_left, node);
+
+ goto color;
+ }
+
+ parent = rb_parent(node);
+ color = rb_color(node);
+
+ if (child)
+ rb_set_parent(child, parent);
+ if (parent)
+ {
+ if (parent->rb_left == node)
+ parent->rb_left = child;
+ else
+ parent->rb_right = child;
+ }
+ else
+ root->rb_node = child;
+
+ color:
+ if (color == RB_BLACK)
+ __rb_erase_color(child, parent, root);
+}
+EXPORT_SYMBOL(rb_erase);
+
+static void rb_augment_path(struct rb_node *node, rb_augment_f func, void *data)
+{
+ struct rb_node *parent;
+
+up:
+ func(node, data);
+ parent = rb_parent(node);
+ if (!parent)
+ return;
+
+ if (node == parent->rb_left && parent->rb_right)
+ func(parent->rb_right, data);
+ else if (parent->rb_left)
+ func(parent->rb_left, data);
+
+ node = parent;
+ goto up;
+}
+
+/*
+ * after inserting @node into the tree, update the tree to account for
+ * both the new entry and any damage done by rebalance
+ */
+void rb_augment_insert(struct rb_node *node, rb_augment_f func, void *data)
+{
+ if (node->rb_left)
+ node = node->rb_left;
+ else if (node->rb_right)
+ node = node->rb_right;
+
+ rb_augment_path(node, func, data);
+}
+EXPORT_SYMBOL(rb_augment_insert);
+
+/*
+ * before removing the node, find the deepest node on the rebalance path
+ * that will still be there after @node gets removed
+ */
+struct rb_node *rb_augment_erase_begin(struct rb_node *node)
+{
+ struct rb_node *deepest;
+
+ if (!node->rb_right && !node->rb_left)
+ deepest = rb_parent(node);
+ else if (!node->rb_right)
+ deepest = node->rb_left;
+ else if (!node->rb_left)
+ deepest = node->rb_right;
+ else {
+ deepest = rb_next(node);
+ if (deepest->rb_right)
+ deepest = deepest->rb_right;
+ else if (rb_parent(deepest) != node)
+ deepest = rb_parent(deepest);
+ }
+
+ return deepest;
+}
+EXPORT_SYMBOL(rb_augment_erase_begin);
+
+/*
+ * after removal, update the tree to account for the removed entry
+ * and any rebalance damage.
+ */
+void rb_augment_erase_end(struct rb_node *node, rb_augment_f func, void *data)
+{
+ if (node)
+ rb_augment_path(node, func, data);
+}
+EXPORT_SYMBOL(rb_augment_erase_end);
+
+/*
+ * This function returns the first node (in sort order) of the tree.
+ */
+struct rb_node *rb_first(const struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_left)
+ n = n->rb_left;
+ return n;
+}
+EXPORT_SYMBOL(rb_first);
+
+struct rb_node *rb_last(const struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_right)
+ n = n->rb_right;
+ return n;
+}
+EXPORT_SYMBOL(rb_last);
+
+struct rb_node *rb_next(const struct rb_node *node)
+{
+ struct rb_node *parent;
+
+ if (rb_parent(node) == node)
+ return NULL;
+
+ /* If we have a right-hand child, go down and then left as far
+ as we can. */
+ if (node->rb_right) {
+ node = node->rb_right;
+ while (node->rb_left)
+ node=node->rb_left;
+ return (struct rb_node *)node;
+ }
+
+ /* No right-hand children. Everything down and left is
+ smaller than us, so any 'next' node must be in the general
+ direction of our parent. Go up the tree; any time the
+ ancestor is a right-hand child of its parent, keep going
+ up. First time it's a left-hand child of its parent, said
+ parent is our 'next' node. */
+ while ((parent = rb_parent(node)) && node == parent->rb_right)
+ node = parent;
+
+ return parent;
+}
+EXPORT_SYMBOL(rb_next);
+
+struct rb_node *rb_prev(const struct rb_node *node)
+{
+ struct rb_node *parent;
+
+ if (rb_parent(node) == node)
+ return NULL;
+
+ /* If we have a left-hand child, go down and then right as far
+ as we can. */
+ if (node->rb_left) {
+ node = node->rb_left;
+ while (node->rb_right)
+ node=node->rb_right;
+ return (struct rb_node *)node;
+ }
+
+ /* No left-hand children. Go up till we find an ancestor which
+ is a right-hand child of its parent */
+ while ((parent = rb_parent(node)) && node == parent->rb_left)
+ node = parent;
+
+ return parent;
+}
+EXPORT_SYMBOL(rb_prev);
+
+void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root)
+{
+ struct rb_node *parent = rb_parent(victim);
+
+ /* Set the surrounding nodes to point to the replacement */
+ if (parent) {
+ if (victim == parent->rb_left)
+ parent->rb_left = new;
+ else
+ parent->rb_right = new;
+ } else {
+ root->rb_node = new;
+ }
+ if (victim->rb_left)
+ rb_set_parent(victim->rb_left, new);
+ if (victim->rb_right)
+ rb_set_parent(victim->rb_right, new);
+
+ /* Copy the pointers/colour from the victim to the replacement */
+ *new = *victim;
+}
+EXPORT_SYMBOL(rb_replace_node);
diff --git a/smartt-perf/util/run-command.c b/smartt-perf/util/run-command.c
new file mode 100644
index 0000000..da8e9b2
--- /dev/null
+++ b/smartt-perf/util/run-command.c
@@ -0,0 +1,214 @@
+#include "cache.h"
+#include "run-command.h"
+#include "exec_cmd.h"
+
+static inline void close_pair(int fd[2])
+{
+ close(fd[0]);
+ close(fd[1]);
+}
+
+static inline void dup_devnull(int to)
+{
+ int fd = open("/dev/null", O_RDWR);
+ dup2(fd, to);
+ close(fd);
+}
+
+int start_command(struct child_process *cmd)
+{
+ int need_in, need_out, need_err;
+ int fdin[2], fdout[2], fderr[2];
+
+ /*
+ * In case of errors we must keep the promise to close FDs
+ * that have been passed in via ->in and ->out.
+ */
+
+ need_in = !cmd->no_stdin && cmd->in < 0;
+ if (need_in) {
+ if (pipe(fdin) < 0) {
+ if (cmd->out > 0)
+ close(cmd->out);
+ return -ERR_RUN_COMMAND_PIPE;
+ }
+ cmd->in = fdin[1];
+ }
+
+ need_out = !cmd->no_stdout
+ && !cmd->stdout_to_stderr
+ && cmd->out < 0;
+ if (need_out) {
+ if (pipe(fdout) < 0) {
+ if (need_in)
+ close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
+ return -ERR_RUN_COMMAND_PIPE;
+ }
+ cmd->out = fdout[0];
+ }
+
+ need_err = !cmd->no_stderr && cmd->err < 0;
+ if (need_err) {
+ if (pipe(fderr) < 0) {
+ if (need_in)
+ close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
+ if (need_out)
+ close_pair(fdout);
+ else if (cmd->out)
+ close(cmd->out);
+ return -ERR_RUN_COMMAND_PIPE;
+ }
+ cmd->err = fderr[0];
+ }
+
+ fflush(NULL);
+ cmd->pid = fork();
+ if (!cmd->pid) {
+ if (cmd->no_stdin)
+ dup_devnull(0);
+ else if (need_in) {
+ dup2(fdin[0], 0);
+ close_pair(fdin);
+ } else if (cmd->in) {
+ dup2(cmd->in, 0);
+ close(cmd->in);
+ }
+
+ if (cmd->no_stderr)
+ dup_devnull(2);
+ else if (need_err) {
+ dup2(fderr[1], 2);
+ close_pair(fderr);
+ }
+
+ if (cmd->no_stdout)
+ dup_devnull(1);
+ else if (cmd->stdout_to_stderr)
+ dup2(2, 1);
+ else if (need_out) {
+ dup2(fdout[1], 1);
+ close_pair(fdout);
+ } else if (cmd->out > 1) {
+ dup2(cmd->out, 1);
+ close(cmd->out);
+ }
+
+ if (cmd->dir && chdir(cmd->dir))
+ die("exec %s: cd to %s failed (%s)", cmd->argv[0],
+ cmd->dir, strerror(errno));
+ if (cmd->env) {
+ for (; *cmd->env; cmd->env++) {
+ if (strchr(*cmd->env, '='))
+ putenv((char*)*cmd->env);
+ else
+ unsetenv(*cmd->env);
+ }
+ }
+ if (cmd->preexec_cb)
+ cmd->preexec_cb();
+ if (cmd->perf_cmd) {
+ execv_perf_cmd(cmd->argv);
+ } else {
+ execvp(cmd->argv[0], (char *const*) cmd->argv);
+ }
+ exit(127);
+ }
+
+ if (cmd->pid < 0) {
+ int err = errno;
+ if (need_in)
+ close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
+ if (need_out)
+ close_pair(fdout);
+ else if (cmd->out)
+ close(cmd->out);
+ if (need_err)
+ close_pair(fderr);
+ return err == ENOENT ?
+ -ERR_RUN_COMMAND_EXEC :
+ -ERR_RUN_COMMAND_FORK;
+ }
+
+ if (need_in)
+ close(fdin[0]);
+ else if (cmd->in)
+ close(cmd->in);
+
+ if (need_out)
+ close(fdout[1]);
+ else if (cmd->out)
+ close(cmd->out);
+
+ if (need_err)
+ close(fderr[1]);
+
+ return 0;
+}
+
+static int wait_or_whine(pid_t pid)
+{
+ for (;;) {
+ int status, code;
+ pid_t waiting = waitpid(pid, &status, 0);
+
+ if (waiting < 0) {
+ if (errno == EINTR)
+ continue;
+ error("waitpid failed (%s)", strerror(errno));
+ return -ERR_RUN_COMMAND_WAITPID;
+ }
+ if (waiting != pid)
+ return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
+ if (WIFSIGNALED(status))
+ return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
+
+ if (!WIFEXITED(status))
+ return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
+ code = WEXITSTATUS(status);
+ switch (code) {
+ case 127:
+ return -ERR_RUN_COMMAND_EXEC;
+ case 0:
+ return 0;
+ default:
+ return -code;
+ }
+ }
+}
+
+int finish_command(struct child_process *cmd)
+{
+ return wait_or_whine(cmd->pid);
+}
+
+int run_command(struct child_process *cmd)
+{
+ int code = start_command(cmd);
+ if (code)
+ return code;
+ return finish_command(cmd);
+}
+
+static void prepare_run_command_v_opt(struct child_process *cmd,
+ const char **argv,
+ int opt)
+{
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->argv = argv;
+ cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
+ cmd->perf_cmd = opt & RUN_PERF_CMD ? 1 : 0;
+ cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+}
+
+int run_command_v_opt(const char **argv, int opt)
+{
+ struct child_process cmd;
+ prepare_run_command_v_opt(&cmd, argv, opt);
+ return run_command(&cmd);
+}
diff --git a/smartt-perf/util/run-command.h b/smartt-perf/util/run-command.h
new file mode 100644
index 0000000..1ef264d
--- /dev/null
+++ b/smartt-perf/util/run-command.h
@@ -0,0 +1,58 @@
+#ifndef __PERF_RUN_COMMAND_H
+#define __PERF_RUN_COMMAND_H
+
+enum {
+ ERR_RUN_COMMAND_FORK = 10000,
+ ERR_RUN_COMMAND_EXEC,
+ ERR_RUN_COMMAND_PIPE,
+ ERR_RUN_COMMAND_WAITPID,
+ ERR_RUN_COMMAND_WAITPID_WRONG_PID,
+ ERR_RUN_COMMAND_WAITPID_SIGNAL,
+ ERR_RUN_COMMAND_WAITPID_NOEXIT,
+};
+#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)
+
+struct child_process {
+ const char **argv;
+ pid_t pid;
+ /*
+ * Using .in, .out, .err:
+ * - Specify 0 for no redirections (child inherits stdin, stdout,
+ * stderr from parent).
+ * - Specify -1 to have a pipe allocated as follows:
+ * .in: returns the writable pipe end; parent writes to it,
+ * the readable pipe end becomes child's stdin
+ * .out, .err: returns the readable pipe end; parent reads from
+ * it, the writable pipe end becomes child's stdout/stderr
+ * The caller of start_command() must close the returned FDs
+ * after it has completed reading from/writing to it!
+ * - Specify > 0 to set a channel to a particular FD as follows:
+ * .in: a readable FD, becomes child's stdin
+ * .out: a writable FD, becomes child's stdout/stderr
+ * .err > 0 not supported
+ * The specified FD is closed by start_command(), even in case
+ * of errors!
+ */
+ int in;
+ int out;
+ int err;
+ const char *dir;
+ const char *const *env;
+ unsigned no_stdin:1;
+ unsigned no_stdout:1;
+ unsigned no_stderr:1;
+ unsigned perf_cmd:1; /* if this is to be perf sub-command */
+ unsigned stdout_to_stderr:1;
+ void (*preexec_cb)(void);
+};
+
+int start_command(struct child_process *);
+int finish_command(struct child_process *);
+int run_command(struct child_process *);
+
+#define RUN_COMMAND_NO_STDIN 1
+#define RUN_PERF_CMD 2 /*If this is to be perf sub-command */
+#define RUN_COMMAND_STDOUT_TO_STDERR 4
+int run_command_v_opt(const char **argv, int opt);
+
+#endif /* __PERF_RUN_COMMAND_H */
diff --git a/smartt-perf/util/scripting-engines/trace-event-perl.c b/smartt-perf/util/scripting-engines/trace-event-perl.c
new file mode 100644
index 0000000..9368081
--- /dev/null
+++ b/smartt-perf/util/scripting-engines/trace-event-perl.c
@@ -0,0 +1,565 @@
+/*
+ * trace-event-perl. Feed perf script events to an embedded Perl interpreter.
+ *
+ * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "../../perf.h"
+#include "../util.h"
+#include "../trace-event.h"
+
+#include <EXTERN.h>
+#include <perl.h>
+
+void boot_Perf__Trace__Context(pTHX_ CV *cv);
+void boot_DynaLoader(pTHX_ CV *cv);
+typedef PerlInterpreter * INTERP;
+
+void xs_init(pTHX);
+
+void xs_init(pTHX)
+{
+ const char *file = __FILE__;
+ dXSUB_SYS;
+
+ newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context,
+ file);
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+}
+
+INTERP my_perl;
+
+#define FTRACE_MAX_EVENT \
+ ((1 << (sizeof(unsigned short) * 8)) - 1)
+
+struct event *events[FTRACE_MAX_EVENT];
+
+extern struct scripting_context *scripting_context;
+
+static char *cur_field_name;
+static int zero_flag_atom;
+
+static void define_symbolic_value(const char *ev_name,
+ const char *field_name,
+ const char *field_value,
+ const char *field_str)
+{
+ unsigned long long value;
+ dSP;
+
+ value = eval_flag(field_value);
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
+ XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
+ XPUSHs(sv_2mortal(newSVuv(value)));
+ XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
+
+ PUTBACK;
+ if (get_cv("main::define_symbolic_value", 0))
+ call_pv("main::define_symbolic_value", G_SCALAR);
+ SPAGAIN;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+static void define_symbolic_values(struct print_flag_sym *field,
+ const char *ev_name,
+ const char *field_name)
+{
+ define_symbolic_value(ev_name, field_name, field->value, field->str);
+ if (field->next)
+ define_symbolic_values(field->next, ev_name, field_name);
+}
+
+static void define_symbolic_field(const char *ev_name,
+ const char *field_name)
+{
+ dSP;
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
+ XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
+
+ PUTBACK;
+ if (get_cv("main::define_symbolic_field", 0))
+ call_pv("main::define_symbolic_field", G_SCALAR);
+ SPAGAIN;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+static void define_flag_value(const char *ev_name,
+ const char *field_name,
+ const char *field_value,
+ const char *field_str)
+{
+ unsigned long long value;
+ dSP;
+
+ value = eval_flag(field_value);
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
+ XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
+ XPUSHs(sv_2mortal(newSVuv(value)));
+ XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
+
+ PUTBACK;
+ if (get_cv("main::define_flag_value", 0))
+ call_pv("main::define_flag_value", G_SCALAR);
+ SPAGAIN;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+static void define_flag_values(struct print_flag_sym *field,
+ const char *ev_name,
+ const char *field_name)
+{
+ define_flag_value(ev_name, field_name, field->value, field->str);
+ if (field->next)
+ define_flag_values(field->next, ev_name, field_name);
+}
+
+static void define_flag_field(const char *ev_name,
+ const char *field_name,
+ const char *delim)
+{
+ dSP;
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
+ XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
+ XPUSHs(sv_2mortal(newSVpv(delim, 0)));
+
+ PUTBACK;
+ if (get_cv("main::define_flag_field", 0))
+ call_pv("main::define_flag_field", G_SCALAR);
+ SPAGAIN;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+static void define_event_symbols(struct event *event,
+ const char *ev_name,
+ struct print_arg *args)
+{
+ switch (args->type) {
+ case PRINT_NULL:
+ break;
+ case PRINT_ATOM:
+ define_flag_value(ev_name, cur_field_name, "0",
+ args->atom.atom);
+ zero_flag_atom = 0;
+ break;
+ case PRINT_FIELD:
+ if (cur_field_name)
+ free(cur_field_name);
+ cur_field_name = strdup(args->field.name);
+ break;
+ case PRINT_FLAGS:
+ define_event_symbols(event, ev_name, args->flags.field);
+ define_flag_field(ev_name, cur_field_name, args->flags.delim);
+ define_flag_values(args->flags.flags, ev_name, cur_field_name);
+ break;
+ case PRINT_SYMBOL:
+ define_event_symbols(event, ev_name, args->symbol.field);
+ define_symbolic_field(ev_name, cur_field_name);
+ define_symbolic_values(args->symbol.symbols, ev_name,
+ cur_field_name);
+ break;
+ case PRINT_STRING:
+ break;
+ case PRINT_TYPE:
+ define_event_symbols(event, ev_name, args->typecast.item);
+ break;
+ case PRINT_OP:
+ if (strcmp(args->op.op, ":") == 0)
+ zero_flag_atom = 1;
+ define_event_symbols(event, ev_name, args->op.left);
+ define_event_symbols(event, ev_name, args->op.right);
+ break;
+ default:
+ /* we should warn... */
+ return;
+ }
+
+ if (args->next)
+ define_event_symbols(event, ev_name, args->next);
+}
+
+static inline struct event *find_cache_event(int type)
+{
+ static char ev_name[256];
+ struct event *event;
+
+ if (events[type])
+ return events[type];
+
+ events[type] = event = trace_find_event(type);
+ if (!event)
+ return NULL;
+
+ sprintf(ev_name, "%s::%s", event->system, event->name);
+
+ define_event_symbols(event, ev_name, event->print_fmt.args);
+
+ return event;
+}
+
+static void perl_process_event(int cpu, void *data,
+ int size __unused,
+ unsigned long long nsecs, char *comm)
+{
+ struct format_field *field;
+ static char handler[256];
+ unsigned long long val;
+ unsigned long s, ns;
+ struct event *event;
+ int type;
+ int pid;
+
+ dSP;
+
+ type = trace_parse_common_type(data);
+
+ event = find_cache_event(type);
+ if (!event)
+ die("ug! no event found for type %d", type);
+
+ pid = trace_parse_common_pid(data);
+
+ sprintf(handler, "%s::%s", event->system, event->name);
+
+ s = nsecs / NSECS_PER_SEC;
+ ns = nsecs - s * NSECS_PER_SEC;
+
+ scripting_context->event_data = data;
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs(sv_2mortal(newSVpv(handler, 0)));
+ XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
+ XPUSHs(sv_2mortal(newSVuv(cpu)));
+ XPUSHs(sv_2mortal(newSVuv(s)));
+ XPUSHs(sv_2mortal(newSVuv(ns)));
+ XPUSHs(sv_2mortal(newSViv(pid)));
+ XPUSHs(sv_2mortal(newSVpv(comm, 0)));
+
+ /* common fields other than pid can be accessed via xsub fns */
+
+ for (field = event->format.fields; field; field = field->next) {
+ if (field->flags & FIELD_IS_STRING) {
+ int offset;
+ if (field->flags & FIELD_IS_DYNAMIC) {
+ offset = *(int *)(data + field->offset);
+ offset &= 0xffff;
+ } else
+ offset = field->offset;
+ XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
+ } else { /* FIELD_IS_NUMERIC */
+ val = read_size(data + field->offset, field->size);
+ if (field->flags & FIELD_IS_SIGNED) {
+ XPUSHs(sv_2mortal(newSViv(val)));
+ } else {
+ XPUSHs(sv_2mortal(newSVuv(val)));
+ }
+ }
+ }
+
+ PUTBACK;
+
+ if (get_cv(handler, 0))
+ call_pv(handler, G_SCALAR);
+ else if (get_cv("main::trace_unhandled", 0)) {
+ XPUSHs(sv_2mortal(newSVpv(handler, 0)));
+ XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
+ XPUSHs(sv_2mortal(newSVuv(cpu)));
+ XPUSHs(sv_2mortal(newSVuv(nsecs)));
+ XPUSHs(sv_2mortal(newSViv(pid)));
+ XPUSHs(sv_2mortal(newSVpv(comm, 0)));
+ call_pv("main::trace_unhandled", G_SCALAR);
+ }
+ SPAGAIN;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+static void run_start_sub(void)
+{
+ dSP; /* access to Perl stack */
+ PUSHMARK(SP);
+
+ if (get_cv("main::trace_begin", 0))
+ call_pv("main::trace_begin", G_DISCARD | G_NOARGS);
+}
+
+/*
+ * Start trace script
+ */
+static int perl_start_script(const char *script, int argc, const char **argv)
+{
+ const char **command_line;
+ int i, err = 0;
+
+ command_line = malloc((argc + 2) * sizeof(const char *));
+ command_line[0] = "";
+ command_line[1] = script;
+ for (i = 2; i < argc + 2; i++)
+ command_line[i] = argv[i - 2];
+
+ my_perl = perl_alloc();
+ perl_construct(my_perl);
+
+ if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line,
+ (char **)NULL)) {
+ err = -1;
+ goto error;
+ }
+
+ if (perl_run(my_perl)) {
+ err = -1;
+ goto error;
+ }
+
+ if (SvTRUE(ERRSV)) {
+ err = -1;
+ goto error;
+ }
+
+ run_start_sub();
+
+ free(command_line);
+ return 0;
+error:
+ perl_free(my_perl);
+ free(command_line);
+
+ return err;
+}
+
+/*
+ * Stop trace script
+ */
+static int perl_stop_script(void)
+{
+ dSP; /* access to Perl stack */
+ PUSHMARK(SP);
+
+ if (get_cv("main::trace_end", 0))
+ call_pv("main::trace_end", G_DISCARD | G_NOARGS);
+
+ perl_destruct(my_perl);
+ perl_free(my_perl);
+
+ return 0;
+}
+
+static int perl_generate_script(const char *outfile)
+{
+ struct event *event = NULL;
+ struct format_field *f;
+ char fname[PATH_MAX];
+ int not_first, count;
+ FILE *ofp;
+
+ sprintf(fname, "%s.pl", outfile);
+ ofp = fopen(fname, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "couldn't open %s\n", fname);
+ return -1;
+ }
+
+ fprintf(ofp, "# perf script event handlers, "
+ "generated by perf script -g perl\n");
+
+ fprintf(ofp, "# Licensed under the terms of the GNU GPL"
+ " License version 2\n\n");
+
+ fprintf(ofp, "# The common_* event handler fields are the most useful "
+ "fields common to\n");
+
+ fprintf(ofp, "# all events. They don't necessarily correspond to "
+ "the 'common_*' fields\n");
+
+ fprintf(ofp, "# in the format files. Those fields not available as "
+ "handler params can\n");
+
+ fprintf(ofp, "# be retrieved using Perl functions of the form "
+ "common_*($context).\n");
+
+ fprintf(ofp, "# See Context.pm for the list of available "
+ "functions.\n\n");
+
+ fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/"
+ "Perf-Trace-Util/lib\";\n");
+
+ fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n");
+ fprintf(ofp, "use Perf::Trace::Core;\n");
+ fprintf(ofp, "use Perf::Trace::Context;\n");
+ fprintf(ofp, "use Perf::Trace::Util;\n\n");
+
+ fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");
+ fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n");
+
+ while ((event = trace_find_next_event(event))) {
+ fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
+ fprintf(ofp, "\tmy (");
+
+ fprintf(ofp, "$event_name, ");
+ fprintf(ofp, "$context, ");
+ fprintf(ofp, "$common_cpu, ");
+ fprintf(ofp, "$common_secs, ");
+ fprintf(ofp, "$common_nsecs,\n");
+ fprintf(ofp, "\t $common_pid, ");
+ fprintf(ofp, "$common_comm,\n\t ");
+
+ not_first = 0;
+ count = 0;
+
+ for (f = event->format.fields; f; f = f->next) {
+ if (not_first++)
+ fprintf(ofp, ", ");
+ if (++count % 5 == 0)
+ fprintf(ofp, "\n\t ");
+
+ fprintf(ofp, "$%s", f->name);
+ }
+ fprintf(ofp, ") = @_;\n\n");
+
+ fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
+ "$common_secs, $common_nsecs,\n\t "
+ "$common_pid, $common_comm);\n\n");
+
+ fprintf(ofp, "\tprintf(\"");
+
+ not_first = 0;
+ count = 0;
+
+ for (f = event->format.fields; f; f = f->next) {
+ if (not_first++)
+ fprintf(ofp, ", ");
+ if (count && count % 4 == 0) {
+ fprintf(ofp, "\".\n\t \"");
+ }
+ count++;
+
+ fprintf(ofp, "%s=", f->name);
+ if (f->flags & FIELD_IS_STRING ||
+ f->flags & FIELD_IS_FLAG ||
+ f->flags & FIELD_IS_SYMBOLIC)
+ fprintf(ofp, "%%s");
+ else if (f->flags & FIELD_IS_SIGNED)
+ fprintf(ofp, "%%d");
+ else
+ fprintf(ofp, "%%u");
+ }
+
+ fprintf(ofp, "\\n\",\n\t ");
+
+ not_first = 0;
+ count = 0;
+
+ for (f = event->format.fields; f; f = f->next) {
+ if (not_first++)
+ fprintf(ofp, ", ");
+
+ if (++count % 5 == 0)
+ fprintf(ofp, "\n\t ");
+
+ if (f->flags & FIELD_IS_FLAG) {
+ if ((count - 1) % 5 != 0) {
+ fprintf(ofp, "\n\t ");
+ count = 4;
+ }
+ fprintf(ofp, "flag_str(\"");
+ fprintf(ofp, "%s::%s\", ", event->system,
+ event->name);
+ fprintf(ofp, "\"%s\", $%s)", f->name,
+ f->name);
+ } else if (f->flags & FIELD_IS_SYMBOLIC) {
+ if ((count - 1) % 5 != 0) {
+ fprintf(ofp, "\n\t ");
+ count = 4;
+ }
+ fprintf(ofp, "symbol_str(\"");
+ fprintf(ofp, "%s::%s\", ", event->system,
+ event->name);
+ fprintf(ofp, "\"%s\", $%s)", f->name,
+ f->name);
+ } else
+ fprintf(ofp, "$%s", f->name);
+ }
+
+ fprintf(ofp, ");\n");
+ fprintf(ofp, "}\n\n");
+ }
+
+ fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, "
+ "$common_cpu, $common_secs, $common_nsecs,\n\t "
+ "$common_pid, $common_comm) = @_;\n\n");
+
+ fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
+ "$common_secs, $common_nsecs,\n\t $common_pid, "
+ "$common_comm);\n}\n\n");
+
+ fprintf(ofp, "sub print_header\n{\n"
+ "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n"
+ "\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t "
+ "$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}");
+
+ fclose(ofp);
+
+ fprintf(stderr, "generated Perl script: %s\n", fname);
+
+ return 0;
+}
+
+struct scripting_ops perl_scripting_ops = {
+ .name = "Perl",
+ .start_script = perl_start_script,
+ .stop_script = perl_stop_script,
+ .process_event = perl_process_event,
+ .generate_script = perl_generate_script,
+};
diff --git a/smartt-perf/util/scripting-engines/trace-event-python.c b/smartt-perf/util/scripting-engines/trace-event-python.c
new file mode 100644
index 0000000..c6d9933
--- /dev/null
+++ b/smartt-perf/util/scripting-engines/trace-event-python.c
@@ -0,0 +1,594 @@
+/*
+ * trace-event-python. Feed trace events to an embedded Python interpreter.
+ *
+ * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <Python.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "../../perf.h"
+#include "../util.h"
+#include "../trace-event.h"
+
+PyMODINIT_FUNC initperf_trace_context(void);
+
+#define FTRACE_MAX_EVENT \
+ ((1 << (sizeof(unsigned short) * 8)) - 1)
+
+struct event *events[FTRACE_MAX_EVENT];
+
+#define MAX_FIELDS 64
+#define N_COMMON_FIELDS 7
+
+extern struct scripting_context *scripting_context;
+
+static char *cur_field_name;
+static int zero_flag_atom;
+
+static PyObject *main_module, *main_dict;
+
+static void handler_call_die(const char *handler_name)
+{
+ PyErr_Print();
+ Py_FatalError("problem in Python trace event handler");
+}
+
+static void define_value(enum print_arg_type field_type,
+ const char *ev_name,
+ const char *field_name,
+ const char *field_value,
+ const char *field_str)
+{
+ const char *handler_name = "define_flag_value";
+ PyObject *handler, *t, *retval;
+ unsigned long long value;
+ unsigned n = 0;
+
+ if (field_type == PRINT_SYMBOL)
+ handler_name = "define_symbolic_value";
+
+ t = PyTuple_New(4);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+
+ value = eval_flag(field_value);
+
+ PyTuple_SetItem(t, n++, PyString_FromString(ev_name));
+ PyTuple_SetItem(t, n++, PyString_FromString(field_name));
+ PyTuple_SetItem(t, n++, PyInt_FromLong(value));
+ PyTuple_SetItem(t, n++, PyString_FromString(field_str));
+
+ handler = PyDict_GetItemString(main_dict, handler_name);
+ if (handler && PyCallable_Check(handler)) {
+ retval = PyObject_CallObject(handler, t);
+ if (retval == NULL)
+ handler_call_die(handler_name);
+ }
+
+ Py_DECREF(t);
+}
+
+static void define_values(enum print_arg_type field_type,
+ struct print_flag_sym *field,
+ const char *ev_name,
+ const char *field_name)
+{
+ define_value(field_type, ev_name, field_name, field->value,
+ field->str);
+
+ if (field->next)
+ define_values(field_type, field->next, ev_name, field_name);
+}
+
+static void define_field(enum print_arg_type field_type,
+ const char *ev_name,
+ const char *field_name,
+ const char *delim)
+{
+ const char *handler_name = "define_flag_field";
+ PyObject *handler, *t, *retval;
+ unsigned n = 0;
+
+ if (field_type == PRINT_SYMBOL)
+ handler_name = "define_symbolic_field";
+
+ if (field_type == PRINT_FLAGS)
+ t = PyTuple_New(3);
+ else
+ t = PyTuple_New(2);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+
+ PyTuple_SetItem(t, n++, PyString_FromString(ev_name));
+ PyTuple_SetItem(t, n++, PyString_FromString(field_name));
+ if (field_type == PRINT_FLAGS)
+ PyTuple_SetItem(t, n++, PyString_FromString(delim));
+
+ handler = PyDict_GetItemString(main_dict, handler_name);
+ if (handler && PyCallable_Check(handler)) {
+ retval = PyObject_CallObject(handler, t);
+ if (retval == NULL)
+ handler_call_die(handler_name);
+ }
+
+ Py_DECREF(t);
+}
+
+static void define_event_symbols(struct event *event,
+ const char *ev_name,
+ struct print_arg *args)
+{
+ switch (args->type) {
+ case PRINT_NULL:
+ break;
+ case PRINT_ATOM:
+ define_value(PRINT_FLAGS, ev_name, cur_field_name, "0",
+ args->atom.atom);
+ zero_flag_atom = 0;
+ break;
+ case PRINT_FIELD:
+ if (cur_field_name)
+ free(cur_field_name);
+ cur_field_name = strdup(args->field.name);
+ break;
+ case PRINT_FLAGS:
+ define_event_symbols(event, ev_name, args->flags.field);
+ define_field(PRINT_FLAGS, ev_name, cur_field_name,
+ args->flags.delim);
+ define_values(PRINT_FLAGS, args->flags.flags, ev_name,
+ cur_field_name);
+ break;
+ case PRINT_SYMBOL:
+ define_event_symbols(event, ev_name, args->symbol.field);
+ define_field(PRINT_SYMBOL, ev_name, cur_field_name, NULL);
+ define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name,
+ cur_field_name);
+ break;
+ case PRINT_STRING:
+ break;
+ case PRINT_TYPE:
+ define_event_symbols(event, ev_name, args->typecast.item);
+ break;
+ case PRINT_OP:
+ if (strcmp(args->op.op, ":") == 0)
+ zero_flag_atom = 1;
+ define_event_symbols(event, ev_name, args->op.left);
+ define_event_symbols(event, ev_name, args->op.right);
+ break;
+ default:
+ /* we should warn... */
+ return;
+ }
+
+ if (args->next)
+ define_event_symbols(event, ev_name, args->next);
+}
+
+static inline struct event *find_cache_event(int type)
+{
+ static char ev_name[256];
+ struct event *event;
+
+ if (events[type])
+ return events[type];
+
+ events[type] = event = trace_find_event(type);
+ if (!event)
+ return NULL;
+
+ sprintf(ev_name, "%s__%s", event->system, event->name);
+
+ define_event_symbols(event, ev_name, event->print_fmt.args);
+
+ return event;
+}
+
+static void python_process_event(int cpu, void *data,
+ int size __unused,
+ unsigned long long nsecs, char *comm)
+{
+ PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;
+ static char handler_name[256];
+ struct format_field *field;
+ unsigned long long val;
+ unsigned long s, ns;
+ struct event *event;
+ unsigned n = 0;
+ int type;
+ int pid;
+
+ t = PyTuple_New(MAX_FIELDS);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+
+ type = trace_parse_common_type(data);
+
+ event = find_cache_event(type);
+ if (!event)
+ die("ug! no event found for type %d", type);
+
+ pid = trace_parse_common_pid(data);
+
+ sprintf(handler_name, "%s__%s", event->system, event->name);
+
+ handler = PyDict_GetItemString(main_dict, handler_name);
+ if (handler && !PyCallable_Check(handler))
+ handler = NULL;
+ if (!handler) {
+ dict = PyDict_New();
+ if (!dict)
+ Py_FatalError("couldn't create Python dict");
+ }
+ s = nsecs / NSECS_PER_SEC;
+ ns = nsecs - s * NSECS_PER_SEC;
+
+ scripting_context->event_data = data;
+
+ context = PyCObject_FromVoidPtr(scripting_context, NULL);
+
+ PyTuple_SetItem(t, n++, PyString_FromString(handler_name));
+ PyTuple_SetItem(t, n++,
+ PyCObject_FromVoidPtr(scripting_context, NULL));
+
+ if (handler) {
+ PyTuple_SetItem(t, n++, PyInt_FromLong(cpu));
+ PyTuple_SetItem(t, n++, PyInt_FromLong(s));
+ PyTuple_SetItem(t, n++, PyInt_FromLong(ns));
+ PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
+ PyTuple_SetItem(t, n++, PyString_FromString(comm));
+ } else {
+ PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu));
+ PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s));
+ PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns));
+ PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid));
+ PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm));
+ }
+ for (field = event->format.fields; field; field = field->next) {
+ if (field->flags & FIELD_IS_STRING) {
+ int offset;
+ if (field->flags & FIELD_IS_DYNAMIC) {
+ offset = *(int *)(data + field->offset);
+ offset &= 0xffff;
+ } else
+ offset = field->offset;
+ obj = PyString_FromString((char *)data + offset);
+ } else { /* FIELD_IS_NUMERIC */
+ val = read_size(data + field->offset, field->size);
+ if (field->flags & FIELD_IS_SIGNED) {
+ if ((long long)val >= LONG_MIN &&
+ (long long)val <= LONG_MAX)
+ obj = PyInt_FromLong(val);
+ else
+ obj = PyLong_FromLongLong(val);
+ } else {
+ if (val <= LONG_MAX)
+ obj = PyInt_FromLong(val);
+ else
+ obj = PyLong_FromUnsignedLongLong(val);
+ }
+ }
+ if (handler)
+ PyTuple_SetItem(t, n++, obj);
+ else
+ PyDict_SetItemString(dict, field->name, obj);
+
+ }
+ if (!handler)
+ PyTuple_SetItem(t, n++, dict);
+
+ if (_PyTuple_Resize(&t, n) == -1)
+ Py_FatalError("error resizing Python tuple");
+
+ if (handler) {
+ retval = PyObject_CallObject(handler, t);
+ if (retval == NULL)
+ handler_call_die(handler_name);
+ } else {
+ handler = PyDict_GetItemString(main_dict, "trace_unhandled");
+ if (handler && PyCallable_Check(handler)) {
+
+ retval = PyObject_CallObject(handler, t);
+ if (retval == NULL)
+ handler_call_die("trace_unhandled");
+ }
+ Py_DECREF(dict);
+ }
+
+ Py_DECREF(t);
+}
+
+static int run_start_sub(void)
+{
+ PyObject *handler, *retval;
+ int err = 0;
+
+ main_module = PyImport_AddModule("__main__");
+ if (main_module == NULL)
+ return -1;
+ Py_INCREF(main_module);
+
+ main_dict = PyModule_GetDict(main_module);
+ if (main_dict == NULL) {
+ err = -1;
+ goto error;
+ }
+ Py_INCREF(main_dict);
+
+ handler = PyDict_GetItemString(main_dict, "trace_begin");
+ if (handler == NULL || !PyCallable_Check(handler))
+ goto out;
+
+ retval = PyObject_CallObject(handler, NULL);
+ if (retval == NULL)
+ handler_call_die("trace_begin");
+
+ Py_DECREF(retval);
+ return err;
+error:
+ Py_XDECREF(main_dict);
+ Py_XDECREF(main_module);
+out:
+ return err;
+}
+
+/*
+ * Start trace script
+ */
+static int python_start_script(const char *script, int argc, const char **argv)
+{
+ const char **command_line;
+ char buf[PATH_MAX];
+ int i, err = 0;
+ FILE *fp;
+
+ command_line = malloc((argc + 1) * sizeof(const char *));
+ command_line[0] = script;
+ for (i = 1; i < argc + 1; i++)
+ command_line[i] = argv[i - 1];
+
+ Py_Initialize();
+
+ initperf_trace_context();
+
+ PySys_SetArgv(argc + 1, (char **)command_line);
+
+ fp = fopen(script, "r");
+ if (!fp) {
+ sprintf(buf, "Can't open python script \"%s\"", script);
+ perror(buf);
+ err = -1;
+ goto error;
+ }
+
+ err = PyRun_SimpleFile(fp, script);
+ if (err) {
+ fprintf(stderr, "Error running python script %s\n", script);
+ goto error;
+ }
+
+ err = run_start_sub();
+ if (err) {
+ fprintf(stderr, "Error starting python script %s\n", script);
+ goto error;
+ }
+
+ free(command_line);
+
+ return err;
+error:
+ Py_Finalize();
+ free(command_line);
+
+ return err;
+}
+
+/*
+ * Stop trace script
+ */
+static int python_stop_script(void)
+{
+ PyObject *handler, *retval;
+ int err = 0;
+
+ handler = PyDict_GetItemString(main_dict, "trace_end");
+ if (handler == NULL || !PyCallable_Check(handler))
+ goto out;
+
+ retval = PyObject_CallObject(handler, NULL);
+ if (retval == NULL)
+ handler_call_die("trace_end");
+ else
+ Py_DECREF(retval);
+out:
+ Py_XDECREF(main_dict);
+ Py_XDECREF(main_module);
+ Py_Finalize();
+
+ return err;
+}
+
+static int python_generate_script(const char *outfile)
+{
+ struct event *event = NULL;
+ struct format_field *f;
+ char fname[PATH_MAX];
+ int not_first, count;
+ FILE *ofp;
+
+ sprintf(fname, "%s.py", outfile);
+ ofp = fopen(fname, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "couldn't open %s\n", fname);
+ return -1;
+ }
+ fprintf(ofp, "# perf script event handlers, "
+ "generated by perf script -g python\n");
+
+ fprintf(ofp, "# Licensed under the terms of the GNU GPL"
+ " License version 2\n\n");
+
+ fprintf(ofp, "# The common_* event handler fields are the most useful "
+ "fields common to\n");
+
+ fprintf(ofp, "# all events. They don't necessarily correspond to "
+ "the 'common_*' fields\n");
+
+ fprintf(ofp, "# in the format files. Those fields not available as "
+ "handler params can\n");
+
+ fprintf(ofp, "# be retrieved using Python functions of the form "
+ "common_*(context).\n");
+
+ fprintf(ofp, "# See the perf-trace-python Documentation for the list "
+ "of available functions.\n\n");
+
+ fprintf(ofp, "import os\n");
+ fprintf(ofp, "import sys\n\n");
+
+ fprintf(ofp, "sys.path.append(os.environ['PERF_EXEC_PATH'] + \\\n");
+ fprintf(ofp, "\t'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')\n");
+ fprintf(ofp, "\nfrom perf_trace_context import *\n");
+ fprintf(ofp, "from Core import *\n\n\n");
+
+ fprintf(ofp, "def trace_begin():\n");
+ fprintf(ofp, "\tprint \"in trace_begin\"\n\n");
+
+ fprintf(ofp, "def trace_end():\n");
+ fprintf(ofp, "\tprint \"in trace_end\"\n\n");
+
+ while ((event = trace_find_next_event(event))) {
+ fprintf(ofp, "def %s__%s(", event->system, event->name);
+ fprintf(ofp, "event_name, ");
+ fprintf(ofp, "context, ");
+ fprintf(ofp, "common_cpu,\n");
+ fprintf(ofp, "\tcommon_secs, ");
+ fprintf(ofp, "common_nsecs, ");
+ fprintf(ofp, "common_pid, ");
+ fprintf(ofp, "common_comm,\n\t");
+
+ not_first = 0;
+ count = 0;
+
+ for (f = event->format.fields; f; f = f->next) {
+ if (not_first++)
+ fprintf(ofp, ", ");
+ if (++count % 5 == 0)
+ fprintf(ofp, "\n\t");
+
+ fprintf(ofp, "%s", f->name);
+ }
+ fprintf(ofp, "):\n");
+
+ fprintf(ofp, "\t\tprint_header(event_name, common_cpu, "
+ "common_secs, common_nsecs,\n\t\t\t"
+ "common_pid, common_comm)\n\n");
+
+ fprintf(ofp, "\t\tprint \"");
+
+ not_first = 0;
+ count = 0;
+
+ for (f = event->format.fields; f; f = f->next) {
+ if (not_first++)
+ fprintf(ofp, ", ");
+ if (count && count % 3 == 0) {
+ fprintf(ofp, "\" \\\n\t\t\"");
+ }
+ count++;
+
+ fprintf(ofp, "%s=", f->name);
+ if (f->flags & FIELD_IS_STRING ||
+ f->flags & FIELD_IS_FLAG ||
+ f->flags & FIELD_IS_SYMBOLIC)
+ fprintf(ofp, "%%s");
+ else if (f->flags & FIELD_IS_SIGNED)
+ fprintf(ofp, "%%d");
+ else
+ fprintf(ofp, "%%u");
+ }
+
+ fprintf(ofp, "\\n\" %% \\\n\t\t(");
+
+ not_first = 0;
+ count = 0;
+
+ for (f = event->format.fields; f; f = f->next) {
+ if (not_first++)
+ fprintf(ofp, ", ");
+
+ if (++count % 5 == 0)
+ fprintf(ofp, "\n\t\t");
+
+ if (f->flags & FIELD_IS_FLAG) {
+ if ((count - 1) % 5 != 0) {
+ fprintf(ofp, "\n\t\t");
+ count = 4;
+ }
+ fprintf(ofp, "flag_str(\"");
+ fprintf(ofp, "%s__%s\", ", event->system,
+ event->name);
+ fprintf(ofp, "\"%s\", %s)", f->name,
+ f->name);
+ } else if (f->flags & FIELD_IS_SYMBOLIC) {
+ if ((count - 1) % 5 != 0) {
+ fprintf(ofp, "\n\t\t");
+ count = 4;
+ }
+ fprintf(ofp, "symbol_str(\"");
+ fprintf(ofp, "%s__%s\", ", event->system,
+ event->name);
+ fprintf(ofp, "\"%s\", %s)", f->name,
+ f->name);
+ } else
+ fprintf(ofp, "%s", f->name);
+ }
+
+ fprintf(ofp, "),\n\n");
+ }
+
+ fprintf(ofp, "def trace_unhandled(event_name, context, "
+ "event_fields_dict):\n");
+
+ fprintf(ofp, "\t\tprint ' '.join(['%%s=%%s'%%(k,str(v))"
+ "for k,v in sorted(event_fields_dict.items())])\n\n");
+
+ fprintf(ofp, "def print_header("
+ "event_name, cpu, secs, nsecs, pid, comm):\n"
+ "\tprint \"%%-20s %%5u %%05u.%%09u %%8u %%-20s \" %% \\\n\t"
+ "(event_name, cpu, secs, nsecs, pid, comm),\n");
+
+ fclose(ofp);
+
+ fprintf(stderr, "generated Python script: %s\n", fname);
+
+ return 0;
+}
+
+struct scripting_ops python_scripting_ops = {
+ .name = "Python",
+ .start_script = python_start_script,
+ .stop_script = python_stop_script,
+ .process_event = python_process_event,
+ .generate_script = python_generate_script,
+};
diff --git a/smartt-perf/util/session.c b/smartt-perf/util/session.c
new file mode 100644
index 0000000..105f00b
--- /dev/null
+++ b/smartt-perf/util/session.c
@@ -0,0 +1,1136 @@
+#define _FILE_OFFSET_BITS 64
+
+#include <linux/kernel.h>
+
+#include <byteswap.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "session.h"
+#include "sort.h"
+#include "util.h"
+
+static int perf_session__open(struct perf_session *self, bool force)
+{
+ struct stat input_stat;
+
+ if (!strcmp(self->filename, "-")) {
+ self->fd_pipe = true;
+ self->fd = STDIN_FILENO;
+
+ if (perf_header__read(self, self->fd) < 0)
+ pr_err("incompatible file format");
+
+ return 0;
+ }
+
+ self->fd = open(self->filename, O_RDONLY);
+ if (self->fd < 0) {
+ int err = errno;
+
+ pr_err("failed to open %s: %s", self->filename, strerror(err));
+ if (err == ENOENT && !strcmp(self->filename, "perf.data"))
+ pr_err(" (try 'perf record' first)");
+ pr_err("\n");
+ return -errno;
+ }
+
+ if (fstat(self->fd, &input_stat) < 0)
+ goto out_close;
+
+ if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
+ pr_err("file %s not owned by current user or root\n",
+ self->filename);
+ goto out_close;
+ }
+
+ if (!input_stat.st_size) {
+ pr_info("zero-sized file (%s), nothing to do!\n",
+ self->filename);
+ goto out_close;
+ }
+
+ if (perf_header__read(self, self->fd) < 0) {
+ pr_err("incompatible file format");
+ goto out_close;
+ }
+
+ self->size = input_stat.st_size;
+ return 0;
+
+out_close:
+ close(self->fd);
+ self->fd = -1;
+ return -1;
+}
+
+static void perf_session__id_header_size(struct perf_session *session)
+{
+ struct sample_data *data;
+ u64 sample_type = session->sample_type;
+ u16 size = 0;
+
+ if (!session->sample_id_all)
+ goto out;
+
+ if (sample_type & PERF_SAMPLE_TID)
+ size += sizeof(data->tid) * 2;
+
+ if (sample_type & PERF_SAMPLE_TIME)
+ size += sizeof(data->time);
+
+ if (sample_type & PERF_SAMPLE_ID)
+ size += sizeof(data->id);
+
+ if (sample_type & PERF_SAMPLE_STREAM_ID)
+ size += sizeof(data->stream_id);
+
+ if (sample_type & PERF_SAMPLE_CPU)
+ size += sizeof(data->cpu) * 2;
+out:
+ session->id_hdr_size = size;
+}
+
+void perf_session__set_sample_id_all(struct perf_session *session, bool value)
+{
+ session->sample_id_all = value;
+ perf_session__id_header_size(session);
+}
+
+void perf_session__set_sample_type(struct perf_session *session, u64 type)
+{
+ session->sample_type = type;
+}
+
+void perf_session__update_sample_type(struct perf_session *self)
+{
+ self->sample_type = perf_header__sample_type(&self->header);
+ self->sample_id_all = perf_header__sample_id_all(&self->header);
+ perf_session__id_header_size(self);
+}
+
+int perf_session__create_kernel_maps(struct perf_session *self)
+{
+ int ret = machine__create_kernel_maps(&self->host_machine);
+
+ if (ret >= 0)
+ ret = machines__create_guest_kernel_maps(&self->machines);
+ return ret;
+}
+
+static void perf_session__destroy_kernel_maps(struct perf_session *self)
+{
+ machine__destroy_kernel_maps(&self->host_machine);
+ machines__destroy_guest_kernel_maps(&self->machines);
+}
+
+struct perf_session *perf_session__new(const char *filename, int mode,
+ bool force, bool repipe,
+ struct perf_event_ops *ops)
+{
+ size_t len = filename ? strlen(filename) + 1 : 0;
+ struct perf_session *self = zalloc(sizeof(*self) + len);
+
+ if (self == NULL)
+ goto out;
+
+ if (perf_header__init(&self->header) < 0)
+ goto out_free;
+
+ memcpy(self->filename, filename, len);
+ self->threads = RB_ROOT;
+ INIT_LIST_HEAD(&self->dead_threads);
+ self->hists_tree = RB_ROOT;
+ self->last_match = NULL;
+ /*
+ * On 64bit we can mmap the data file in one go. No need for tiny mmap
+ * slices. On 32bit we use 32MB.
+ */
+#if BITS_PER_LONG == 64
+ self->mmap_window = ULLONG_MAX;
+#else
+ self->mmap_window = 32 * 1024 * 1024ULL;
+#endif
+ self->machines = RB_ROOT;
+ self->repipe = repipe;
+ INIT_LIST_HEAD(&self->ordered_samples.samples);
+ INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
+ INIT_LIST_HEAD(&self->ordered_samples.to_free);
+ machine__init(&self->host_machine, "", HOST_KERNEL_ID);
+
+ if (mode == O_RDONLY) {
+ if (perf_session__open(self, force) < 0)
+ goto out_delete;
+ } else if (mode == O_WRONLY) {
+ /*
+ * In O_RDONLY mode this will be performed when reading the
+ * kernel MMAP event, in event__process_mmap().
+ */
+ if (perf_session__create_kernel_maps(self) < 0)
+ goto out_delete;
+ }
+
+ perf_session__update_sample_type(self);
+
+ if (ops && ops->ordering_requires_timestamps &&
+ ops->ordered_samples && !self->sample_id_all) {
+ dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
+ ops->ordered_samples = false;
+ }
+
+out:
+ return self;
+out_free:
+ free(self);
+ return NULL;
+out_delete:
+ perf_session__delete(self);
+ return NULL;
+}
+
+static void perf_session__delete_dead_threads(struct perf_session *self)
+{
+ struct thread *n, *t;
+
+ list_for_each_entry_safe(t, n, &self->dead_threads, node) {
+ list_del(&t->node);
+ thread__delete(t);
+ }
+}
+
+static void perf_session__delete_threads(struct perf_session *self)
+{
+ struct rb_node *nd = rb_first(&self->threads);
+
+ while (nd) {
+ struct thread *t = rb_entry(nd, struct thread, rb_node);
+
+ rb_erase(&t->rb_node, &self->threads);
+ nd = rb_next(nd);
+ thread__delete(t);
+ }
+}
+
+void perf_session__delete(struct perf_session *self)
+{
+ perf_header__exit(&self->header);
+ perf_session__destroy_kernel_maps(self);
+ perf_session__delete_dead_threads(self);
+ perf_session__delete_threads(self);
+ machine__exit(&self->host_machine);
+ close(self->fd);
+ free(self);
+}
+
+void perf_session__remove_thread(struct perf_session *self, struct thread *th)
+{
+ self->last_match = NULL;
+ rb_erase(&th->rb_node, &self->threads);
+ /*
+ * We may have references to this thread, for instance in some hist_entry
+ * instances, so just move them to a separate list.
+ */
+ list_add_tail(&th->node, &self->dead_threads);
+}
+
+static bool symbol__match_parent_regex(struct symbol *sym)
+{
+ if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
+ return 1;
+
+ return 0;
+}
+
+struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
+ struct thread *thread,
+ struct ip_callchain *chain,
+ struct symbol **parent)
+{
+ u8 cpumode = PERF_RECORD_MISC_USER;
+ unsigned int i;
+ struct map_symbol *syms = calloc(chain->nr, sizeof(*syms));
+
+ if (!syms)
+ return NULL;
+
+ for (i = 0; i < chain->nr; i++) {
+ u64 ip = chain->ips[i];
+ struct addr_location al;
+
+ if (ip >= PERF_CONTEXT_MAX) {
+ switch (ip) {
+ case PERF_CONTEXT_HV:
+ cpumode = PERF_RECORD_MISC_HYPERVISOR; break;
+ case PERF_CONTEXT_KERNEL:
+ cpumode = PERF_RECORD_MISC_KERNEL; break;
+ case PERF_CONTEXT_USER:
+ cpumode = PERF_RECORD_MISC_USER; break;
+ default:
+ break;
+ }
+ continue;
+ }
+
+ al.filtered = false;
+ thread__find_addr_location(thread, self, cpumode,
+ MAP__FUNCTION, thread->pid, ip, &al, NULL);
+ if (al.sym != NULL) {
+ if (sort__has_parent && !*parent &&
+ symbol__match_parent_regex(al.sym))
+ *parent = al.sym;
+ if (!symbol_conf.use_callchain)
+ break;
+ syms[i].map = al.map;
+ syms[i].sym = al.sym;
+ }
+ }
+
+ return syms;
+}
+
+static int process_event_synth_stub(event_t *event __used,
+ struct perf_session *session __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_event_stub(event_t *event __used,
+ struct sample_data *sample __used,
+ struct perf_session *session __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_finished_round_stub(event_t *event __used,
+ struct perf_session *session __used,
+ struct perf_event_ops *ops __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_finished_round(event_t *event,
+ struct perf_session *session,
+ struct perf_event_ops *ops);
+
+static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
+{
+ if (handler->sample == NULL)
+ handler->sample = process_event_stub;
+ if (handler->mmap == NULL)
+ handler->mmap = process_event_stub;
+ if (handler->comm == NULL)
+ handler->comm = process_event_stub;
+ if (handler->fork == NULL)
+ handler->fork = process_event_stub;
+ if (handler->exit == NULL)
+ handler->exit = process_event_stub;
+ if (handler->lost == NULL)
+ handler->lost = event__process_lost;
+ if (handler->read == NULL)
+ handler->read = process_event_stub;
+ if (handler->throttle == NULL)
+ handler->throttle = process_event_stub;
+ if (handler->unthrottle == NULL)
+ handler->unthrottle = process_event_stub;
+ if (handler->attr == NULL)
+ handler->attr = process_event_synth_stub;
+ if (handler->event_type == NULL)
+ handler->event_type = process_event_synth_stub;
+ if (handler->tracing_data == NULL)
+ handler->tracing_data = process_event_synth_stub;
+ if (handler->build_id == NULL)
+ handler->build_id = process_event_synth_stub;
+ if (handler->finished_round == NULL) {
+ if (handler->ordered_samples)
+ handler->finished_round = process_finished_round;
+ else
+ handler->finished_round = process_finished_round_stub;
+ }
+}
+
+void mem_bswap_64(void *src, int byte_size)
+{
+ u64 *m = src;
+
+ while (byte_size > 0) {
+ *m = bswap_64(*m);
+ byte_size -= sizeof(u64);
+ ++m;
+ }
+}
+
+static void event__all64_swap(event_t *self)
+{
+ struct perf_event_header *hdr = &self->header;
+ mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
+}
+
+static void event__comm_swap(event_t *self)
+{
+ self->comm.pid = bswap_32(self->comm.pid);
+ self->comm.tid = bswap_32(self->comm.tid);
+}
+
+static void event__mmap_swap(event_t *self)
+{
+ self->mmap.pid = bswap_32(self->mmap.pid);
+ self->mmap.tid = bswap_32(self->mmap.tid);
+ self->mmap.start = bswap_64(self->mmap.start);
+ self->mmap.len = bswap_64(self->mmap.len);
+ self->mmap.pgoff = bswap_64(self->mmap.pgoff);
+}
+
+static void event__task_swap(event_t *self)
+{
+ self->fork.pid = bswap_32(self->fork.pid);
+ self->fork.tid = bswap_32(self->fork.tid);
+ self->fork.ppid = bswap_32(self->fork.ppid);
+ self->fork.ptid = bswap_32(self->fork.ptid);
+ self->fork.time = bswap_64(self->fork.time);
+}
+
+static void event__read_swap(event_t *self)
+{
+ self->read.pid = bswap_32(self->read.pid);
+ self->read.tid = bswap_32(self->read.tid);
+ self->read.value = bswap_64(self->read.value);
+ self->read.time_enabled = bswap_64(self->read.time_enabled);
+ self->read.time_running = bswap_64(self->read.time_running);
+ self->read.id = bswap_64(self->read.id);
+}
+
+static void event__attr_swap(event_t *self)
+{
+ size_t size;
+
+ self->attr.attr.type = bswap_32(self->attr.attr.type);
+ self->attr.attr.size = bswap_32(self->attr.attr.size);
+ self->attr.attr.config = bswap_64(self->attr.attr.config);
+ self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period);
+ self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type);
+ self->attr.attr.read_format = bswap_64(self->attr.attr.read_format);
+ self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events);
+ self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type);
+ self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr);
+ self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len);
+
+ size = self->header.size;
+ size -= (void *)&self->attr.id - (void *)self;
+ mem_bswap_64(self->attr.id, size);
+}
+
+static void event__event_type_swap(event_t *self)
+{
+ self->event_type.event_type.event_id =
+ bswap_64(self->event_type.event_type.event_id);
+}
+
+static void event__tracing_data_swap(event_t *self)
+{
+ self->tracing_data.size = bswap_32(self->tracing_data.size);
+}
+
+typedef void (*event__swap_op)(event_t *self);
+
+static event__swap_op event__swap_ops[] = {
+ [PERF_RECORD_MMAP] = event__mmap_swap,
+ [PERF_RECORD_COMM] = event__comm_swap,
+ [PERF_RECORD_FORK] = event__task_swap,
+ [PERF_RECORD_EXIT] = event__task_swap,
+ [PERF_RECORD_LOST] = event__all64_swap,
+ [PERF_RECORD_READ] = event__read_swap,
+ [PERF_RECORD_SAMPLE] = event__all64_swap,
+ [PERF_RECORD_HEADER_ATTR] = event__attr_swap,
+ [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap,
+ [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap,
+ [PERF_RECORD_HEADER_BUILD_ID] = NULL,
+ [PERF_RECORD_HEADER_MAX] = NULL,
+};
+
+struct sample_queue {
+ u64 timestamp;
+ u64 file_offset;
+ event_t *event;
+ struct list_head list;
+};
+
+static void perf_session_free_sample_buffers(struct perf_session *session)
+{
+ struct ordered_samples *os = &session->ordered_samples;
+
+ while (!list_empty(&os->to_free)) {
+ struct sample_queue *sq;
+
+ sq = list_entry(os->to_free.next, struct sample_queue, list);
+ list_del(&sq->list);
+ free(sq);
+ }
+}
+
+static int perf_session_deliver_event(struct perf_session *session,
+ event_t *event,
+ struct sample_data *sample,
+ struct perf_event_ops *ops,
+ u64 file_offset);
+
+static void flush_sample_queue(struct perf_session *s,
+ struct perf_event_ops *ops)
+{
+ struct ordered_samples *os = &s->ordered_samples;
+ struct list_head *head = &os->samples;
+ struct sample_queue *tmp, *iter;
+ struct sample_data sample;
+ u64 limit = os->next_flush;
+ u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL;
+
+ if (!ops->ordered_samples || !limit)
+ return;
+
+ list_for_each_entry_safe(iter, tmp, head, list) {
+ if (iter->timestamp > limit)
+ break;
+
+ event__parse_sample(iter->event, s, &sample);
+ perf_session_deliver_event(s, iter->event, &sample, ops,
+ iter->file_offset);
+
+ os->last_flush = iter->timestamp;
+ list_del(&iter->list);
+ list_add(&iter->list, &os->sample_cache);
+ }
+
+ if (list_empty(head)) {
+ os->last_sample = NULL;
+ } else if (last_ts <= limit) {
+ os->last_sample =
+ list_entry(head->prev, struct sample_queue, list);
+ }
+}
+
+/*
+ * When perf record finishes a pass on every buffers, it records this pseudo
+ * event.
+ * We record the max timestamp t found in the pass n.
+ * Assuming these timestamps are monotonic across cpus, we know that if
+ * a buffer still has events with timestamps below t, they will be all
+ * available and then read in the pass n + 1.
+ * Hence when we start to read the pass n + 2, we can safely flush every
+ * events with timestamps below t.
+ *
+ * ============ PASS n =================
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 1 | 2
+ * 2 | 3
+ * - | 4 <--- max recorded
+ *
+ * ============ PASS n + 1 ==============
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 3 | 5
+ * 4 | 6
+ * 5 | 7 <---- max recorded
+ *
+ * Flush every events below timestamp 4
+ *
+ * ============ PASS n + 2 ==============
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 6 | 8
+ * 7 | 9
+ * - | 10
+ *
+ * Flush every events below timestamp 7
+ * etc...
+ */
+static int process_finished_round(event_t *event __used,
+ struct perf_session *session,
+ struct perf_event_ops *ops)
+{
+ flush_sample_queue(session, ops);
+ session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
+
+ return 0;
+}
+
+/* The queue is ordered by time */
+static void __queue_event(struct sample_queue *new, struct perf_session *s)
+{
+ struct ordered_samples *os = &s->ordered_samples;
+ struct sample_queue *sample = os->last_sample;
+ u64 timestamp = new->timestamp;
+ struct list_head *p;
+
+ os->last_sample = new;
+
+ if (!sample) {
+ list_add(&new->list, &os->samples);
+ os->max_timestamp = timestamp;
+ return;
+ }
+
+ /*
+ * last_sample might point to some random place in the list as it's
+ * the last queued event. We expect that the new event is close to
+ * this.
+ */
+ if (sample->timestamp <= timestamp) {
+ while (sample->timestamp <= timestamp) {
+ p = sample->list.next;
+ if (p == &os->samples) {
+ list_add_tail(&new->list, &os->samples);
+ os->max_timestamp = timestamp;
+ return;
+ }
+ sample = list_entry(p, struct sample_queue, list);
+ }
+ list_add_tail(&new->list, &sample->list);
+ } else {
+ while (sample->timestamp > timestamp) {
+ p = sample->list.prev;
+ if (p == &os->samples) {
+ list_add(&new->list, &os->samples);
+ return;
+ }
+ sample = list_entry(p, struct sample_queue, list);
+ }
+ list_add(&new->list, &sample->list);
+ }
+}
+
+#define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue))
+
+static int perf_session_queue_event(struct perf_session *s, event_t *event,
+ struct sample_data *data, u64 file_offset)
+{
+ struct ordered_samples *os = &s->ordered_samples;
+ struct list_head *sc = &os->sample_cache;
+ u64 timestamp = data->time;
+ struct sample_queue *new;
+
+ if (!timestamp || timestamp == ~0ULL)
+ return -ETIME;
+
+ if (timestamp < s->ordered_samples.last_flush) {
+ printf("Warning: Timestamp below last timeslice flush\n");
+ return -EINVAL;
+ }
+
+ if (!list_empty(sc)) {
+ new = list_entry(sc->next, struct sample_queue, list);
+ list_del(&new->list);
+ } else if (os->sample_buffer) {
+ new = os->sample_buffer + os->sample_buffer_idx;
+ if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER)
+ os->sample_buffer = NULL;
+ } else {
+ os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new));
+ if (!os->sample_buffer)
+ return -ENOMEM;
+ list_add(&os->sample_buffer->list, &os->to_free);
+ os->sample_buffer_idx = 2;
+ new = os->sample_buffer + 1;
+ }
+
+ new->timestamp = timestamp;
+ new->file_offset = file_offset;
+ new->event = event;
+
+ __queue_event(new, s);
+
+ return 0;
+}
+
+static void callchain__printf(struct sample_data *sample)
+{
+ unsigned int i;
+
+ printf("... chain: nr:%" PRIu64 "\n", sample->callchain->nr);
+
+ for (i = 0; i < sample->callchain->nr; i++)
+ printf("..... %2d: %016" PRIx64 "\n",
+ i, sample->callchain->ips[i]);
+}
+
+static void perf_session__print_tstamp(struct perf_session *session,
+ event_t *event,
+ struct sample_data *sample)
+{
+ if (event->header.type != PERF_RECORD_SAMPLE &&
+ !session->sample_id_all) {
+ fputs("-1 -1 ", stdout);
+ return;
+ }
+
+ if ((session->sample_type & PERF_SAMPLE_CPU))
+ printf("%u ", sample->cpu);
+
+ if (session->sample_type & PERF_SAMPLE_TIME)
+ printf("%" PRIu64 " ", sample->time);
+}
+
+static void dump_event(struct perf_session *session, event_t *event,
+ u64 file_offset, struct sample_data *sample)
+{
+ if (!dump_trace)
+ return;
+
+ printf("\n%#" PRIx64 " [%#x]: event: %d\n",
+ file_offset, event->header.size, event->header.type);
+
+ trace_event(event);
+
+ if (sample)
+ perf_session__print_tstamp(session, event, sample);
+
+ printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset,
+ event->header.size, event__get_event_name(event->header.type));
+}
+
+static void dump_sample(struct perf_session *session, event_t *event,
+ struct sample_data *sample)
+{
+ if (!dump_trace)
+ return;
+
+ printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n",
+ event->header.misc, sample->pid, sample->tid, sample->ip,
+ sample->period);
+
+ if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
+ callchain__printf(sample);
+}
+
+static int perf_session_deliver_event(struct perf_session *session,
+ event_t *event,
+ struct sample_data *sample,
+ struct perf_event_ops *ops,
+ u64 file_offset)
+{
+ dump_event(session, event, file_offset, sample);
+
+ switch (event->header.type) {
+ case PERF_RECORD_SAMPLE:
+ dump_sample(session, event, sample);
+ return ops->sample(event, sample, session);
+ case PERF_RECORD_MMAP:
+ return ops->mmap(event, sample, session);
+ case PERF_RECORD_COMM:
+ return ops->comm(event, sample, session);
+ case PERF_RECORD_FORK:
+ return ops->fork(event, sample, session);
+ case PERF_RECORD_EXIT:
+ return ops->exit(event, sample, session);
+ case PERF_RECORD_LOST:
+ return ops->lost(event, sample, session);
+ case PERF_RECORD_READ:
+ return ops->read(event, sample, session);
+ case PERF_RECORD_THROTTLE:
+ return ops->throttle(event, sample, session);
+ case PERF_RECORD_UNTHROTTLE:
+ return ops->unthrottle(event, sample, session);
+ default:
+ ++session->hists.stats.nr_unknown_events;
+ return -1;
+ }
+}
+
+static int perf_session__preprocess_sample(struct perf_session *session,
+ event_t *event, struct sample_data *sample)
+{
+ if (event->header.type != PERF_RECORD_SAMPLE ||
+ !(session->sample_type & PERF_SAMPLE_CALLCHAIN))
+ return 0;
+
+ if (!ip_callchain__valid(sample->callchain, event)) {
+ pr_debug("call-chain problem with event, skipping it.\n");
+ ++session->hists.stats.nr_invalid_chains;
+ session->hists.stats.total_invalid_chains += sample->period;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int perf_session__process_user_event(struct perf_session *session, event_t *event,
+ struct perf_event_ops *ops, u64 file_offset)
+{
+ dump_event(session, event, file_offset, NULL);
+
+ /* These events are processed right away */
+ switch (event->header.type) {
+ case PERF_RECORD_HEADER_ATTR:
+ return ops->attr(event, session);
+ case PERF_RECORD_HEADER_EVENT_TYPE:
+ return ops->event_type(event, session);
+ case PERF_RECORD_HEADER_TRACING_DATA:
+ /* setup for reading amidst mmap */
+ lseek(session->fd, file_offset, SEEK_SET);
+ return ops->tracing_data(event, session);
+ case PERF_RECORD_HEADER_BUILD_ID:
+ return ops->build_id(event, session);
+ case PERF_RECORD_FINISHED_ROUND:
+ return ops->finished_round(event, session, ops);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int perf_session__process_event(struct perf_session *session,
+ event_t *event,
+ struct perf_event_ops *ops,
+ u64 file_offset)
+{
+ struct sample_data sample;
+ int ret;
+
+ if (session->header.needs_swap && event__swap_ops[event->header.type])
+ event__swap_ops[event->header.type](event);
+
+ if (event->header.type >= PERF_RECORD_HEADER_MAX)
+ return -EINVAL;
+
+ hists__inc_nr_events(&session->hists, event->header.type);
+
+ if (event->header.type >= PERF_RECORD_USER_TYPE_START)
+ return perf_session__process_user_event(session, event, ops, file_offset);
+
+ /*
+ * For all kernel events we get the sample data
+ */
+ event__parse_sample(event, session, &sample);
+
+ /* Preprocess sample records - precheck callchains */
+ if (perf_session__preprocess_sample(session, event, &sample))
+ return 0;
+
+ if (ops->ordered_samples) {
+ ret = perf_session_queue_event(session, event, &sample,
+ file_offset);
+ if (ret != -ETIME)
+ return ret;
+ }
+
+ return perf_session_deliver_event(session, event, &sample, ops,
+ file_offset);
+}
+
+void perf_event_header__bswap(struct perf_event_header *self)
+{
+ self->type = bswap_32(self->type);
+ self->misc = bswap_16(self->misc);
+ self->size = bswap_16(self->size);
+}
+
+static struct thread *perf_session__register_idle_thread(struct perf_session *self)
+{
+ struct thread *thread = perf_session__findnew(self, 0);
+
+ if (thread == NULL || thread__set_comm(thread, "swapper")) {
+ pr_err("problem inserting idle task.\n");
+ thread = NULL;
+ }
+
+ return thread;
+}
+
+static void perf_session__warn_about_errors(const struct perf_session *session,
+ const struct perf_event_ops *ops)
+{
+ if (ops->lost == event__process_lost &&
+ session->hists.stats.total_lost != 0) {
+ ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64
+ "!\n\nCheck IO/CPU overload!\n\n",
+ session->hists.stats.total_period,
+ session->hists.stats.total_lost);
+ }
+
+ if (session->hists.stats.nr_unknown_events != 0) {
+ ui__warning("Found %u unknown events!\n\n"
+ "Is this an older tool processing a perf.data "
+ "file generated by a more recent tool?\n\n"
+ "If that is not the case, consider "
+ "reporting to linux-kernel@vger.kernel.org.\n\n",
+ session->hists.stats.nr_unknown_events);
+ }
+
+ if (session->hists.stats.nr_invalid_chains != 0) {
+ ui__warning("Found invalid callchains!\n\n"
+ "%u out of %u events were discarded for this reason.\n\n"
+ "Consider reporting to linux-kernel@vger.kernel.org.\n\n",
+ session->hists.stats.nr_invalid_chains,
+ session->hists.stats.nr_events[PERF_RECORD_SAMPLE]);
+ }
+}
+
+#define session_done() (*(volatile int *)(&session_done))
+volatile int session_done;
+
+static int __perf_session__process_pipe_events(struct perf_session *self,
+ struct perf_event_ops *ops)
+{
+ event_t event;
+ uint32_t size;
+ int skip = 0;
+ u64 head;
+ int err;
+ void *p;
+
+ perf_event_ops__fill_defaults(ops);
+
+ head = 0;
+more:
+ err = readn(self->fd, &event, sizeof(struct perf_event_header));
+ if (err <= 0) {
+ if (err == 0)
+ goto done;
+
+ pr_err("failed to read event header\n");
+ goto out_err;
+ }
+
+ if (self->header.needs_swap)
+ perf_event_header__bswap(&event.header);
+
+ size = event.header.size;
+ if (size == 0)
+ size = 8;
+
+ p = &event;
+ p += sizeof(struct perf_event_header);
+
+ if (size - sizeof(struct perf_event_header)) {
+ err = readn(self->fd, p, size - sizeof(struct perf_event_header));
+ if (err <= 0) {
+ if (err == 0) {
+ pr_err("unexpected end of event stream\n");
+ goto done;
+ }
+
+ pr_err("failed to read event data\n");
+ goto out_err;
+ }
+ }
+
+ if (size == 0 ||
+ (skip = perf_session__process_event(self, &event, ops, head)) < 0) {
+ dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
+ head, event.header.size, event.header.type);
+ /*
+ * assume we lost track of the stream, check alignment, and
+ * increment a single u64 in the hope to catch on again 'soon'.
+ */
+ if (unlikely(head & 7))
+ head &= ~7ULL;
+
+ size = 8;
+ }
+
+ head += size;
+
+ if (skip > 0)
+ head += skip;
+
+ if (!session_done())
+ goto more;
+done:
+ err = 0;
+out_err:
+ perf_session__warn_about_errors(self, ops);
+ perf_session_free_sample_buffers(self);
+ return err;
+}
+
+int __perf_session__process_events(struct perf_session *session,
+ u64 data_offset, u64 data_size,
+ u64 file_size, struct perf_event_ops *ops)
+{
+ u64 head, page_offset, file_offset, file_pos, progress_next;
+ int err, mmap_prot, mmap_flags, map_idx = 0;
+ struct ui_progress *progress;
+ size_t page_size, mmap_size;
+ char *buf, *mmaps[8];
+ event_t *event;
+ uint32_t size;
+
+ perf_event_ops__fill_defaults(ops);
+
+ page_size = sysconf(_SC_PAGESIZE);
+
+ page_offset = page_size * (data_offset / page_size);
+ file_offset = page_offset;
+ head = data_offset - page_offset;
+
+ if (data_offset + data_size < file_size)
+ file_size = data_offset + data_size;
+
+ progress_next = file_size / 16;
+ progress = ui_progress__new("Processing events...", file_size);
+ if (progress == NULL)
+ return -1;
+
+ mmap_size = session->mmap_window;
+ if (mmap_size > file_size)
+ mmap_size = file_size;
+
+ memset(mmaps, 0, sizeof(mmaps));
+
+ mmap_prot = PROT_READ;
+ mmap_flags = MAP_SHARED;
+
+ if (session->header.needs_swap) {
+ mmap_prot |= PROT_WRITE;
+ mmap_flags = MAP_PRIVATE;
+ }
+remap:
+ buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd,
+ file_offset);
+ if (buf == MAP_FAILED) {
+ pr_err("failed to mmap file\n");
+ err = -errno;
+ goto out_err;
+ }
+ mmaps[map_idx] = buf;
+ map_idx = (map_idx + 1) & (ARRAY_SIZE(mmaps) - 1);
+ file_pos = file_offset + head;
+
+more:
+ event = (event_t *)(buf + head);
+
+ if (session->header.needs_swap)
+ perf_event_header__bswap(&event->header);
+ size = event->header.size;
+ if (size == 0)
+ size = 8;
+
+ if (head + event->header.size > mmap_size) {
+ if (mmaps[map_idx]) {
+ munmap(mmaps[map_idx], mmap_size);
+ mmaps[map_idx] = NULL;
+ }
+
+ page_offset = page_size * (head / page_size);
+ file_offset += page_offset;
+ head -= page_offset;
+ goto remap;
+ }
+
+ size = event->header.size;
+
+ if (size == 0 ||
+ perf_session__process_event(session, event, ops, file_pos) < 0) {
+ dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
+ file_offset + head, event->header.size,
+ event->header.type);
+ /*
+ * assume we lost track of the stream, check alignment, and
+ * increment a single u64 in the hope to catch on again 'soon'.
+ */
+ if (unlikely(head & 7))
+ head &= ~7ULL;
+
+ size = 8;
+ }
+
+ head += size;
+ file_pos += size;
+
+ if (file_pos >= progress_next) {
+ progress_next += file_size / 16;
+ ui_progress__update(progress, file_pos);
+ }
+
+ if (file_pos < file_size)
+ goto more;
+
+ err = 0;
+ /* do the final flush for ordered samples */
+ session->ordered_samples.next_flush = ULLONG_MAX;
+ flush_sample_queue(session, ops);
+out_err:
+ ui_progress__delete(progress);
+ perf_session__warn_about_errors(session, ops);
+ perf_session_free_sample_buffers(session);
+ return err;
+}
+
+int perf_session__process_events(struct perf_session *self,
+ struct perf_event_ops *ops)
+{
+ int err;
+
+ if (perf_session__register_idle_thread(self) == NULL)
+ return -ENOMEM;
+
+ if (!self->fd_pipe)
+ err = __perf_session__process_events(self,
+ self->header.data_offset,
+ self->header.data_size,
+ self->size, ops);
+ else
+ err = __perf_session__process_pipe_events(self, ops);
+
+ return err;
+}
+
+bool perf_session__has_traces(struct perf_session *self, const char *msg)
+{
+ if (!(self->sample_type & PERF_SAMPLE_RAW)) {
+ pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
+ return false;
+ }
+
+ return true;
+}
+
+int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
+ const char *symbol_name,
+ u64 addr)
+{
+ char *bracket;
+ enum map_type i;
+ struct ref_reloc_sym *ref;
+
+ ref = zalloc(sizeof(struct ref_reloc_sym));
+ if (ref == NULL)
+ return -ENOMEM;
+
+ ref->name = strdup(symbol_name);
+ if (ref->name == NULL) {
+ free(ref);
+ return -ENOMEM;
+ }
+
+ bracket = strchr(ref->name, ']');
+ if (bracket)
+ *bracket = '\0';
+
+ ref->addr = addr;
+
+ for (i = 0; i < MAP__NR_TYPES; ++i) {
+ struct kmap *kmap = map__kmap(maps[i]);
+ kmap->ref_reloc_sym = ref;
+ }
+
+ return 0;
+}
+
+size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
+{
+ return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) +
+ __dsos__fprintf(&self->host_machine.user_dsos, fp) +
+ machines__fprintf_dsos(&self->machines, fp);
+}
+
+size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
+ bool with_hits)
+{
+ size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
+ return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
+}
diff --git a/smartt-perf/util/session.h b/smartt-perf/util/session.h
new file mode 100644
index 0000000..1f63829
--- /dev/null
+++ b/smartt-perf/util/session.h
@@ -0,0 +1,157 @@
+#ifndef __PERF_SESSION_H
+#define __PERF_SESSION_H
+
+#include "hist.h"
+#include "event.h"
+#include "header.h"
+#include "symbol.h"
+#include "thread.h"
+#include <linux/rbtree.h>
+#include "linux/perf_event.h"
+
+struct sample_queue;
+struct ip_callchain;
+struct thread;
+
+struct ordered_samples {
+ u64 last_flush;
+ u64 next_flush;
+ u64 max_timestamp;
+ struct list_head samples;
+ struct list_head sample_cache;
+ struct list_head to_free;
+ struct sample_queue *sample_buffer;
+ struct sample_queue *last_sample;
+ int sample_buffer_idx;
+};
+
+struct perf_session {
+ struct perf_header header;
+ unsigned long size;
+ unsigned long mmap_window;
+ struct rb_root threads;
+ struct list_head dead_threads;
+ struct thread *last_match;
+ struct machine host_machine;
+ struct rb_root machines;
+ struct rb_root hists_tree;
+ /*
+ * FIXME: should point to the first entry in hists_tree and
+ * be a hists instance. Right now its only 'report'
+ * that is using ->hists_tree while all the rest use
+ * ->hists.
+ */
+ struct hists hists;
+ u64 sample_type;
+ int fd;
+ bool fd_pipe;
+ bool repipe;
+ bool sample_id_all;
+ u16 id_hdr_size;
+ int cwdlen;
+ char *cwd;
+ struct ordered_samples ordered_samples;
+ char filename[0];
+};
+
+struct perf_event_ops;
+
+typedef int (*event_op)(event_t *self, struct sample_data *sample,
+ struct perf_session *session);
+typedef int (*event_synth_op)(event_t *self, struct perf_session *session);
+typedef int (*event_op2)(event_t *self, struct perf_session *session,
+ struct perf_event_ops *ops);
+
+struct perf_event_ops {
+ event_op sample,
+ mmap,
+ comm,
+ fork,
+ exit,
+ lost,
+ read,
+ throttle,
+ unthrottle;
+ event_synth_op attr,
+ event_type,
+ tracing_data,
+ build_id;
+ event_op2 finished_round;
+ bool ordered_samples;
+ bool ordering_requires_timestamps;
+};
+
+struct perf_session *perf_session__new(const char *filename, int mode,
+ bool force, bool repipe,
+ struct perf_event_ops *ops);
+void perf_session__delete(struct perf_session *self);
+
+void perf_event_header__bswap(struct perf_event_header *self);
+
+int __perf_session__process_events(struct perf_session *self,
+ u64 data_offset, u64 data_size, u64 size,
+ struct perf_event_ops *ops);
+int perf_session__process_events(struct perf_session *self,
+ struct perf_event_ops *event_ops);
+
+struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
+ struct thread *thread,
+ struct ip_callchain *chain,
+ struct symbol **parent);
+
+bool perf_session__has_traces(struct perf_session *self, const char *msg);
+
+int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
+ const char *symbol_name,
+ u64 addr);
+
+void mem_bswap_64(void *src, int byte_size);
+
+int perf_session__create_kernel_maps(struct perf_session *self);
+
+void perf_session__update_sample_type(struct perf_session *self);
+void perf_session__set_sample_id_all(struct perf_session *session, bool value);
+void perf_session__set_sample_type(struct perf_session *session, u64 type);
+void perf_session__remove_thread(struct perf_session *self, struct thread *th);
+
+static inline
+struct machine *perf_session__find_host_machine(struct perf_session *self)
+{
+ return &self->host_machine;
+}
+
+static inline
+struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid)
+{
+ if (pid == HOST_KERNEL_ID)
+ return &self->host_machine;
+ return machines__find(&self->machines, pid);
+}
+
+static inline
+struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid)
+{
+ if (pid == HOST_KERNEL_ID)
+ return &self->host_machine;
+ return machines__findnew(&self->machines, pid);
+}
+
+static inline
+void perf_session__process_machines(struct perf_session *self,
+ machine__process_t process)
+{
+ process(&self->host_machine, self);
+ return machines__process(&self->machines, process, self);
+}
+
+size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
+
+size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
+ FILE *fp, bool with_hits);
+
+static inline
+size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
+{
+ return hists__fprintf_nr_events(&self->hists, fp);
+}
+#endif /* __PERF_SESSION_H */
diff --git a/smartt-perf/util/sigchain.c b/smartt-perf/util/sigchain.c
new file mode 100644
index 0000000..ba785e9
--- /dev/null
+++ b/smartt-perf/util/sigchain.c
@@ -0,0 +1,52 @@
+#include "sigchain.h"
+#include "cache.h"
+
+#define SIGCHAIN_MAX_SIGNALS 32
+
+struct sigchain_signal {
+ sigchain_fun *old;
+ int n;
+ int alloc;
+};
+static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];
+
+static void check_signum(int sig)
+{
+ if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)
+ die("BUG: signal out of range: %d", sig);
+}
+
+static int sigchain_push(int sig, sigchain_fun f)
+{
+ struct sigchain_signal *s = signals + sig;
+ check_signum(sig);
+
+ ALLOC_GROW(s->old, s->n + 1, s->alloc);
+ s->old[s->n] = signal(sig, f);
+ if (s->old[s->n] == SIG_ERR)
+ return -1;
+ s->n++;
+ return 0;
+}
+
+int sigchain_pop(int sig)
+{
+ struct sigchain_signal *s = signals + sig;
+ check_signum(sig);
+ if (s->n < 1)
+ return 0;
+
+ if (signal(sig, s->old[s->n - 1]) == SIG_ERR)
+ return -1;
+ s->n--;
+ return 0;
+}
+
+void sigchain_push_common(sigchain_fun f)
+{
+ sigchain_push(SIGINT, f);
+ sigchain_push(SIGHUP, f);
+ sigchain_push(SIGTERM, f);
+ sigchain_push(SIGQUIT, f);
+ sigchain_push(SIGPIPE, f);
+}
diff --git a/smartt-perf/util/sigchain.h b/smartt-perf/util/sigchain.h
new file mode 100644
index 0000000..959d64e
--- /dev/null
+++ b/smartt-perf/util/sigchain.h
@@ -0,0 +1,10 @@
+#ifndef __PERF_SIGCHAIN_H
+#define __PERF_SIGCHAIN_H
+
+typedef void (*sigchain_fun)(int);
+
+int sigchain_pop(int sig);
+
+void sigchain_push_common(sigchain_fun f);
+
+#endif /* __PERF_SIGCHAIN_H */
diff --git a/smartt-perf/util/sort.c b/smartt-perf/util/sort.c
new file mode 100644
index 0000000..f44fa54
--- /dev/null
+++ b/smartt-perf/util/sort.c
@@ -0,0 +1,345 @@
+#include "sort.h"
+#include "hist.h"
+
+regex_t parent_regex;
+const char default_parent_pattern[] = "^sys_|^do_page_fault";
+const char *parent_pattern = default_parent_pattern;
+const char default_sort_order[] = "comm,dso,symbol";
+const char *sort_order = default_sort_order;
+int sort__need_collapse = 0;
+int sort__has_parent = 0;
+
+enum sort_type sort__first_dimension;
+
+char * field_sep;
+
+LIST_HEAD(hist_entry__sort_list);
+
+static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+
+struct sort_entry sort_thread = {
+ .se_header = "Command: Pid",
+ .se_cmp = sort__thread_cmp,
+ .se_snprintf = hist_entry__thread_snprintf,
+ .se_width_idx = HISTC_THREAD,
+};
+
+struct sort_entry sort_comm = {
+ .se_header = "Command",
+ .se_cmp = sort__comm_cmp,
+ .se_collapse = sort__comm_collapse,
+ .se_snprintf = hist_entry__comm_snprintf,
+ .se_width_idx = HISTC_COMM,
+};
+
+struct sort_entry sort_dso = {
+ .se_header = "Shared Object",
+ .se_cmp = sort__dso_cmp,
+ .se_snprintf = hist_entry__dso_snprintf,
+ .se_width_idx = HISTC_DSO,
+};
+
+struct sort_entry sort_sym = {
+ .se_header = "Symbol",
+ .se_cmp = sort__sym_cmp,
+ .se_snprintf = hist_entry__sym_snprintf,
+ .se_width_idx = HISTC_SYMBOL,
+};
+
+struct sort_entry sort_parent = {
+ .se_header = "Parent symbol",
+ .se_cmp = sort__parent_cmp,
+ .se_snprintf = hist_entry__parent_snprintf,
+ .se_width_idx = HISTC_PARENT,
+};
+
+struct sort_entry sort_cpu = {
+ .se_header = "CPU",
+ .se_cmp = sort__cpu_cmp,
+ .se_snprintf = hist_entry__cpu_snprintf,
+ .se_width_idx = HISTC_CPU,
+};
+
+struct sort_dimension {
+ const char *name;
+ struct sort_entry *entry;
+ int taken;
+};
+
+static struct sort_dimension sort_dimensions[] = {
+ { .name = "pid", .entry = &sort_thread, },
+ { .name = "comm", .entry = &sort_comm, },
+ { .name = "dso", .entry = &sort_dso, },
+ { .name = "symbol", .entry = &sort_sym, },
+ { .name = "parent", .entry = &sort_parent, },
+ { .name = "cpu", .entry = &sort_cpu, },
+};
+
+int64_t cmp_null(void *l, void *r)
+{
+ if (!l && !r)
+ return 0;
+ else if (!l)
+ return -1;
+ else
+ return 1;
+}
+
+/* --sort pid */
+
+int64_t
+sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->thread->pid - left->thread->pid;
+}
+
+static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+
+ va_start(ap, fmt);
+ n = vsnprintf(bf, size, fmt, ap);
+ if (field_sep && n > 0) {
+ char *sep = bf;
+
+ while (1) {
+ sep = strchr(sep, *field_sep);
+ if (sep == NULL)
+ break;
+ *sep = '.';
+ }
+ }
+ va_end(ap);
+ return n;
+}
+
+static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%*s:%5d", width,
+ self->thread->comm ?: "", self->thread->pid);
+}
+
+static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
+}
+
+/* --sort dso */
+
+int64_t
+sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
+ struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
+ const char *dso_name_l, *dso_name_r;
+
+ if (!dso_l || !dso_r)
+ return cmp_null(dso_l, dso_r);
+
+ if (verbose) {
+ dso_name_l = dso_l->long_name;
+ dso_name_r = dso_r->long_name;
+ } else {
+ dso_name_l = dso_l->short_name;
+ dso_name_r = dso_r->short_name;
+ }
+
+ return strcmp(dso_name_l, dso_name_r);
+}
+
+static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ if (self->ms.map && self->ms.map->dso) {
+ const char *dso_name = !verbose ? self->ms.map->dso->short_name :
+ self->ms.map->dso->long_name;
+ return repsep_snprintf(bf, size, "%-*s", width, dso_name);
+ }
+
+ return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
+}
+
+/* --sort symbol */
+
+int64_t
+sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ u64 ip_l, ip_r;
+
+ if (left->ms.sym == right->ms.sym)
+ return 0;
+
+ ip_l = left->ms.sym ? left->ms.sym->start : left->ip;
+ ip_r = right->ms.sym ? right->ms.sym->start : right->ip;
+
+ return (int64_t)(ip_r - ip_l);
+}
+
+static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ size_t ret = 0;
+
+ if (verbose) {
+ char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
+ ret += repsep_snprintf(bf, size, "%-#*llx %c ",
+ BITS_PER_LONG / 4, self->ip, o);
+ }
+
+ ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
+ if (self->ms.sym)
+ ret += repsep_snprintf(bf + ret, size - ret, "%s",
+ self->ms.sym->name);
+ else
+ ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
+ BITS_PER_LONG / 4, self->ip);
+
+ return ret;
+}
+
+/* --sort comm */
+
+int64_t
+sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->thread->pid - left->thread->pid;
+}
+
+int64_t
+sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
+{
+ char *comm_l = left->thread->comm;
+ char *comm_r = right->thread->comm;
+
+ if (!comm_l || !comm_r)
+ return cmp_null(comm_l, comm_r);
+
+ return strcmp(comm_l, comm_r);
+}
+
+/* --sort parent */
+
+int64_t
+sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct symbol *sym_l = left->parent;
+ struct symbol *sym_r = right->parent;
+
+ if (!sym_l || !sym_r)
+ return cmp_null(sym_l, sym_r);
+
+ return strcmp(sym_l->name, sym_r->name);
+}
+
+static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%-*s", width,
+ self->parent ? self->parent->name : "[other]");
+}
+
+/* --sort cpu */
+
+int64_t
+sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->cpu - left->cpu;
+}
+
+static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
+}
+
+int sort_dimension__add(const char *tok)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
+ struct sort_dimension *sd = &sort_dimensions[i];
+
+ if (sd->taken)
+ continue;
+
+ if (strncasecmp(tok, sd->name, strlen(tok)))
+ continue;
+
+ if (sd->entry->se_collapse)
+ sort__need_collapse = 1;
+
+ if (sd->entry == &sort_parent) {
+ int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
+ if (ret) {
+ char err[BUFSIZ];
+
+ regerror(ret, &parent_regex, err, sizeof(err));
+ pr_err("Invalid regex: %s\n%s", parent_pattern, err);
+ return -EINVAL;
+ }
+ sort__has_parent = 1;
+ }
+
+ if (list_empty(&hist_entry__sort_list)) {
+ if (!strcmp(sd->name, "pid"))
+ sort__first_dimension = SORT_PID;
+ else if (!strcmp(sd->name, "comm"))
+ sort__first_dimension = SORT_COMM;
+ else if (!strcmp(sd->name, "dso"))
+ sort__first_dimension = SORT_DSO;
+ else if (!strcmp(sd->name, "symbol"))
+ sort__first_dimension = SORT_SYM;
+ else if (!strcmp(sd->name, "parent"))
+ sort__first_dimension = SORT_PARENT;
+ else if (!strcmp(sd->name, "cpu"))
+ sort__first_dimension = SORT_CPU;
+ }
+
+ list_add_tail(&sd->entry->list, &hist_entry__sort_list);
+ sd->taken = 1;
+
+ return 0;
+ }
+
+ return -ESRCH;
+}
+
+void setup_sorting(const char * const usagestr[], const struct option *opts)
+{
+ char *tmp, *tok, *str = strdup(sort_order);
+
+ for (tok = strtok_r(str, ", ", &tmp);
+ tok; tok = strtok_r(NULL, ", ", &tmp)) {
+ if (sort_dimension__add(tok) < 0) {
+ error("Unknown --sort key: `%s'", tok);
+ usage_with_options(usagestr, opts);
+ }
+ }
+
+ free(str);
+}
+
+void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
+ const char *list_name, FILE *fp)
+{
+ if (list && strlist__nr_entries(list) == 1) {
+ if (fp != NULL)
+ fprintf(fp, "# %s: %s\n", list_name,
+ strlist__entry(list, 0)->s);
+ self->elide = true;
+ }
+}
diff --git a/smartt-perf/util/sort.h b/smartt-perf/util/sort.h
new file mode 100644
index 0000000..0b91053
--- /dev/null
+++ b/smartt-perf/util/sort.h
@@ -0,0 +1,124 @@
+#ifndef __PERF_SORT_H
+#define __PERF_SORT_H
+#include "../builtin.h"
+
+#include "util.h"
+
+#include "color.h"
+#include <linux/list.h>
+#include "cache.h"
+#include <linux/rbtree.h>
+#include "symbol.h"
+#include "string.h"
+#include "callchain.h"
+#include "strlist.h"
+#include "values.h"
+
+#include "../perf.h"
+#include "debug.h"
+#include "header.h"
+
+#include "parse-options.h"
+#include "parse-events.h"
+
+#include "thread.h"
+#include "sort.h"
+
+extern regex_t parent_regex;
+extern const char *sort_order;
+extern const char default_parent_pattern[];
+extern const char *parent_pattern;
+extern const char default_sort_order[];
+extern int sort__need_collapse;
+extern int sort__has_parent;
+extern char *field_sep;
+extern struct sort_entry sort_comm;
+extern struct sort_entry sort_dso;
+extern struct sort_entry sort_sym;
+extern struct sort_entry sort_parent;
+extern enum sort_type sort__first_dimension;
+
+/**
+ * struct hist_entry - histogram entry
+ *
+ * @row_offset - offset from the first callchain expanded to appear on screen
+ * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding
+ */
+struct hist_entry {
+ struct rb_node rb_node;
+ u64 period;
+ u64 period_sys;
+ u64 period_us;
+ u64 period_guest_sys;
+ u64 period_guest_us;
+ struct map_symbol ms;
+ struct thread *thread;
+ u64 ip;
+ s32 cpu;
+ u32 nr_events;
+
+ /* XXX These two should move to some tree widget lib */
+ u16 row_offset;
+ u16 nr_rows;
+
+ bool init_have_children;
+ char level;
+ u8 filtered;
+ struct symbol *parent;
+ union {
+ unsigned long position;
+ struct hist_entry *pair;
+ struct rb_root sorted_chain;
+ };
+ struct callchain_root callchain[0];
+};
+
+enum sort_type {
+ SORT_PID,
+ SORT_COMM,
+ SORT_DSO,
+ SORT_SYM,
+ SORT_PARENT,
+ SORT_CPU,
+};
+
+/*
+ * configurable sorting bits
+ */
+
+struct sort_entry {
+ struct list_head list;
+
+ const char *se_header;
+
+ int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
+ int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
+ int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
+ unsigned int width);
+ u8 se_width_idx;
+ bool elide;
+};
+
+extern struct sort_entry sort_thread;
+extern struct list_head hist_entry__sort_list;
+
+void setup_sorting(const char * const usagestr[], const struct option *opts);
+
+extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used);
+extern int64_t cmp_null(void *, void *);
+extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
+int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
+extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
+extern int sort_dimension__add(const char *);
+void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
+ const char *list_name, FILE *fp);
+
+#endif /* __PERF_SORT_H */
diff --git a/smartt-perf/util/strbuf.c b/smartt-perf/util/strbuf.c
new file mode 100644
index 0000000..92e0685
--- /dev/null
+++ b/smartt-perf/util/strbuf.c
@@ -0,0 +1,133 @@
+#include "cache.h"
+
+int prefixcmp(const char *str, const char *prefix)
+{
+ for (; ; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+/*
+ * Used as the default ->buf value, so that people can always assume
+ * buf is non NULL and ->buf is NUL terminated even for a freshly
+ * initialized strbuf.
+ */
+char strbuf_slopbuf[1];
+
+void strbuf_init(struct strbuf *sb, ssize_t hint)
+{
+ sb->alloc = sb->len = 0;
+ sb->buf = strbuf_slopbuf;
+ if (hint)
+ strbuf_grow(sb, hint);
+}
+
+void strbuf_release(struct strbuf *sb)
+{
+ if (sb->alloc) {
+ free(sb->buf);
+ strbuf_init(sb, 0);
+ }
+}
+
+char *strbuf_detach(struct strbuf *sb, size_t *sz)
+{
+ char *res = sb->alloc ? sb->buf : NULL;
+ if (sz)
+ *sz = sb->len;
+ strbuf_init(sb, 0);
+ return res;
+}
+
+void strbuf_grow(struct strbuf *sb, size_t extra)
+{
+ if (sb->len + extra + 1 <= sb->len)
+ die("you want to use way too much memory");
+ if (!sb->alloc)
+ sb->buf = NULL;
+ ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
+}
+
+static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
+ const void *data, size_t dlen)
+{
+ if (pos + len < pos)
+ die("you want to use way too much memory");
+ if (pos > sb->len)
+ die("`pos' is too far after the end of the buffer");
+ if (pos + len > sb->len)
+ die("`pos + len' is too far after the end of the buffer");
+
+ if (dlen >= len)
+ strbuf_grow(sb, dlen - len);
+ memmove(sb->buf + pos + dlen,
+ sb->buf + pos + len,
+ sb->len - pos - len);
+ memcpy(sb->buf + pos, data, dlen);
+ strbuf_setlen(sb, sb->len + dlen - len);
+}
+
+void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
+{
+ strbuf_splice(sb, pos, len, NULL, 0);
+}
+
+void strbuf_add(struct strbuf *sb, const void *data, size_t len)
+{
+ strbuf_grow(sb, len);
+ memcpy(sb->buf + sb->len, data, len);
+ strbuf_setlen(sb, sb->len + len);
+}
+
+void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
+{
+ int len;
+ va_list ap;
+
+ if (!strbuf_avail(sb))
+ strbuf_grow(sb, 64);
+ va_start(ap, fmt);
+ len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+ va_end(ap);
+ if (len < 0)
+ die("your vsnprintf is broken");
+ if (len > strbuf_avail(sb)) {
+ strbuf_grow(sb, len);
+ va_start(ap, fmt);
+ len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+ va_end(ap);
+ if (len > strbuf_avail(sb)) {
+ die("this should not happen, your snprintf is broken");
+ }
+ }
+ strbuf_setlen(sb, sb->len + len);
+}
+
+ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
+{
+ size_t oldlen = sb->len;
+ size_t oldalloc = sb->alloc;
+
+ strbuf_grow(sb, hint ? hint : 8192);
+ for (;;) {
+ ssize_t cnt;
+
+ cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
+ if (cnt < 0) {
+ if (oldalloc == 0)
+ strbuf_release(sb);
+ else
+ strbuf_setlen(sb, oldlen);
+ return -1;
+ }
+ if (!cnt)
+ break;
+ sb->len += cnt;
+ strbuf_grow(sb, 8192);
+ }
+
+ sb->buf[sb->len] = '\0';
+ return sb->len - oldlen;
+}
diff --git a/smartt-perf/util/strbuf.h b/smartt-perf/util/strbuf.h
new file mode 100644
index 0000000..436ac31
--- /dev/null
+++ b/smartt-perf/util/strbuf.h
@@ -0,0 +1,92 @@
+#ifndef __PERF_STRBUF_H
+#define __PERF_STRBUF_H
+
+/*
+ * Strbuf's can be use in many ways: as a byte array, or to store arbitrary
+ * long, overflow safe strings.
+ *
+ * Strbufs has some invariants that are very important to keep in mind:
+ *
+ * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to
+ * build complex strings/buffers whose final size isn't easily known.
+ *
+ * It is NOT legal to copy the ->buf pointer away.
+ * `strbuf_detach' is the operation that detachs a buffer from its shell
+ * while keeping the shell valid wrt its invariants.
+ *
+ * 2. the ->buf member is a byte array that has at least ->len + 1 bytes
+ * allocated. The extra byte is used to store a '\0', allowing the ->buf
+ * member to be a valid C-string. Every strbuf function ensure this
+ * invariant is preserved.
+ *
+ * Note that it is OK to "play" with the buffer directly if you work it
+ * that way:
+ *
+ * strbuf_grow(sb, SOME_SIZE);
+ * ... Here, the memory array starting at sb->buf, and of length
+ * ... strbuf_avail(sb) is all yours, and you are sure that
+ * ... strbuf_avail(sb) is at least SOME_SIZE.
+ * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE);
+ *
+ * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb).
+ *
+ * Doing so is safe, though if it has to be done in many places, adding the
+ * missing API to the strbuf module is the way to go.
+ *
+ * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1
+ * even if it's true in the current implementation. Alloc is somehow a
+ * "private" member that should not be messed with.
+ */
+
+#include <assert.h>
+
+extern char strbuf_slopbuf[];
+struct strbuf {
+ size_t alloc;
+ size_t len;
+ char *buf;
+};
+
+#define STRBUF_INIT { 0, 0, strbuf_slopbuf }
+
+/*----- strbuf life cycle -----*/
+extern void strbuf_init(struct strbuf *buf, ssize_t hint);
+extern void strbuf_release(struct strbuf *);
+extern char *strbuf_detach(struct strbuf *, size_t *);
+
+/*----- strbuf size related -----*/
+static inline ssize_t strbuf_avail(const struct strbuf *sb) {
+ return sb->alloc ? sb->alloc - sb->len - 1 : 0;
+}
+
+extern void strbuf_grow(struct strbuf *, size_t);
+
+static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
+ if (!sb->alloc)
+ strbuf_grow(sb, 0);
+ assert(len < sb->alloc);
+ sb->len = len;
+ sb->buf[len] = '\0';
+}
+
+/*----- add data in your buffer -----*/
+static inline void strbuf_addch(struct strbuf *sb, int c) {
+ strbuf_grow(sb, 1);
+ sb->buf[sb->len++] = c;
+ sb->buf[sb->len] = '\0';
+}
+
+extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
+
+extern void strbuf_add(struct strbuf *, const void *, size_t);
+static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
+ strbuf_add(sb, s, strlen(s));
+}
+
+__attribute__((format(printf,2,3)))
+extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+
+/* XXX: if read fails, any partial read is undone */
+extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
+
+#endif /* __PERF_STRBUF_H */
diff --git a/smartt-perf/util/string.c b/smartt-perf/util/string.c
new file mode 100644
index 0000000..8fc0bd3
--- /dev/null
+++ b/smartt-perf/util/string.c
@@ -0,0 +1,296 @@
+#include "util.h"
+#include "string.h"
+
+#define K 1024LL
+/*
+ * perf_atoll()
+ * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
+ * and return its numeric value
+ */
+s64 perf_atoll(const char *str)
+{
+ unsigned int i;
+ s64 length = -1, unit = 1;
+
+ if (!isdigit(str[0]))
+ goto out_err;
+
+ for (i = 1; i < strlen(str); i++) {
+ switch (str[i]) {
+ case 'B':
+ case 'b':
+ break;
+ case 'K':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto kilo;
+ case 'k':
+ if (str[i + 1] != 'b')
+ goto out_err;
+kilo:
+ unit = K;
+ break;
+ case 'M':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto mega;
+ case 'm':
+ if (str[i + 1] != 'b')
+ goto out_err;
+mega:
+ unit = K * K;
+ break;
+ case 'G':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto giga;
+ case 'g':
+ if (str[i + 1] != 'b')
+ goto out_err;
+giga:
+ unit = K * K * K;
+ break;
+ case 'T':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto tera;
+ case 't':
+ if (str[i + 1] != 'b')
+ goto out_err;
+tera:
+ unit = K * K * K * K;
+ break;
+ case '\0': /* only specified figures */
+ unit = 1;
+ break;
+ default:
+ if (!isdigit(str[i]))
+ goto out_err;
+ break;
+ }
+ }
+
+ length = atoll(str) * unit;
+ goto out;
+
+out_err:
+ length = -1;
+out:
+ return length;
+}
+
+/*
+ * Helper function for splitting a string into an argv-like array.
+ * originaly copied from lib/argv_split.c
+ */
+static const char *skip_sep(const char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+
+ return cp;
+}
+
+static const char *skip_arg(const char *cp)
+{
+ while (*cp && !isspace(*cp))
+ cp++;
+
+ return cp;
+}
+
+static int count_argc(const char *str)
+{
+ int count = 0;
+
+ while (*str) {
+ str = skip_sep(str);
+ if (*str) {
+ count++;
+ str = skip_arg(str);
+ }
+ }
+
+ return count;
+}
+
+/**
+ * argv_free - free an argv
+ * @argv - the argument vector to be freed
+ *
+ * Frees an argv and the strings it points to.
+ */
+void argv_free(char **argv)
+{
+ char **p;
+ for (p = argv; *p; p++)
+ free(*p);
+
+ free(argv);
+}
+
+/**
+ * argv_split - split a string at whitespace, returning an argv
+ * @str: the string to be split
+ * @argcp: returned argument count
+ *
+ * Returns an array of pointers to strings which are split out from
+ * @str. This is performed by strictly splitting on white-space; no
+ * quote processing is performed. Multiple whitespace characters are
+ * considered to be a single argument separator. The returned array
+ * is always NULL-terminated. Returns NULL on memory allocation
+ * failure.
+ */
+char **argv_split(const char *str, int *argcp)
+{
+ int argc = count_argc(str);
+ char **argv = zalloc(sizeof(*argv) * (argc+1));
+ char **argvp;
+
+ if (argv == NULL)
+ goto out;
+
+ if (argcp)
+ *argcp = argc;
+
+ argvp = argv;
+
+ while (*str) {
+ str = skip_sep(str);
+
+ if (*str) {
+ const char *p = str;
+ char *t;
+
+ str = skip_arg(str);
+
+ t = strndup(p, str-p);
+ if (t == NULL)
+ goto fail;
+ *argvp++ = t;
+ }
+ }
+ *argvp = NULL;
+
+out:
+ return argv;
+
+fail:
+ argv_free(argv);
+ return NULL;
+}
+
+/* Character class matching */
+static bool __match_charclass(const char *pat, char c, const char **npat)
+{
+ bool complement = false, ret = true;
+
+ if (*pat == '!') {
+ complement = true;
+ pat++;
+ }
+ if (*pat++ == c) /* First character is special */
+ goto end;
+
+ while (*pat && *pat != ']') { /* Matching */
+ if (*pat == '-' && *(pat + 1) != ']') { /* Range */
+ if (*(pat - 1) <= c && c <= *(pat + 1))
+ goto end;
+ if (*(pat - 1) > *(pat + 1))
+ goto error;
+ pat += 2;
+ } else if (*pat++ == c)
+ goto end;
+ }
+ if (!*pat)
+ goto error;
+ ret = false;
+
+end:
+ while (*pat && *pat != ']') /* Searching closing */
+ pat++;
+ if (!*pat)
+ goto error;
+ *npat = pat + 1;
+ return complement ? !ret : ret;
+
+error:
+ return false;
+}
+
+/* Glob/lazy pattern matching */
+static bool __match_glob(const char *str, const char *pat, bool ignore_space)
+{
+ while (*str && *pat && *pat != '*') {
+ if (ignore_space) {
+ /* Ignore spaces for lazy matching */
+ if (isspace(*str)) {
+ str++;
+ continue;
+ }
+ if (isspace(*pat)) {
+ pat++;
+ continue;
+ }
+ }
+ if (*pat == '?') { /* Matches any single character */
+ str++;
+ pat++;
+ continue;
+ } else if (*pat == '[') /* Character classes/Ranges */
+ if (__match_charclass(pat + 1, *str, &pat)) {
+ str++;
+ continue;
+ } else
+ return false;
+ else if (*pat == '\\') /* Escaped char match as normal char */
+ pat++;
+ if (*str++ != *pat++)
+ return false;
+ }
+ /* Check wild card */
+ if (*pat == '*') {
+ while (*pat == '*')
+ pat++;
+ if (!*pat) /* Tail wild card matches all */
+ return true;
+ while (*str)
+ if (__match_glob(str++, pat, ignore_space))
+ return true;
+ }
+ return !*str && !*pat;
+}
+
+/**
+ * strglobmatch - glob expression pattern matching
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This returns true if the @str matches @pat. @pat can includes wildcards
+ * ('*','?') and character classes ([CHARS], complementation and ranges are
+ * also supported). Also, this supports escape character ('\') to use special
+ * characters as normal character.
+ *
+ * Note: if @pat syntax is broken, this always returns false.
+ */
+bool strglobmatch(const char *str, const char *pat)
+{
+ return __match_glob(str, pat, false);
+}
+
+/**
+ * strlazymatch - matching pattern strings lazily with glob pattern
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This is similar to strglobmatch, except this ignores spaces in
+ * the target string.
+ */
+bool strlazymatch(const char *str, const char *pat)
+{
+ return __match_glob(str, pat, true);
+}
diff --git a/smartt-perf/util/strlist.c b/smartt-perf/util/strlist.c
new file mode 100644
index 0000000..6783a20
--- /dev/null
+++ b/smartt-perf/util/strlist.c
@@ -0,0 +1,200 @@
+/*
+ * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include "strlist.h"
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct str_node *str_node__new(const char *s, bool dupstr)
+{
+ struct str_node *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ if (dupstr) {
+ s = strdup(s);
+ if (s == NULL)
+ goto out_delete;
+ }
+ self->s = s;
+ }
+
+ return self;
+
+out_delete:
+ free(self);
+ return NULL;
+}
+
+static void str_node__delete(struct str_node *self, bool dupstr)
+{
+ if (dupstr)
+ free((void *)self->s);
+ free(self);
+}
+
+int strlist__add(struct strlist *self, const char *new_entry)
+{
+ struct rb_node **p = &self->entries.rb_node;
+ struct rb_node *parent = NULL;
+ struct str_node *sn;
+
+ while (*p != NULL) {
+ int rc;
+
+ parent = *p;
+ sn = rb_entry(parent, struct str_node, rb_node);
+ rc = strcmp(sn->s, new_entry);
+
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return -EEXIST;
+ }
+
+ sn = str_node__new(new_entry, self->dupstr);
+ if (sn == NULL)
+ return -ENOMEM;
+
+ rb_link_node(&sn->rb_node, parent, p);
+ rb_insert_color(&sn->rb_node, &self->entries);
+ ++self->nr_entries;
+
+ return 0;
+}
+
+int strlist__load(struct strlist *self, const char *filename)
+{
+ char entry[1024];
+ int err;
+ FILE *fp = fopen(filename, "r");
+
+ if (fp == NULL)
+ return errno;
+
+ while (fgets(entry, sizeof(entry), fp) != NULL) {
+ const size_t len = strlen(entry);
+
+ if (len == 0)
+ continue;
+ entry[len - 1] = '\0';
+
+ err = strlist__add(self, entry);
+ if (err != 0)
+ goto out;
+ }
+
+ err = 0;
+out:
+ fclose(fp);
+ return err;
+}
+
+void strlist__remove(struct strlist *self, struct str_node *sn)
+{
+ rb_erase(&sn->rb_node, &self->entries);
+ str_node__delete(sn, self->dupstr);
+}
+
+struct str_node *strlist__find(struct strlist *self, const char *entry)
+{
+ struct rb_node **p = &self->entries.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p != NULL) {
+ struct str_node *sn;
+ int rc;
+
+ parent = *p;
+ sn = rb_entry(parent, struct str_node, rb_node);
+ rc = strcmp(sn->s, entry);
+
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return sn;
+ }
+
+ return NULL;
+}
+
+static int strlist__parse_list_entry(struct strlist *self, const char *s)
+{
+ if (strncmp(s, "file://", 7) == 0)
+ return strlist__load(self, s + 7);
+
+ return strlist__add(self, s);
+}
+
+int strlist__parse_list(struct strlist *self, const char *s)
+{
+ char *sep;
+ int err;
+
+ while ((sep = strchr(s, ',')) != NULL) {
+ *sep = '\0';
+ err = strlist__parse_list_entry(self, s);
+ *sep = ',';
+ if (err != 0)
+ return err;
+ s = sep + 1;
+ }
+
+ return *s ? strlist__parse_list_entry(self, s) : 0;
+}
+
+struct strlist *strlist__new(bool dupstr, const char *slist)
+{
+ struct strlist *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ self->entries = RB_ROOT;
+ self->dupstr = dupstr;
+ self->nr_entries = 0;
+ if (slist && strlist__parse_list(self, slist) != 0)
+ goto out_error;
+ }
+
+ return self;
+out_error:
+ free(self);
+ return NULL;
+}
+
+void strlist__delete(struct strlist *self)
+{
+ if (self != NULL) {
+ struct str_node *pos;
+ struct rb_node *next = rb_first(&self->entries);
+
+ while (next) {
+ pos = rb_entry(next, struct str_node, rb_node);
+ next = rb_next(&pos->rb_node);
+ strlist__remove(self, pos);
+ }
+ self->entries = RB_ROOT;
+ free(self);
+ }
+}
+
+struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
+
+ if (!idx--)
+ return pos;
+ }
+
+ return NULL;
+}
diff --git a/smartt-perf/util/strlist.h b/smartt-perf/util/strlist.h
new file mode 100644
index 0000000..3ba8390
--- /dev/null
+++ b/smartt-perf/util/strlist.h
@@ -0,0 +1,78 @@
+#ifndef __PERF_STRLIST_H
+#define __PERF_STRLIST_H
+
+#include <linux/rbtree.h>
+#include <stdbool.h>
+
+struct str_node {
+ struct rb_node rb_node;
+ const char *s;
+};
+
+struct strlist {
+ struct rb_root entries;
+ unsigned int nr_entries;
+ bool dupstr;
+};
+
+struct strlist *strlist__new(bool dupstr, const char *slist);
+void strlist__delete(struct strlist *self);
+
+void strlist__remove(struct strlist *self, struct str_node *sn);
+int strlist__load(struct strlist *self, const char *filename);
+int strlist__add(struct strlist *self, const char *str);
+
+struct str_node *strlist__entry(const struct strlist *self, unsigned int idx);
+struct str_node *strlist__find(struct strlist *self, const char *entry);
+
+static inline bool strlist__has_entry(struct strlist *self, const char *entry)
+{
+ return strlist__find(self, entry) != NULL;
+}
+
+static inline bool strlist__empty(const struct strlist *self)
+{
+ return self->nr_entries == 0;
+}
+
+static inline unsigned int strlist__nr_entries(const struct strlist *self)
+{
+ return self->nr_entries;
+}
+
+/* For strlist iteration */
+static inline struct str_node *strlist__first(struct strlist *self)
+{
+ struct rb_node *rn = rb_first(&self->entries);
+ return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
+}
+static inline struct str_node *strlist__next(struct str_node *sn)
+{
+ struct rb_node *rn;
+ if (!sn)
+ return NULL;
+ rn = rb_next(&sn->rb_node);
+ return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
+}
+
+/**
+ * strlist_for_each - iterate over a strlist
+ * @pos: the &struct str_node to use as a loop cursor.
+ * @self: the &struct strlist for loop.
+ */
+#define strlist__for_each(pos, self) \
+ for (pos = strlist__first(self); pos; pos = strlist__next(pos))
+
+/**
+ * strlist_for_each_safe - iterate over a strlist safe against removal of
+ * str_node
+ * @pos: the &struct str_node to use as a loop cursor.
+ * @n: another &struct str_node to use as temporary storage.
+ * @self: the &struct strlist for loop.
+ */
+#define strlist__for_each_safe(pos, n, self) \
+ for (pos = strlist__first(self), n = strlist__next(pos); pos;\
+ pos = n, n = strlist__next(n))
+
+int strlist__parse_list(struct strlist *self, const char *s);
+#endif /* __PERF_STRLIST_H */
diff --git a/smartt-perf/util/svghelper.c b/smartt-perf/util/svghelper.c
new file mode 100644
index 0000000..96c8660
--- /dev/null
+++ b/smartt-perf/util/svghelper.c
@@ -0,0 +1,501 @@
+/*
+ * svghelper.c - helper functions for outputting svg
+ *
+ * (C) Copyright 2009 Intel Corporation
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "svghelper.h"
+
+static u64 first_time, last_time;
+static u64 turbo_frequency, max_freq;
+
+
+#define SLOT_MULT 30.0
+#define SLOT_HEIGHT 25.0
+
+int svg_page_width = 1000;
+
+#define MIN_TEXT_SIZE 0.01
+
+static u64 total_height;
+static FILE *svgfile;
+
+static double cpu2slot(int cpu)
+{
+ return 2 * cpu + 1;
+}
+
+static double cpu2y(int cpu)
+{
+ return cpu2slot(cpu) * SLOT_MULT;
+}
+
+static double time2pixels(u64 __time)
+{
+ double X;
+
+ X = 1.0 * svg_page_width * (__time - first_time) / (last_time - first_time);
+ return X;
+}
+
+/*
+ * Round text sizes so that the svg viewer only needs a discrete
+ * number of renderings of the font
+ */
+static double round_text_size(double size)
+{
+ int loop = 100;
+ double target = 10.0;
+
+ if (size >= 10.0)
+ return size;
+ while (loop--) {
+ if (size >= target)
+ return target;
+ target = target / 2.0;
+ }
+ return size;
+}
+
+void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)
+{
+ int new_width;
+
+ svgfile = fopen(filename, "w");
+ if (!svgfile) {
+ fprintf(stderr, "Cannot open %s for output\n", filename);
+ return;
+ }
+ first_time = start;
+ first_time = first_time / 100000000 * 100000000;
+ last_time = end;
+
+ /*
+ * if the recording is short, we default to a width of 1000, but
+ * for longer recordings we want at least 200 units of width per second
+ */
+ new_width = (last_time - first_time) / 5000000;
+
+ if (new_width > svg_page_width)
+ svg_page_width = new_width;
+
+ total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;
+ fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n");
+ fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height);
+
+ fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
+
+ fprintf(svgfile, " rect { stroke-width: 1; }\n");
+ fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n");
+ fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
+ fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
+ fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
+ fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
+ fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
+ fprintf(svgfile, " rect.cpu { fill:rgb(192,192,192); fill-opacity:0.2; stroke-width:0.5; stroke:rgb(128,128,128); } \n");
+ fprintf(svgfile, " rect.pstate { fill:rgb(128,128,128); fill-opacity:0.8; stroke-width:0; } \n");
+ fprintf(svgfile, " rect.c1 { fill:rgb(255,214,214); fill-opacity:0.5; stroke-width:0; } \n");
+ fprintf(svgfile, " rect.c2 { fill:rgb(255,172,172); fill-opacity:0.5; stroke-width:0; } \n");
+ fprintf(svgfile, " rect.c3 { fill:rgb(255,130,130); fill-opacity:0.5; stroke-width:0; } \n");
+ fprintf(svgfile, " rect.c4 { fill:rgb(255, 88, 88); fill-opacity:0.5; stroke-width:0; } \n");
+ fprintf(svgfile, " rect.c5 { fill:rgb(255, 44, 44); fill-opacity:0.5; stroke-width:0; } \n");
+ fprintf(svgfile, " rect.c6 { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; } \n");
+ fprintf(svgfile, " line.pstate { stroke:rgb(255,255, 0); stroke-opacity:0.8; stroke-width:2; } \n");
+
+ fprintf(svgfile, " ]]>\n </style>\n</defs>\n");
+}
+
+void svg_box(int Yslot, u64 start, u64 end, const char *type)
+{
+ if (!svgfile)
+ return;
+
+ fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n",
+ time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);
+}
+
+void svg_sample(int Yslot, int cpu, u64 start, u64 end)
+{
+ double text_size;
+ if (!svgfile)
+ return;
+
+ fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n",
+ time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT);
+
+ text_size = (time2pixels(end)-time2pixels(start));
+ if (cpu > 9)
+ text_size = text_size/2;
+ if (text_size > 1.25)
+ text_size = 1.25;
+ text_size = round_text_size(text_size);
+
+ if (text_size > MIN_TEXT_SIZE)
+ fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n",
+ time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1);
+
+}
+
+static char *time_to_string(u64 duration)
+{
+ static char text[80];
+
+ text[0] = 0;
+
+ if (duration < 1000) /* less than 1 usec */
+ return text;
+
+ if (duration < 1000 * 1000) { /* less than 1 msec */
+ sprintf(text, "%4.1f us", duration / 1000.0);
+ return text;
+ }
+ sprintf(text, "%4.1f ms", duration / 1000.0 / 1000);
+
+ return text;
+}
+
+void svg_waiting(int Yslot, u64 start, u64 end)
+{
+ char *text;
+ const char *style;
+ double font_size;
+
+ if (!svgfile)
+ return;
+
+ style = "waiting";
+
+ if (end-start > 10 * 1000000) /* 10 msec */
+ style = "WAITING";
+
+ text = time_to_string(end-start);
+
+ font_size = 1.0 * (time2pixels(end)-time2pixels(start));
+
+ if (font_size > 3)
+ font_size = 3;
+
+ font_size = round_text_size(font_size);
+
+ fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT);
+ fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
+ time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);
+ if (font_size > MIN_TEXT_SIZE)
+ fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%1.8fpt\"> %s</text>\n",
+ font_size, text);
+ fprintf(svgfile, "</g>\n");
+}
+
+static char *cpu_model(void)
+{
+ static char cpu_m[255];
+ char buf[256];
+ FILE *file;
+
+ cpu_m[0] = 0;
+ /* CPU type */
+ file = fopen("/proc/cpuinfo", "r");
+ if (file) {
+ while (fgets(buf, 255, file)) {
+ if (strstr(buf, "model name")) {
+ strncpy(cpu_m, &buf[13], 255);
+ break;
+ }
+ }
+ fclose(file);
+ }
+
+ /* CPU type */
+ file = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies", "r");
+ if (file) {
+ while (fgets(buf, 255, file)) {
+ unsigned int freq;
+ freq = strtoull(buf, NULL, 10);
+ if (freq > max_freq)
+ max_freq = freq;
+ }
+ fclose(file);
+ }
+ return cpu_m;
+}
+
+void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
+{
+ char cpu_string[80];
+ if (!svgfile)
+ return;
+
+ max_freq = __max_freq;
+ turbo_frequency = __turbo_freq;
+
+ fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",
+ time2pixels(first_time),
+ time2pixels(last_time)-time2pixels(first_time),
+ cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
+
+ sprintf(cpu_string, "CPU %i", (int)cpu+1);
+ fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",
+ 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string);
+
+ fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",
+ 10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model());
+}
+
+void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name)
+{
+ double width;
+
+ if (!svgfile)
+ return;
+
+
+ fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu));
+ fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
+ time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type);
+ width = time2pixels(end)-time2pixels(start);
+ if (width > 6)
+ width = 6;
+
+ width = round_text_size(width);
+
+ if (width > MIN_TEXT_SIZE)
+ fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%3.8fpt\">%s</text>\n",
+ width, name);
+
+ fprintf(svgfile, "</g>\n");
+}
+
+void svg_cstate(int cpu, u64 start, u64 end, int type)
+{
+ double width;
+ char style[128];
+
+ if (!svgfile)
+ return;
+
+
+ if (type > 6)
+ type = 6;
+ sprintf(style, "c%i", type);
+
+ fprintf(svgfile, "<rect class=\"%s\" x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\"/>\n",
+ style,
+ time2pixels(start), time2pixels(end)-time2pixels(start),
+ cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
+
+ width = (time2pixels(end)-time2pixels(start))/2.0;
+ if (width > 6)
+ width = 6;
+
+ width = round_text_size(width);
+
+ if (width > MIN_TEXT_SIZE)
+ fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n",
+ time2pixels(start), cpu2y(cpu)+width, width, type);
+}
+
+static char *HzToHuman(unsigned long hz)
+{
+ static char buffer[1024];
+ unsigned long long Hz;
+
+ memset(buffer, 0, 1024);
+
+ Hz = hz;
+
+ /* default: just put the Number in */
+ sprintf(buffer, "%9lli", Hz);
+
+ if (Hz > 1000)
+ sprintf(buffer, " %6lli Mhz", (Hz+500)/1000);
+
+ if (Hz > 1500000)
+ sprintf(buffer, " %6.2f Ghz", (Hz+5000.0)/1000000);
+
+ if (Hz == turbo_frequency)
+ sprintf(buffer, "Turbo");
+
+ return buffer;
+}
+
+void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
+{
+ double height = 0;
+
+ if (!svgfile)
+ return;
+
+ if (max_freq)
+ height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);
+ height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height;
+ fprintf(svgfile, "<line x1=\"%4.8f\" x2=\"%4.8f\" y1=\"%4.1f\" y2=\"%4.1f\" class=\"pstate\"/>\n",
+ time2pixels(start), time2pixels(end), height, height);
+ fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n",
+ time2pixels(start), height+0.9, HzToHuman(freq));
+
+}
+
+
+void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2)
+{
+ double height;
+
+ if (!svgfile)
+ return;
+
+
+ if (row1 < row2) {
+ if (row1) {
+ fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
+ time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32);
+ if (desc2)
+ fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &gt;</text></g>\n",
+ time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_HEIGHT/48, desc2);
+ }
+ if (row2) {
+ fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
+ time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row2 * SLOT_MULT);
+ if (desc1)
+ fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &gt;</text></g>\n",
+ time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, desc1);
+ }
+ } else {
+ if (row2) {
+ fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
+ time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32);
+ if (desc1)
+ fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &lt;</text></g>\n",
+ time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/48, desc1);
+ }
+ if (row1) {
+ fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
+ time2pixels(start), row1 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row1 * SLOT_MULT);
+ if (desc2)
+ fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &lt;</text></g>\n",
+ time2pixels(start), row1 * SLOT_MULT - SLOT_HEIGHT/32, desc2);
+ }
+ }
+ height = row1 * SLOT_MULT;
+ if (row2 > row1)
+ height += SLOT_HEIGHT;
+ if (row1)
+ fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
+ time2pixels(start), height);
+}
+
+void svg_wakeline(u64 start, int row1, int row2)
+{
+ double height;
+
+ if (!svgfile)
+ return;
+
+
+ if (row1 < row2)
+ fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
+ time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT);
+ else
+ fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
+ time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT);
+
+ height = row1 * SLOT_MULT;
+ if (row2 > row1)
+ height += SLOT_HEIGHT;
+ fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
+ time2pixels(start), height);
+}
+
+void svg_interrupt(u64 start, int row)
+{
+ if (!svgfile)
+ return;
+
+ fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
+ time2pixels(start), row * SLOT_MULT);
+ fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
+ time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT);
+}
+
+void svg_text(int Yslot, u64 start, const char *text)
+{
+ if (!svgfile)
+ return;
+
+ fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",
+ time2pixels(start), Yslot * SLOT_MULT+SLOT_HEIGHT/2, text);
+}
+
+static void svg_legenda_box(int X, const char *text, const char *style)
+{
+ double boxsize;
+ boxsize = SLOT_HEIGHT / 2;
+
+ fprintf(svgfile, "<rect x=\"%i\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
+ X, boxsize, boxsize, style);
+ fprintf(svgfile, "<text transform=\"translate(%4.8f, %4.8f)\" font-size=\"%4.8fpt\">%s</text>\n",
+ X + boxsize + 5, boxsize, 0.8 * boxsize, text);
+}
+
+void svg_legenda(void)
+{
+ if (!svgfile)
+ return;
+
+ svg_legenda_box(0, "Running", "sample");
+ svg_legenda_box(100, "Idle","c1");
+ svg_legenda_box(200, "Deeper Idle", "c3");
+ svg_legenda_box(350, "Deepest Idle", "c6");
+ svg_legenda_box(550, "Sleeping", "process2");
+ svg_legenda_box(650, "Waiting for cpu", "waiting");
+ svg_legenda_box(800, "Blocked on IO", "blocked");
+}
+
+void svg_time_grid(void)
+{
+ u64 i;
+
+ if (!svgfile)
+ return;
+
+ i = first_time;
+ while (i < last_time) {
+ int color = 220;
+ double thickness = 0.075;
+ if ((i % 100000000) == 0) {
+ thickness = 0.5;
+ color = 192;
+ }
+ if ((i % 1000000000) == 0) {
+ thickness = 2.0;
+ color = 128;
+ }
+
+ fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%" PRIu64 "\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%1.3f\"/>\n",
+ time2pixels(i), SLOT_MULT/2, time2pixels(i), total_height, color, color, color, thickness);
+
+ i += 10000000;
+ }
+}
+
+void svg_close(void)
+{
+ if (svgfile) {
+ fprintf(svgfile, "</svg>\n");
+ fclose(svgfile);
+ svgfile = NULL;
+ }
+}
diff --git a/smartt-perf/util/svghelper.h b/smartt-perf/util/svghelper.h
new file mode 100644
index 0000000..e078198
--- /dev/null
+++ b/smartt-perf/util/svghelper.h
@@ -0,0 +1,28 @@
+#ifndef __PERF_SVGHELPER_H
+#define __PERF_SVGHELPER_H
+
+#include "types.h"
+
+extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
+extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
+extern void svg_sample(int Yslot, int cpu, u64 start, u64 end);
+extern void svg_waiting(int Yslot, u64 start, u64 end);
+extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency);
+
+
+extern void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name);
+extern void svg_cstate(int cpu, u64 start, u64 end, int type);
+extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);
+
+
+extern void svg_time_grid(void);
+extern void svg_legenda(void);
+extern void svg_wakeline(u64 start, int row1, int row2);
+extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2);
+extern void svg_interrupt(u64 start, int row);
+extern void svg_text(int Yslot, u64 start, const char *text);
+extern void svg_close(void);
+
+extern int svg_page_width;
+
+#endif /* __PERF_SVGHELPER_H */
diff --git a/smartt-perf/util/symbol.c b/smartt-perf/util/symbol.c
new file mode 100644
index 0000000..b1bf490
--- /dev/null
+++ b/smartt-perf/util/symbol.c
@@ -0,0 +1,2603 @@
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include "build-id.h"
+#include "debug.h"
+#include "symbol.h"
+#include "strlist.h"
+
+#include <libelf.h>
+#include <gelf.h>
+#include <elf.h>
+#include <limits.h>
+#include <sys/utsname.h>
+
+#ifndef KSYM_NAME_LEN
+#define KSYM_NAME_LEN 128
+#endif
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+static bool dso__build_id_equal(const struct dso *self, u8 *build_id);
+static int elf_read_build_id(Elf *elf, void *bf, size_t size);
+static void dsos__add(struct list_head *head, struct dso *dso);
+static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
+static int dso__load_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter);
+static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter);
+static int vmlinux_path__nr_entries;
+static char **vmlinux_path;
+
+struct symbol_conf symbol_conf = {
+ .exclude_other = true,
+ .use_modules = true,
+ .try_vmlinux_path = true,
+ .symfs = "",
+};
+
+int dso__name_len(const struct dso *self)
+{
+ if (verbose)
+ return self->long_name_len;
+
+ return self->short_name_len;
+}
+
+bool dso__loaded(const struct dso *self, enum map_type type)
+{
+ return self->loaded & (1 << type);
+}
+
+bool dso__sorted_by_name(const struct dso *self, enum map_type type)
+{
+ return self->sorted_by_name & (1 << type);
+}
+
+static void dso__set_sorted_by_name(struct dso *self, enum map_type type)
+{
+ self->sorted_by_name |= (1 << type);
+}
+
+bool symbol_type__is_a(char symbol_type, enum map_type map_type)
+{
+ switch (map_type) {
+ case MAP__FUNCTION:
+ return symbol_type == 'T' || symbol_type == 'W';
+ case MAP__VARIABLE:
+ return symbol_type == 'D' || symbol_type == 'd';
+ default:
+ return false;
+ }
+}
+
+static void symbols__fixup_end(struct rb_root *self)
+{
+ struct rb_node *nd, *prevnd = rb_first(self);
+ struct symbol *curr, *prev;
+
+ if (prevnd == NULL)
+ return;
+
+ curr = rb_entry(prevnd, struct symbol, rb_node);
+
+ for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ prev = curr;
+ curr = rb_entry(nd, struct symbol, rb_node);
+
+ if (prev->end == prev->start && prev->end != curr->start)
+ prev->end = curr->start - 1;
+ }
+
+ /* Last entry */
+ if (curr->end == curr->start)
+ curr->end = roundup(curr->start, 4096);
+}
+
+static void __map_groups__fixup_end(struct map_groups *self, enum map_type type)
+{
+ struct map *prev, *curr;
+ struct rb_node *nd, *prevnd = rb_first(&self->maps[type]);
+
+ if (prevnd == NULL)
+ return;
+
+ curr = rb_entry(prevnd, struct map, rb_node);
+
+ for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ prev = curr;
+ curr = rb_entry(nd, struct map, rb_node);
+ prev->end = curr->start - 1;
+ }
+
+ /*
+ * We still haven't the actual symbols, so guess the
+ * last map final address.
+ */
+ curr->end = ~0ULL;
+}
+
+static void map_groups__fixup_end(struct map_groups *self)
+{
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ __map_groups__fixup_end(self, i);
+}
+
+static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
+ const char *name)
+{
+ size_t namelen = strlen(name) + 1;
+ struct symbol *self = calloc(1, (symbol_conf.priv_size +
+ sizeof(*self) + namelen));
+ if (self == NULL)
+ return NULL;
+
+ if (symbol_conf.priv_size)
+ self = ((void *)self) + symbol_conf.priv_size;
+
+ self->start = start;
+ self->end = len ? start + len - 1 : start;
+ self->binding = binding;
+ self->namelen = namelen - 1;
+
+ pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", __func__, name, start, self->end);
+
+ memcpy(self->name, name, namelen);
+
+ return self;
+}
+
+void symbol__delete(struct symbol *self)
+{
+ free(((void *)self) - symbol_conf.priv_size);
+}
+
+static size_t symbol__fprintf(struct symbol *self, FILE *fp)
+{
+ return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
+ self->start, self->end,
+ self->binding == STB_GLOBAL ? 'g' :
+ self->binding == STB_LOCAL ? 'l' : 'w',
+ self->name);
+}
+
+void dso__set_long_name(struct dso *self, char *name)
+{
+ if (name == NULL)
+ return;
+ self->long_name = name;
+ self->long_name_len = strlen(name);
+}
+
+static void dso__set_short_name(struct dso *self, const char *name)
+{
+ if (name == NULL)
+ return;
+ self->short_name = name;
+ self->short_name_len = strlen(name);
+}
+
+static void dso__set_basename(struct dso *self)
+{
+ dso__set_short_name(self, basename(self->long_name));
+}
+
+struct dso *dso__new(const char *name)
+{
+ struct dso *self = calloc(1, sizeof(*self) + strlen(name) + 1);
+
+ if (self != NULL) {
+ int i;
+ strcpy(self->name, name);
+ dso__set_long_name(self, self->name);
+ dso__set_short_name(self, self->name);
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ self->symbols[i] = self->symbol_names[i] = RB_ROOT;
+ self->slen_calculated = 0;
+ self->origin = DSO__ORIG_NOT_FOUND;
+ self->loaded = 0;
+ self->sorted_by_name = 0;
+ self->has_build_id = 0;
+ self->kernel = DSO_TYPE_USER;
+ INIT_LIST_HEAD(&self->node);
+ }
+
+ return self;
+}
+
+static void symbols__delete(struct rb_root *self)
+{
+ struct symbol *pos;
+ struct rb_node *next = rb_first(self);
+
+ while (next) {
+ pos = rb_entry(next, struct symbol, rb_node);
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, self);
+ symbol__delete(pos);
+ }
+}
+
+void dso__delete(struct dso *self)
+{
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ symbols__delete(&self->symbols[i]);
+ if (self->sname_alloc)
+ free((char *)self->short_name);
+ if (self->lname_alloc)
+ free(self->long_name);
+ free(self);
+}
+
+void dso__set_build_id(struct dso *self, void *build_id)
+{
+ memcpy(self->build_id, build_id, sizeof(self->build_id));
+ self->has_build_id = 1;
+}
+
+static void symbols__insert(struct rb_root *self, struct symbol *sym)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ const u64 ip = sym->start;
+ struct symbol *s;
+
+ while (*p != NULL) {
+ parent = *p;
+ s = rb_entry(parent, struct symbol, rb_node);
+ if (ip < s->start)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&sym->rb_node, parent, p);
+ rb_insert_color(&sym->rb_node, self);
+}
+
+static struct symbol *symbols__find(struct rb_root *self, u64 ip)
+{
+ struct rb_node *n;
+
+ if (self == NULL)
+ return NULL;
+
+ n = self->rb_node;
+
+ while (n) {
+ struct symbol *s = rb_entry(n, struct symbol, rb_node);
+
+ if (ip < s->start)
+ n = n->rb_left;
+ else if (ip > s->end)
+ n = n->rb_right;
+ else
+ return s;
+ }
+
+ return NULL;
+}
+
+struct symbol_name_rb_node {
+ struct rb_node rb_node;
+ struct symbol sym;
+};
+
+static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct symbol_name_rb_node *symn, *s;
+
+ symn = container_of(sym, struct symbol_name_rb_node, sym);
+
+ while (*p != NULL) {
+ parent = *p;
+ s = rb_entry(parent, struct symbol_name_rb_node, rb_node);
+ if (strcmp(sym->name, s->sym.name) < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&symn->rb_node, parent, p);
+ rb_insert_color(&symn->rb_node, self);
+}
+
+static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(source); nd; nd = rb_next(nd)) {
+ struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+ symbols__insert_by_name(self, pos);
+ }
+}
+
+static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name)
+{
+ struct rb_node *n;
+
+ if (self == NULL)
+ return NULL;
+
+ n = self->rb_node;
+
+ while (n) {
+ struct symbol_name_rb_node *s;
+ int cmp;
+
+ s = rb_entry(n, struct symbol_name_rb_node, rb_node);
+ cmp = strcmp(name, s->sym.name);
+
+ if (cmp < 0)
+ n = n->rb_left;
+ else if (cmp > 0)
+ n = n->rb_right;
+ else
+ return &s->sym;
+ }
+
+ return NULL;
+}
+
+struct symbol *dso__find_symbol(struct dso *self,
+ enum map_type type, u64 addr)
+{
+ return symbols__find(&self->symbols[type], addr);
+}
+
+struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
+ const char *name)
+{
+ return symbols__find_by_name(&self->symbol_names[type], name);
+}
+
+void dso__sort_by_name(struct dso *self, enum map_type type)
+{
+ dso__set_sorted_by_name(self, type);
+ return symbols__sort_by_name(&self->symbol_names[type],
+ &self->symbols[type]);
+}
+
+int build_id__sprintf(const u8 *self, int len, char *bf)
+{
+ char *bid = bf;
+ const u8 *raw = self;
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ sprintf(bid, "%02x", *raw);
+ ++raw;
+ bid += 2;
+ }
+
+ return raw - self;
+}
+
+size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
+{
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
+ return fprintf(fp, "%s", sbuild_id);
+}
+
+size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp)
+{
+ size_t ret = 0;
+ struct rb_node *nd;
+ struct symbol_name_rb_node *pos;
+
+ for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
+ pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
+ fprintf(fp, "%s\n", pos->sym.name);
+ }
+
+ return ret;
+}
+
+size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
+{
+ struct rb_node *nd;
+ size_t ret = fprintf(fp, "dso: %s (", self->short_name);
+
+ if (self->short_name != self->long_name)
+ ret += fprintf(fp, "%s, ", self->long_name);
+ ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type],
+ self->loaded ? "" : "NOT ");
+ ret += dso__fprintf_buildid(self, fp);
+ ret += fprintf(fp, ")\n");
+ for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) {
+ struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+ ret += symbol__fprintf(pos, fp);
+ }
+
+ return ret;
+}
+
+int kallsyms__parse(const char *filename, void *arg,
+ int (*process_symbol)(void *arg, const char *name,
+ char type, u64 start, u64 end))
+{
+ char *line = NULL;
+ size_t n;
+ int err = -1;
+ u64 prev_start = 0;
+ char prev_symbol_type = 0;
+ char *prev_symbol_name;
+ FILE *file = fopen(filename, "r");
+
+ if (file == NULL)
+ goto out_failure;
+
+ prev_symbol_name = malloc(KSYM_NAME_LEN);
+ if (prev_symbol_name == NULL)
+ goto out_close;
+
+ err = 0;
+
+ while (!feof(file)) {
+ u64 start;
+ int line_len, len;
+ char symbol_type;
+ char *symbol_name;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0 || !line)
+ break;
+
+ line[--line_len] = '\0'; /* \n */
+
+ len = hex2u64(line, &start);
+
+ len++;
+ if (len + 2 >= line_len)
+ continue;
+
+ symbol_type = toupper(line[len]);
+ len += 2;
+ symbol_name = line + len;
+ len = line_len - len;
+
+ if (len >= KSYM_NAME_LEN) {
+ err = -1;
+ break;
+ }
+
+ if (prev_symbol_type) {
+ u64 end = start;
+ if (end != prev_start)
+ --end;
+ err = process_symbol(arg, prev_symbol_name,
+ prev_symbol_type, prev_start, end);
+ if (err)
+ break;
+ }
+
+ memcpy(prev_symbol_name, symbol_name, len + 1);
+ prev_symbol_type = symbol_type;
+ prev_start = start;
+ }
+
+ free(prev_symbol_name);
+ free(line);
+out_close:
+ fclose(file);
+ return err;
+
+out_failure:
+ return -1;
+}
+
+struct process_kallsyms_args {
+ struct map *map;
+ struct dso *dso;
+};
+
+static u8 kallsyms2elf_type(char type)
+{
+ if (type == 'W')
+ return STB_WEAK;
+
+ return isupper(type) ? STB_GLOBAL : STB_LOCAL;
+}
+
+static int map__process_kallsym_symbol(void *arg, const char *name,
+ char type, u64 start, u64 end)
+{
+ struct symbol *sym;
+ struct process_kallsyms_args *a = arg;
+ struct rb_root *root = &a->dso->symbols[a->map->type];
+
+ if (!symbol_type__is_a(type, a->map->type))
+ return 0;
+
+ sym = symbol__new(start, end - start + 1,
+ kallsyms2elf_type(type), name);
+ if (sym == NULL)
+ return -ENOMEM;
+ /*
+ * We will pass the symbols to the filter later, in
+ * map__split_kallsyms, when we have split the maps per module
+ */
+ symbols__insert(root, sym);
+
+ return 0;
+}
+
+/*
+ * Loads the function entries in /proc/kallsyms into kernel_map->dso,
+ * so that we can in the next step set the symbol ->end address and then
+ * call kernel_maps__split_kallsyms.
+ */
+static int dso__load_all_kallsyms(struct dso *self, const char *filename,
+ struct map *map)
+{
+ struct process_kallsyms_args args = { .map = map, .dso = self, };
+ return kallsyms__parse(filename, &args, map__process_kallsym_symbol);
+}
+
+/*
+ * Split the symbols into maps, making sure there are no overlaps, i.e. the
+ * kernel range is broken in several maps, named [kernel].N, as we don't have
+ * the original ELF section names vmlinux have.
+ */
+static int dso__split_kallsyms(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ struct map_groups *kmaps = map__kmap(map)->kmaps;
+ struct machine *machine = kmaps->machine;
+ struct map *curr_map = map;
+ struct symbol *pos;
+ int count = 0, moved = 0;
+ struct rb_root *root = &self->symbols[map->type];
+ struct rb_node *next = rb_first(root);
+ int kernel_range = 0;
+
+ while (next) {
+ char *module;
+
+ pos = rb_entry(next, struct symbol, rb_node);
+ next = rb_next(&pos->rb_node);
+
+ module = strchr(pos->name, '\t');
+ if (module) {
+ if (!symbol_conf.use_modules)
+ goto discard_symbol;
+
+ *module++ = '\0';
+
+ if (strcmp(curr_map->dso->short_name, module)) {
+ if (curr_map != map &&
+ self->kernel == DSO_TYPE_GUEST_KERNEL &&
+ machine__is_default_guest(machine)) {
+ /*
+ * We assume all symbols of a module are
+ * continuous in * kallsyms, so curr_map
+ * points to a module and all its
+ * symbols are in its kmap. Mark it as
+ * loaded.
+ */
+ dso__set_loaded(curr_map->dso,
+ curr_map->type);
+ }
+
+ curr_map = map_groups__find_by_name(kmaps,
+ map->type, module);
+ if (curr_map == NULL) {
+ pr_debug("%s/proc/{kallsyms,modules} "
+ "inconsistency while looking "
+ "for \"%s\" module!\n",
+ machine->root_dir, module);
+ curr_map = map;
+ goto discard_symbol;
+ }
+
+ if (curr_map->dso->loaded &&
+ !machine__is_default_guest(machine))
+ goto discard_symbol;
+ }
+ /*
+ * So that we look just like we get from .ko files,
+ * i.e. not prelinked, relative to map->start.
+ */
+ pos->start = curr_map->map_ip(curr_map, pos->start);
+ pos->end = curr_map->map_ip(curr_map, pos->end);
+ } else if (curr_map != map) {
+ char dso_name[PATH_MAX];
+ struct dso *dso;
+
+ if (count == 0) {
+ curr_map = map;
+ goto filter_symbol;
+ }
+
+ if (self->kernel == DSO_TYPE_GUEST_KERNEL)
+ snprintf(dso_name, sizeof(dso_name),
+ "[guest.kernel].%d",
+ kernel_range++);
+ else
+ snprintf(dso_name, sizeof(dso_name),
+ "[kernel].%d",
+ kernel_range++);
+
+ dso = dso__new(dso_name);
+ if (dso == NULL)
+ return -1;
+
+ dso->kernel = self->kernel;
+
+ curr_map = map__new2(pos->start, dso, map->type);
+ if (curr_map == NULL) {
+ dso__delete(dso);
+ return -1;
+ }
+
+ curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
+ map_groups__insert(kmaps, curr_map);
+ ++kernel_range;
+ }
+filter_symbol:
+ if (filter && filter(curr_map, pos)) {
+discard_symbol: rb_erase(&pos->rb_node, root);
+ symbol__delete(pos);
+ } else {
+ if (curr_map != map) {
+ rb_erase(&pos->rb_node, root);
+ symbols__insert(&curr_map->dso->symbols[curr_map->type], pos);
+ ++moved;
+ } else
+ ++count;
+ }
+ }
+
+ if (curr_map != map &&
+ self->kernel == DSO_TYPE_GUEST_KERNEL &&
+ machine__is_default_guest(kmaps->machine)) {
+ dso__set_loaded(curr_map->dso, curr_map->type);
+ }
+
+ return count + moved;
+}
+
+int dso__load_kallsyms(struct dso *self, const char *filename,
+ struct map *map, symbol_filter_t filter)
+{
+ if (dso__load_all_kallsyms(self, filename, map) < 0)
+ return -1;
+
+ if (self->kernel == DSO_TYPE_GUEST_KERNEL)
+ self->origin = DSO__ORIG_GUEST_KERNEL;
+ else
+ self->origin = DSO__ORIG_KERNEL;
+
+ return dso__split_kallsyms(self, map, filter);
+}
+
+static int dso__load_perf_map(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ char *line = NULL;
+ size_t n;
+ FILE *file;
+ int nr_syms = 0;
+
+ file = fopen(self->long_name, "r");
+ if (file == NULL)
+ goto out_failure;
+
+ while (!feof(file)) {
+ u64 start, size;
+ struct symbol *sym;
+ int line_len, len;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0)
+ break;
+
+ if (!line)
+ goto out_failure;
+
+ line[--line_len] = '\0'; /* \n */
+
+ len = hex2u64(line, &start);
+
+ len++;
+ if (len + 2 >= line_len)
+ continue;
+
+ len += hex2u64(line + len, &size);
+
+ len++;
+ if (len + 2 >= line_len)
+ continue;
+
+ sym = symbol__new(start, size, STB_GLOBAL, line + len);
+
+ if (sym == NULL)
+ goto out_delete_line;
+
+ if (filter && filter(map, sym))
+ symbol__delete(sym);
+ else {
+ symbols__insert(&self->symbols[map->type], sym);
+ nr_syms++;
+ }
+ }
+
+ free(line);
+ fclose(file);
+
+ return nr_syms;
+
+out_delete_line:
+ free(line);
+out_failure:
+ return -1;
+}
+
+/**
+ * elf_symtab__for_each_symbol - iterate thru all the symbols
+ *
+ * @self: struct elf_symtab instance to iterate
+ * @idx: uint32_t idx
+ * @sym: GElf_Sym iterator
+ */
+#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
+ for (idx = 0, gelf_getsym(syms, idx, &sym);\
+ idx < nr_syms; \
+ idx++, gelf_getsym(syms, idx, &sym))
+
+static inline uint8_t elf_sym__type(const GElf_Sym *sym)
+{
+ return GELF_ST_TYPE(sym->st_info);
+}
+
+static inline int elf_sym__is_function(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_FUNC &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline bool elf_sym__is_object(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_OBJECT &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline int elf_sym__is_label(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_NOTYPE &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx != SHN_ABS;
+}
+
+static inline const char *elf_sec__name(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return secstrs->d_buf + shdr->sh_name;
+}
+
+static inline int elf_sec__is_text(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
+}
+
+static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
+}
+
+static inline const char *elf_sym__name(const GElf_Sym *sym,
+ const Elf_Data *symstrs)
+{
+ return symstrs->d_buf + sym->st_name;
+}
+
+static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name,
+ size_t *idx)
+{
+ Elf_Scn *sec = NULL;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ char *str;
+
+ gelf_getshdr(sec, shp);
+ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+ if (!strcmp(name, str)) {
+ if (idx)
+ *idx = cnt;
+ break;
+ }
+ ++cnt;
+ }
+
+ return sec;
+}
+
+#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
+
+#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
+
+/*
+ * We need to check if we have a .dynsym, so that we can handle the
+ * .plt, synthesizing its symbols, that aren't on the symtabs (be it
+ * .dynsym or .symtab).
+ * And always look at the original dso, not at debuginfo packages, that
+ * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
+ */
+static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ uint32_t nr_rel_entries, idx;
+ GElf_Sym sym;
+ u64 plt_offset;
+ GElf_Shdr shdr_plt;
+ struct symbol *f;
+ GElf_Shdr shdr_rel_plt, shdr_dynsym;
+ Elf_Data *reldata, *syms, *symstrs;
+ Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
+ size_t dynsym_idx;
+ GElf_Ehdr ehdr;
+ char sympltname[1024];
+ Elf *elf;
+ int nr = 0, symidx, fd, err = 0;
+ char name[PATH_MAX];
+
+ snprintf(name, sizeof(name), "%s%s",
+ symbol_conf.symfs, self->long_name);
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ goto out_close;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out_elf_end;
+
+ scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
+ ".dynsym", &dynsym_idx);
+ if (scn_dynsym == NULL)
+ goto out_elf_end;
+
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rela.plt", NULL);
+ if (scn_plt_rel == NULL) {
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rel.plt", NULL);
+ if (scn_plt_rel == NULL)
+ goto out_elf_end;
+ }
+
+ err = -1;
+
+ if (shdr_rel_plt.sh_link != dynsym_idx)
+ goto out_elf_end;
+
+ if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
+ goto out_elf_end;
+
+ /*
+ * Fetch the relocation section to find the idxes to the GOT
+ * and the symbols in the .dynsym they refer to.
+ */
+ reldata = elf_getdata(scn_plt_rel, NULL);
+ if (reldata == NULL)
+ goto out_elf_end;
+
+ syms = elf_getdata(scn_dynsym, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
+ if (scn_symstrs == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(scn_symstrs, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
+ plt_offset = shdr_plt.sh_offset;
+
+ if (shdr_rel_plt.sh_type == SHT_RELA) {
+ GElf_Rela pos_mem, *pos;
+
+ elf_section__for_each_rela(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ STB_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&self->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ } else if (shdr_rel_plt.sh_type == SHT_REL) {
+ GElf_Rel pos_mem, *pos;
+ elf_section__for_each_rel(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ STB_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&self->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ }
+
+ err = 0;
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+
+ if (err == 0)
+ return nr;
+out:
+ pr_debug("%s: problems reading %s PLT info.\n",
+ __func__, self->long_name);
+ return 0;
+}
+
+static bool elf_sym__is_a(GElf_Sym *self, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sym__is_function(self);
+ case MAP__VARIABLE:
+ return elf_sym__is_object(self);
+ default:
+ return false;
+ }
+}
+
+static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sec__is_text(self, secstrs);
+ case MAP__VARIABLE:
+ return elf_sec__is_data(self, secstrs);
+ default:
+ return false;
+ }
+}
+
+static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
+{
+ Elf_Scn *sec = NULL;
+ GElf_Shdr shdr;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ gelf_getshdr(sec, &shdr);
+
+ if ((addr >= shdr.sh_addr) &&
+ (addr < (shdr.sh_addr + shdr.sh_size)))
+ return cnt;
+
+ ++cnt;
+ }
+
+ return -1;
+}
+
+static int dso__load_sym(struct dso *self, struct map *map, const char *name,
+ int fd, symbol_filter_t filter, int kmodule,
+ int want_symtab)
+{
+ struct kmap *kmap = self->kernel ? map__kmap(map) : NULL;
+ struct map *curr_map = map;
+ struct dso *curr_dso = self;
+ Elf_Data *symstrs, *secstrs;
+ uint32_t nr_syms;
+ int err = -1;
+ uint32_t idx;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr, opdshdr;
+ Elf_Data *syms, *opddata = NULL;
+ GElf_Sym sym;
+ Elf_Scn *sec, *sec_strndx, *opdsec;
+ Elf *elf;
+ int nr = 0;
+ size_t opdidx = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
+ goto out_close;
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_debug("%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ /* Always reject images with a mismatched build-id: */
+ if (self->has_build_id) {
+ u8 build_id[BUILD_ID_SIZE];
+
+ if (elf_read_build_id(elf, build_id,
+ BUILD_ID_SIZE) != BUILD_ID_SIZE)
+ goto out_elf_end;
+
+ if (!dso__build_id_equal(self, build_id))
+ goto out_elf_end;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
+ if (sec == NULL) {
+ if (want_symtab)
+ goto out_elf_end;
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
+ if (sec == NULL)
+ goto out_elf_end;
+ }
+
+ opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
+ if (opdsec)
+ opddata = elf_rawdata(opdsec, NULL);
+
+ syms = elf_getdata(sec, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ sec = elf_getscn(elf, shdr.sh_link);
+ if (sec == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(sec, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
+ if (sec_strndx == NULL)
+ goto out_elf_end;
+
+ secstrs = elf_getdata(sec_strndx, NULL);
+ if (secstrs == NULL)
+ goto out_elf_end;
+
+ nr_syms = shdr.sh_size / shdr.sh_entsize;
+
+ memset(&sym, 0, sizeof(sym));
+ if (self->kernel == DSO_TYPE_USER) {
+ self->adjust_symbols = (ehdr.e_type == ET_EXEC ||
+ elf_section_by_name(elf, &ehdr, &shdr,
+ ".gnu.prelink_undo",
+ NULL) != NULL);
+ } else self->adjust_symbols = 0;
+
+ elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
+ struct symbol *f;
+ const char *elf_name = elf_sym__name(&sym, symstrs);
+ char *demangled = NULL;
+ int is_label = elf_sym__is_label(&sym);
+ const char *section_name;
+
+ if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
+ strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
+ kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
+
+ if (!is_label && !elf_sym__is_a(&sym, map->type))
+ continue;
+
+ /* Reject ARM ELF "mapping symbols": these aren't unique and
+ * don't identify functions, so will confuse the profile
+ * output: */
+ if (ehdr.e_machine == EM_ARM) {
+ if (!strcmp(elf_name, "$a") ||
+ !strcmp(elf_name, "$d") ||
+ !strcmp(elf_name, "$t"))
+ continue;
+ }
+
+ if (opdsec && sym.st_shndx == opdidx) {
+ u32 offset = sym.st_value - opdshdr.sh_addr;
+ u64 *opd = opddata->d_buf + offset;
+ sym.st_value = *opd;
+ sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
+ }
+
+ sec = elf_getscn(elf, sym.st_shndx);
+ if (!sec)
+ goto out_elf_end;
+
+ gelf_getshdr(sec, &shdr);
+
+ if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
+ continue;
+
+ section_name = elf_sec__name(&shdr, secstrs);
+
+ /* On ARM, symbols for thumb functions have 1 added to
+ * the symbol address as a flag - remove it */
+ if ((ehdr.e_machine == EM_ARM) &&
+ (map->type == MAP__FUNCTION) &&
+ (sym.st_value & 1))
+ --sym.st_value;
+
+ if (self->kernel != DSO_TYPE_USER || kmodule) {
+ char dso_name[PATH_MAX];
+
+ if (strcmp(section_name,
+ (curr_dso->short_name +
+ self->short_name_len)) == 0)
+ goto new_symbol;
+
+ if (strcmp(section_name, ".text") == 0) {
+ curr_map = map;
+ curr_dso = self;
+ goto new_symbol;
+ }
+
+ snprintf(dso_name, sizeof(dso_name),
+ "%s%s", self->short_name, section_name);
+
+ curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
+ if (curr_map == NULL) {
+ u64 start = sym.st_value;
+
+ if (kmodule)
+ start += map->start + shdr.sh_offset;
+
+ curr_dso = dso__new(dso_name);
+ if (curr_dso == NULL)
+ goto out_elf_end;
+ curr_dso->kernel = self->kernel;
+ curr_map = map__new2(start, curr_dso,
+ map->type);
+ if (curr_map == NULL) {
+ dso__delete(curr_dso);
+ goto out_elf_end;
+ }
+ curr_map->map_ip = identity__map_ip;
+ curr_map->unmap_ip = identity__map_ip;
+ curr_dso->origin = self->origin;
+ map_groups__insert(kmap->kmaps, curr_map);
+ dsos__add(&self->node, curr_dso);
+ dso__set_loaded(curr_dso, map->type);
+ } else
+ curr_dso = curr_map->dso;
+
+ goto new_symbol;
+ }
+
+ if (curr_dso->adjust_symbols) {
+ pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
+ "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__,
+ (u64)sym.st_value, (u64)shdr.sh_addr,
+ (u64)shdr.sh_offset);
+ sym.st_value -= shdr.sh_addr - shdr.sh_offset;
+ }
+ /*
+ * We need to figure out if the object was created from C++ sources
+ * DWARF DW_compile_unit has this, but we don't always have access
+ * to it...
+ */
+ demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
+ if (demangled != NULL)
+ elf_name = demangled;
+new_symbol:
+ f = symbol__new(sym.st_value, sym.st_size,
+ GELF_ST_BIND(sym.st_info), elf_name);
+ free(demangled);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(curr_map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&curr_dso->symbols[curr_map->type], f);
+ nr++;
+ }
+ }
+
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0) {
+ symbols__fixup_end(&self->symbols[map->type]);
+ if (kmap) {
+ /*
+ * We need to fixup this here too because we create new
+ * maps here, for things like vsyscall sections.
+ */
+ __map_groups__fixup_end(kmap->kmaps, map->type);
+ }
+ }
+ err = nr;
+out_elf_end:
+ elf_end(elf);
+out_close:
+ return err;
+}
+
+static bool dso__build_id_equal(const struct dso *self, u8 *build_id)
+{
+ return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0;
+}
+
+bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
+{
+ bool have_build_id = false;
+ struct dso *pos;
+
+ list_for_each_entry(pos, head, node) {
+ if (with_hits && !pos->hit)
+ continue;
+ if (pos->has_build_id) {
+ have_build_id = true;
+ continue;
+ }
+ if (filename__read_build_id(pos->long_name, pos->build_id,
+ sizeof(pos->build_id)) > 0) {
+ have_build_id = true;
+ pos->has_build_id = true;
+ }
+ }
+
+ return have_build_id;
+}
+
+/*
+ * Align offset to 4 bytes as needed for note name and descriptor data.
+ */
+#define NOTE_ALIGN(n) (((n) + 3) & -4U)
+
+static int elf_read_build_id(Elf *elf, void *bf, size_t size)
+{
+ int err = -1;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Scn *sec;
+ Elf_Kind ek;
+ void *ptr;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ ek = elf_kind(elf);
+ if (ek != ELF_K_ELF)
+ goto out;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_err("%s: cannot get elf header.\n", __func__);
+ goto out;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note.gnu.build-id", NULL);
+ if (sec == NULL) {
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".notes", NULL);
+ if (sec == NULL)
+ goto out;
+ }
+
+ data = elf_getdata(sec, NULL);
+ if (data == NULL)
+ goto out;
+
+ ptr = data->d_buf;
+ while (ptr < (data->d_buf + data->d_size)) {
+ GElf_Nhdr *nhdr = ptr;
+ int namesz = NOTE_ALIGN(nhdr->n_namesz),
+ descsz = NOTE_ALIGN(nhdr->n_descsz);
+ const char *name;
+
+ ptr += sizeof(*nhdr);
+ name = ptr;
+ ptr += namesz;
+ if (nhdr->n_type == NT_GNU_BUILD_ID &&
+ nhdr->n_namesz == sizeof("GNU")) {
+ if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+ memcpy(bf, ptr, BUILD_ID_SIZE);
+ err = BUILD_ID_SIZE;
+ break;
+ }
+ }
+ ptr += descsz;
+ }
+
+out:
+ return err;
+}
+
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ int fd, err = -1;
+ Elf *elf;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
+ goto out_close;
+ }
+
+ err = elf_read_build_id(elf, bf, size);
+
+ elf_end(elf);
+out_close:
+ close(fd);
+out:
+ return err;
+}
+
+int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
+{
+ int fd, err = -1;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ while (1) {
+ char bf[BUFSIZ];
+ GElf_Nhdr nhdr;
+ int namesz, descsz;
+
+ if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
+ break;
+
+ namesz = NOTE_ALIGN(nhdr.n_namesz);
+ descsz = NOTE_ALIGN(nhdr.n_descsz);
+ if (nhdr.n_type == NT_GNU_BUILD_ID &&
+ nhdr.n_namesz == sizeof("GNU")) {
+ if (read(fd, bf, namesz) != namesz)
+ break;
+ if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
+ if (read(fd, build_id,
+ BUILD_ID_SIZE) == BUILD_ID_SIZE) {
+ err = 0;
+ break;
+ }
+ } else if (read(fd, bf, descsz) != descsz)
+ break;
+ } else {
+ int n = namesz + descsz;
+ if (read(fd, bf, n) != n)
+ break;
+ }
+ }
+ close(fd);
+out:
+ return err;
+}
+
+char dso__symtab_origin(const struct dso *self)
+{
+ static const char origin[] = {
+ [DSO__ORIG_KERNEL] = 'k',
+ [DSO__ORIG_JAVA_JIT] = 'j',
+ [DSO__ORIG_BUILD_ID_CACHE] = 'B',
+ [DSO__ORIG_FEDORA] = 'f',
+ [DSO__ORIG_UBUNTU] = 'u',
+ [DSO__ORIG_BUILDID] = 'b',
+ [DSO__ORIG_DSO] = 'd',
+ [DSO__ORIG_KMODULE] = 'K',
+ [DSO__ORIG_GUEST_KERNEL] = 'g',
+ [DSO__ORIG_GUEST_KMODULE] = 'G',
+ };
+
+ if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
+ return '!';
+ return origin[self->origin];
+}
+
+int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
+{
+ int size = PATH_MAX;
+ char *name;
+ int ret = -1;
+ int fd;
+ struct machine *machine;
+ const char *root_dir;
+ int want_symtab;
+
+ dso__set_loaded(self, map->type);
+
+ if (self->kernel == DSO_TYPE_KERNEL)
+ return dso__load_kernel_sym(self, map, filter);
+ else if (self->kernel == DSO_TYPE_GUEST_KERNEL)
+ return dso__load_guest_kernel_sym(self, map, filter);
+
+ if (map->groups && map->groups->machine)
+ machine = map->groups->machine;
+ else
+ machine = NULL;
+
+ name = malloc(size);
+ if (!name)
+ return -1;
+
+ self->adjust_symbols = 0;
+
+ if (strncmp(self->name, "/tmp/perf-", 10) == 0) {
+ ret = dso__load_perf_map(self, map, filter);
+ self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT :
+ DSO__ORIG_NOT_FOUND;
+ return ret;
+ }
+
+ /* Iterate over candidate debug images.
+ * On the first pass, only load images if they have a full symtab.
+ * Failing that, do a second pass where we accept .dynsym also
+ */
+ for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1;
+ self->origin != DSO__ORIG_NOT_FOUND;
+ self->origin++) {
+ switch (self->origin) {
+ case DSO__ORIG_BUILD_ID_CACHE:
+ /* skip the locally configured cache if a symfs is given */
+ if (symbol_conf.symfs[0] ||
+ (dso__build_id_filename(self, name, size) == NULL)) {
+ continue;
+ }
+ break;
+ case DSO__ORIG_FEDORA:
+ snprintf(name, size, "%s/usr/lib/debug%s.debug",
+ symbol_conf.symfs, self->long_name);
+ break;
+ case DSO__ORIG_UBUNTU:
+ snprintf(name, size, "%s/usr/lib/debug%s",
+ symbol_conf.symfs, self->long_name);
+ break;
+ case DSO__ORIG_BUILDID: {
+ char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+
+ if (!self->has_build_id)
+ continue;
+
+ build_id__sprintf(self->build_id,
+ sizeof(self->build_id),
+ build_id_hex);
+ snprintf(name, size,
+ "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
+ symbol_conf.symfs, build_id_hex, build_id_hex + 2);
+ }
+ break;
+ case DSO__ORIG_DSO:
+ snprintf(name, size, "%s%s",
+ symbol_conf.symfs, self->long_name);
+ break;
+ case DSO__ORIG_GUEST_KMODULE:
+ if (map->groups && map->groups->machine)
+ root_dir = map->groups->machine->root_dir;
+ else
+ root_dir = "";
+ snprintf(name, size, "%s%s%s", symbol_conf.symfs,
+ root_dir, self->long_name);
+ break;
+
+ case DSO__ORIG_KMODULE:
+ snprintf(name, size, "%s%s", symbol_conf.symfs,
+ self->long_name);
+ break;
+
+ default:
+ /*
+ * If we wanted a full symtab but no image had one,
+ * relax our requirements and repeat the search.
+ */
+ if (want_symtab) {
+ want_symtab = 0;
+ self->origin = DSO__ORIG_BUILD_ID_CACHE;
+ } else
+ continue;
+ }
+
+ /* Name is now the name of the next image to try */
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ continue;
+
+ ret = dso__load_sym(self, map, name, fd, filter, 0,
+ want_symtab);
+ close(fd);
+
+ /*
+ * Some people seem to have debuginfo files _WITHOUT_ debug
+ * info!?!?
+ */
+ if (!ret)
+ continue;
+
+ if (ret > 0) {
+ int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
+ if (nr_plt > 0)
+ ret += nr_plt;
+ break;
+ }
+ }
+
+ free(name);
+ if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
+ return 0;
+ return ret;
+}
+
+struct map *map_groups__find_by_name(struct map_groups *self,
+ enum map_type type, const char *name)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *map = rb_entry(nd, struct map, rb_node);
+
+ if (map->dso && strcmp(map->dso->short_name, name) == 0)
+ return map;
+ }
+
+ return NULL;
+}
+
+static int dso__kernel_module_get_build_id(struct dso *self,
+ const char *root_dir)
+{
+ char filename[PATH_MAX];
+ /*
+ * kernel module short names are of the form "[module]" and
+ * we need just "module" here.
+ */
+ const char *name = self->short_name + 1;
+
+ snprintf(filename, sizeof(filename),
+ "%s/sys/module/%.*s/notes/.note.gnu.build-id",
+ root_dir, (int)strlen(name) - 1, name);
+
+ if (sysfs__read_build_id(filename, self->build_id,
+ sizeof(self->build_id)) == 0)
+ self->has_build_id = true;
+
+ return 0;
+}
+
+static int map_groups__set_modules_path_dir(struct map_groups *self,
+ const char *dir_name)
+{
+ struct dirent *dent;
+ DIR *dir = opendir(dir_name);
+ int ret = 0;
+
+ if (!dir) {
+ pr_debug("%s: cannot open %s dir\n", __func__, dir_name);
+ return -1;
+ }
+
+ while ((dent = readdir(dir)) != NULL) {
+ char path[PATH_MAX];
+ struct stat st;
+
+ /*sshfs might return bad dent->d_type, so we have to stat*/
+ sprintf(path, "%s/%s", dir_name, dent->d_name);
+ if (stat(path, &st))
+ continue;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (!strcmp(dent->d_name, ".") ||
+ !strcmp(dent->d_name, ".."))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ dir_name, dent->d_name);
+ ret = map_groups__set_modules_path_dir(self, path);
+ if (ret < 0)
+ goto out;
+ } else {
+ char *dot = strrchr(dent->d_name, '.'),
+ dso_name[PATH_MAX];
+ struct map *map;
+ char *long_name;
+
+ if (dot == NULL || strcmp(dot, ".ko"))
+ continue;
+ snprintf(dso_name, sizeof(dso_name), "[%.*s]",
+ (int)(dot - dent->d_name), dent->d_name);
+
+ strxfrchar(dso_name, '-', '_');
+ map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name);
+ if (map == NULL)
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ dir_name, dent->d_name);
+
+ long_name = strdup(path);
+ if (long_name == NULL) {
+ ret = -1;
+ goto out;
+ }
+ dso__set_long_name(map->dso, long_name);
+ map->dso->lname_alloc = 1;
+ dso__kernel_module_get_build_id(map->dso, "");
+ }
+ }
+
+out:
+ closedir(dir);
+ return ret;
+}
+
+static char *get_kernel_version(const char *root_dir)
+{
+ char version[PATH_MAX];
+ FILE *file;
+ char *name, *tmp;
+ const char *prefix = "Linux version ";
+
+ sprintf(version, "%s/proc/version", root_dir);
+ file = fopen(version, "r");
+ if (!file)
+ return NULL;
+
+ version[0] = '\0';
+ tmp = fgets(version, sizeof(version), file);
+ fclose(file);
+
+ name = strstr(version, prefix);
+ if (!name)
+ return NULL;
+ name += strlen(prefix);
+ tmp = strchr(name, ' ');
+ if (tmp)
+ *tmp = '\0';
+
+ return strdup(name);
+}
+
+static int machine__set_modules_path(struct machine *self)
+{
+ char *version;
+ char modules_path[PATH_MAX];
+
+ version = get_kernel_version(self->root_dir);
+ if (!version)
+ return -1;
+
+ snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel",
+ self->root_dir, version);
+ free(version);
+
+ return map_groups__set_modules_path_dir(&self->kmaps, modules_path);
+}
+
+/*
+ * Constructor variant for modules (where we know from /proc/modules where
+ * they are loaded) and for vmlinux, where only after we load all the
+ * symbols we'll know where it starts and ends.
+ */
+static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
+{
+ struct map *self = calloc(1, (sizeof(*self) +
+ (dso->kernel ? sizeof(struct kmap) : 0)));
+ if (self != NULL) {
+ /*
+ * ->end will be filled after we load all the symbols
+ */
+ map__init(self, type, start, 0, 0, dso);
+ }
+
+ return self;
+}
+
+struct map *machine__new_module(struct machine *self, u64 start,
+ const char *filename)
+{
+ struct map *map;
+ struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename);
+
+ if (dso == NULL)
+ return NULL;
+
+ map = map__new2(start, dso, MAP__FUNCTION);
+ if (map == NULL)
+ return NULL;
+
+ if (machine__is_host(self))
+ dso->origin = DSO__ORIG_KMODULE;
+ else
+ dso->origin = DSO__ORIG_GUEST_KMODULE;
+ map_groups__insert(&self->kmaps, map);
+ return map;
+}
+
+static int machine__create_modules(struct machine *self)
+{
+ char *line = NULL;
+ size_t n;
+ FILE *file;
+ struct map *map;
+ const char *modules;
+ char path[PATH_MAX];
+
+ if (machine__is_default_guest(self))
+ modules = symbol_conf.default_guest_modules;
+ else {
+ sprintf(path, "%s/proc/modules", self->root_dir);
+ modules = path;
+ }
+
+ file = fopen(modules, "r");
+ if (file == NULL)
+ return -1;
+
+ while (!feof(file)) {
+ char name[PATH_MAX];
+ u64 start;
+ char *sep;
+ int line_len;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0)
+ break;
+
+ if (!line)
+ goto out_failure;
+
+ line[--line_len] = '\0'; /* \n */
+
+ sep = strrchr(line, 'x');
+ if (sep == NULL)
+ continue;
+
+ hex2u64(sep + 1, &start);
+
+ sep = strchr(line, ' ');
+ if (sep == NULL)
+ continue;
+
+ *sep = '\0';
+
+ snprintf(name, sizeof(name), "[%s]", line);
+ map = machine__new_module(self, start, name);
+ if (map == NULL)
+ goto out_delete_line;
+ dso__kernel_module_get_build_id(map->dso, self->root_dir);
+ }
+
+ free(line);
+ fclose(file);
+
+ return machine__set_modules_path(self);
+
+out_delete_line:
+ free(line);
+out_failure:
+ return -1;
+}
+
+int dso__load_vmlinux(struct dso *self, struct map *map,
+ const char *vmlinux, symbol_filter_t filter)
+{
+ int err = -1, fd;
+ char symfs_vmlinux[PATH_MAX];
+
+ snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s",
+ symbol_conf.symfs, vmlinux);
+ fd = open(symfs_vmlinux, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ dso__set_loaded(self, map->type);
+ err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
+ close(fd);
+
+ if (err > 0)
+ pr_debug("Using %s for symbols\n", symfs_vmlinux);
+
+ return err;
+}
+
+int dso__load_vmlinux_path(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ int i, err = 0;
+ char *filename;
+
+ pr_debug("Looking at the vmlinux_path (%d entries long)\n",
+ vmlinux_path__nr_entries + 1);
+
+ filename = dso__build_id_filename(self, NULL, 0);
+ if (filename != NULL) {
+ err = dso__load_vmlinux(self, map, filename, filter);
+ if (err > 0) {
+ dso__set_long_name(self, filename);
+ goto out;
+ }
+ free(filename);
+ }
+
+ for (i = 0; i < vmlinux_path__nr_entries; ++i) {
+ err = dso__load_vmlinux(self, map, vmlinux_path[i], filter);
+ if (err > 0) {
+ dso__set_long_name(self, strdup(vmlinux_path[i]));
+ break;
+ }
+ }
+out:
+ return err;
+}
+
+static int dso__load_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ int err;
+ const char *kallsyms_filename = NULL;
+ char *kallsyms_allocated_filename = NULL;
+ /*
+ * Step 1: if the user specified a kallsyms or vmlinux filename, use
+ * it and only it, reporting errors to the user if it cannot be used.
+ *
+ * For instance, try to analyse an ARM perf.data file _without_ a
+ * build-id, or if the user specifies the wrong path to the right
+ * vmlinux file, obviously we can't fallback to another vmlinux (a
+ * x86_86 one, on the machine where analysis is being performed, say),
+ * or worse, /proc/kallsyms.
+ *
+ * If the specified file _has_ a build-id and there is a build-id
+ * section in the perf.data file, we will still do the expected
+ * validation in dso__load_vmlinux and will bail out if they don't
+ * match.
+ */
+ if (symbol_conf.kallsyms_name != NULL) {
+ kallsyms_filename = symbol_conf.kallsyms_name;
+ goto do_kallsyms;
+ }
+
+ if (symbol_conf.vmlinux_name != NULL) {
+ err = dso__load_vmlinux(self, map,
+ symbol_conf.vmlinux_name, filter);
+ if (err > 0) {
+ dso__set_long_name(self,
+ strdup(symbol_conf.vmlinux_name));
+ goto out_fixup;
+ }
+ return err;
+ }
+
+ if (vmlinux_path != NULL) {
+ err = dso__load_vmlinux_path(self, map, filter);
+ if (err > 0)
+ goto out_fixup;
+ }
+
+ /* do not try local files if a symfs was given */
+ if (symbol_conf.symfs[0] != 0)
+ return -1;
+
+ /*
+ * Say the kernel DSO was created when processing the build-id header table,
+ * we have a build-id, so check if it is the same as the running kernel,
+ * using it if it is.
+ */
+ if (self->has_build_id) {
+ u8 kallsyms_build_id[BUILD_ID_SIZE];
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
+ sizeof(kallsyms_build_id)) == 0) {
+ if (dso__build_id_equal(self, kallsyms_build_id)) {
+ kallsyms_filename = "/proc/kallsyms";
+ goto do_kallsyms;
+ }
+ }
+ /*
+ * Now look if we have it on the build-id cache in
+ * $HOME/.debug/[kernel.kallsyms].
+ */
+ build_id__sprintf(self->build_id, sizeof(self->build_id),
+ sbuild_id);
+
+ if (asprintf(&kallsyms_allocated_filename,
+ "%s/.debug/[kernel.kallsyms]/%s",
+ getenv("HOME"), sbuild_id) == -1) {
+ pr_err("Not enough memory for kallsyms file lookup\n");
+ return -1;
+ }
+
+ kallsyms_filename = kallsyms_allocated_filename;
+
+ if (access(kallsyms_filename, F_OK)) {
+ pr_err("No kallsyms or vmlinux with build-id %s "
+ "was found\n", sbuild_id);
+ free(kallsyms_allocated_filename);
+ return -1;
+ }
+ } else {
+ /*
+ * Last resort, if we don't have a build-id and couldn't find
+ * any vmlinux file, try the running kernel kallsyms table.
+ */
+ kallsyms_filename = "/proc/kallsyms";
+ }
+
+do_kallsyms:
+ err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
+ if (err > 0)
+ pr_debug("Using %s for symbols\n", kallsyms_filename);
+ free(kallsyms_allocated_filename);
+
+ if (err > 0) {
+out_fixup:
+ if (kallsyms_filename != NULL)
+ dso__set_long_name(self, strdup("[kernel.kallsyms]"));
+ map__fixup_start(map);
+ map__fixup_end(map);
+ }
+
+ return err;
+}
+
+static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ int err;
+ const char *kallsyms_filename = NULL;
+ struct machine *machine;
+ char path[PATH_MAX];
+
+ if (!map->groups) {
+ pr_debug("Guest kernel map hasn't the point to groups\n");
+ return -1;
+ }
+ machine = map->groups->machine;
+
+ if (machine__is_default_guest(machine)) {
+ /*
+ * if the user specified a vmlinux filename, use it and only
+ * it, reporting errors to the user if it cannot be used.
+ * Or use file guest_kallsyms inputted by user on commandline
+ */
+ if (symbol_conf.default_guest_vmlinux_name != NULL) {
+ err = dso__load_vmlinux(self, map,
+ symbol_conf.default_guest_vmlinux_name, filter);
+ goto out_try_fixup;
+ }
+
+ kallsyms_filename = symbol_conf.default_guest_kallsyms;
+ if (!kallsyms_filename)
+ return -1;
+ } else {
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
+ kallsyms_filename = path;
+ }
+
+ err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
+ if (err > 0)
+ pr_debug("Using %s for symbols\n", kallsyms_filename);
+
+out_try_fixup:
+ if (err > 0) {
+ if (kallsyms_filename != NULL) {
+ machine__mmap_name(machine, path, sizeof(path));
+ dso__set_long_name(self, strdup(path));
+ }
+ map__fixup_start(map);
+ map__fixup_end(map);
+ }
+
+ return err;
+}
+
+static void dsos__add(struct list_head *head, struct dso *dso)
+{
+ list_add_tail(&dso->node, head);
+}
+
+static struct dso *dsos__find(struct list_head *head, const char *name)
+{
+ struct dso *pos;
+
+ list_for_each_entry(pos, head, node)
+ if (strcmp(pos->long_name, name) == 0)
+ return pos;
+ return NULL;
+}
+
+struct dso *__dsos__findnew(struct list_head *head, const char *name)
+{
+ struct dso *dso = dsos__find(head, name);
+
+ if (!dso) {
+ dso = dso__new(name);
+ if (dso != NULL) {
+ dsos__add(head, dso);
+ dso__set_basename(dso);
+ }
+ }
+
+ return dso;
+}
+
+size_t __dsos__fprintf(struct list_head *head, FILE *fp)
+{
+ struct dso *pos;
+ size_t ret = 0;
+
+ list_for_each_entry(pos, head, node) {
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ ret += dso__fprintf(pos, i, fp);
+ }
+
+ return ret;
+}
+
+size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp)
+{
+ struct rb_node *nd;
+ size_t ret = 0;
+
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret += __dsos__fprintf(&pos->kernel_dsos, fp);
+ ret += __dsos__fprintf(&pos->user_dsos, fp);
+ }
+
+ return ret;
+}
+
+static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
+ bool with_hits)
+{
+ struct dso *pos;
+ size_t ret = 0;
+
+ list_for_each_entry(pos, head, node) {
+ if (with_hits && !pos->hit)
+ continue;
+ ret += dso__fprintf_buildid(pos, fp);
+ ret += fprintf(fp, " %s\n", pos->long_name);
+ }
+ return ret;
+}
+
+size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits)
+{
+ return __dsos__fprintf_buildid(&self->kernel_dsos, fp, with_hits) +
+ __dsos__fprintf_buildid(&self->user_dsos, fp, with_hits);
+}
+
+size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits)
+{
+ struct rb_node *nd;
+ size_t ret = 0;
+
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret += machine__fprintf_dsos_buildid(pos, fp, with_hits);
+ }
+ return ret;
+}
+
+struct dso *dso__new_kernel(const char *name)
+{
+ struct dso *self = dso__new(name ?: "[kernel.kallsyms]");
+
+ if (self != NULL) {
+ dso__set_short_name(self, "[kernel]");
+ self->kernel = DSO_TYPE_KERNEL;
+ }
+
+ return self;
+}
+
+static struct dso *dso__new_guest_kernel(struct machine *machine,
+ const char *name)
+{
+ char bf[PATH_MAX];
+ struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf)));
+
+ if (self != NULL) {
+ dso__set_short_name(self, "[guest.kernel]");
+ self->kernel = DSO_TYPE_GUEST_KERNEL;
+ }
+
+ return self;
+}
+
+void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine)
+{
+ char path[PATH_MAX];
+
+ if (machine__is_default_guest(machine))
+ return;
+ sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
+ if (sysfs__read_build_id(path, self->build_id,
+ sizeof(self->build_id)) == 0)
+ self->has_build_id = true;
+}
+
+static struct dso *machine__create_kernel(struct machine *self)
+{
+ const char *vmlinux_name = NULL;
+ struct dso *kernel;
+
+ if (machine__is_host(self)) {
+ vmlinux_name = symbol_conf.vmlinux_name;
+ kernel = dso__new_kernel(vmlinux_name);
+ } else {
+ if (machine__is_default_guest(self))
+ vmlinux_name = symbol_conf.default_guest_vmlinux_name;
+ kernel = dso__new_guest_kernel(self, vmlinux_name);
+ }
+
+ if (kernel != NULL) {
+ dso__read_running_kernel_build_id(kernel, self);
+ dsos__add(&self->kernel_dsos, kernel);
+ }
+ return kernel;
+}
+
+struct process_args {
+ u64 start;
+};
+
+static int symbol__in_kernel(void *arg, const char *name,
+ char type __used, u64 start, u64 end __used)
+{
+ struct process_args *args = arg;
+
+ if (strchr(name, '['))
+ return 0;
+
+ args->start = start;
+ return 1;
+}
+
+/* Figure out the start address of kernel map from /proc/kallsyms */
+static u64 machine__get_kernel_start_addr(struct machine *machine)
+{
+ const char *filename;
+ char path[PATH_MAX];
+ struct process_args args;
+
+ if (machine__is_host(machine)) {
+ filename = "/proc/kallsyms";
+ } else {
+ if (machine__is_default_guest(machine))
+ filename = (char *)symbol_conf.default_guest_kallsyms;
+ else {
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
+ filename = path;
+ }
+ }
+
+ if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0)
+ return 0;
+
+ return args.start;
+}
+
+int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
+{
+ enum map_type type;
+ u64 start = machine__get_kernel_start_addr(self);
+
+ for (type = 0; type < MAP__NR_TYPES; ++type) {
+ struct kmap *kmap;
+
+ self->vmlinux_maps[type] = map__new2(start, kernel, type);
+ if (self->vmlinux_maps[type] == NULL)
+ return -1;
+
+ self->vmlinux_maps[type]->map_ip =
+ self->vmlinux_maps[type]->unmap_ip = identity__map_ip;
+
+ kmap = map__kmap(self->vmlinux_maps[type]);
+ kmap->kmaps = &self->kmaps;
+ map_groups__insert(&self->kmaps, self->vmlinux_maps[type]);
+ }
+
+ return 0;
+}
+
+void machine__destroy_kernel_maps(struct machine *self)
+{
+ enum map_type type;
+
+ for (type = 0; type < MAP__NR_TYPES; ++type) {
+ struct kmap *kmap;
+
+ if (self->vmlinux_maps[type] == NULL)
+ continue;
+
+ kmap = map__kmap(self->vmlinux_maps[type]);
+ map_groups__remove(&self->kmaps, self->vmlinux_maps[type]);
+ if (kmap->ref_reloc_sym) {
+ /*
+ * ref_reloc_sym is shared among all maps, so free just
+ * on one of them.
+ */
+ if (type == MAP__FUNCTION) {
+ free((char *)kmap->ref_reloc_sym->name);
+ kmap->ref_reloc_sym->name = NULL;
+ free(kmap->ref_reloc_sym);
+ }
+ kmap->ref_reloc_sym = NULL;
+ }
+
+ map__delete(self->vmlinux_maps[type]);
+ self->vmlinux_maps[type] = NULL;
+ }
+}
+
+int machine__create_kernel_maps(struct machine *self)
+{
+ struct dso *kernel = machine__create_kernel(self);
+
+ if (kernel == NULL ||
+ __machine__create_kernel_maps(self, kernel) < 0)
+ return -1;
+
+ if (symbol_conf.use_modules && machine__create_modules(self) < 0)
+ pr_debug("Problems creating module maps, continuing anyway...\n");
+ /*
+ * Now that we have all the maps created, just set the ->end of them:
+ */
+ map_groups__fixup_end(&self->kmaps);
+ return 0;
+}
+
+static void vmlinux_path__exit(void)
+{
+ while (--vmlinux_path__nr_entries >= 0) {
+ free(vmlinux_path[vmlinux_path__nr_entries]);
+ vmlinux_path[vmlinux_path__nr_entries] = NULL;
+ }
+
+ free(vmlinux_path);
+ vmlinux_path = NULL;
+}
+
+static int vmlinux_path__init(void)
+{
+ struct utsname uts;
+ char bf[PATH_MAX];
+
+ vmlinux_path = malloc(sizeof(char *) * 5);
+ if (vmlinux_path == NULL)
+ return -1;
+
+ vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux");
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux");
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+
+ /* only try running kernel version if no symfs was given */
+ if (symbol_conf.symfs[0] != 0)
+ return 0;
+
+ if (uname(&uts) < 0)
+ return -1;
+
+ snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
+ uts.release);
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+
+ return 0;
+
+out_fail:
+ vmlinux_path__exit();
+ return -1;
+}
+
+size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp)
+{
+ int i;
+ size_t printed = 0;
+ struct dso *kdso = self->vmlinux_maps[MAP__FUNCTION]->dso;
+
+ if (kdso->has_build_id) {
+ char filename[PATH_MAX];
+ if (dso__build_id_filename(kdso, filename, sizeof(filename)))
+ printed += fprintf(fp, "[0] %s\n", filename);
+ }
+
+ for (i = 0; i < vmlinux_path__nr_entries; ++i)
+ printed += fprintf(fp, "[%d] %s\n",
+ i + kdso->has_build_id, vmlinux_path[i]);
+
+ return printed;
+}
+
+static int setup_list(struct strlist **list, const char *list_str,
+ const char *list_name)
+{
+ if (list_str == NULL)
+ return 0;
+
+ *list = strlist__new(true, list_str);
+ if (!*list) {
+ pr_err("problems parsing %s list\n", list_name);
+ return -1;
+ }
+ return 0;
+}
+
+int symbol__init(void)
+{
+ const char *symfs;
+
+ if (symbol_conf.initialized)
+ return 0;
+
+ elf_version(EV_CURRENT);
+ if (symbol_conf.sort_by_name)
+ symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
+ sizeof(struct symbol));
+
+ if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
+ return -1;
+
+ if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') {
+ pr_err("'.' is the only non valid --field-separator argument\n");
+ return -1;
+ }
+
+ if (setup_list(&symbol_conf.dso_list,
+ symbol_conf.dso_list_str, "dso") < 0)
+ return -1;
+
+ if (setup_list(&symbol_conf.comm_list,
+ symbol_conf.comm_list_str, "comm") < 0)
+ goto out_free_dso_list;
+
+ if (setup_list(&symbol_conf.sym_list,
+ symbol_conf.sym_list_str, "symbol") < 0)
+ goto out_free_comm_list;
+
+ /*
+ * A path to symbols of "/" is identical to ""
+ * reset here for simplicity.
+ */
+ symfs = realpath(symbol_conf.symfs, NULL);
+ if (symfs == NULL)
+ symfs = symbol_conf.symfs;
+ if (strcmp(symfs, "/") == 0)
+ symbol_conf.symfs = "";
+ if (symfs != symbol_conf.symfs)
+ free((void *)symfs);
+
+ symbol_conf.initialized = true;
+ return 0;
+
+out_free_dso_list:
+ strlist__delete(symbol_conf.dso_list);
+out_free_comm_list:
+ strlist__delete(symbol_conf.comm_list);
+ return -1;
+}
+
+void symbol__exit(void)
+{
+ if (!symbol_conf.initialized)
+ return;
+ strlist__delete(symbol_conf.sym_list);
+ strlist__delete(symbol_conf.dso_list);
+ strlist__delete(symbol_conf.comm_list);
+ vmlinux_path__exit();
+ symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
+ symbol_conf.initialized = false;
+}
+
+int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
+{
+ struct machine *machine = machines__findnew(self, pid);
+
+ if (machine == NULL)
+ return -1;
+
+ return machine__create_kernel_maps(machine);
+}
+
+static int hex(char ch)
+{
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ if ((ch >= 'A') && (ch <= 'F'))
+ return ch - 'A' + 10;
+ return -1;
+}
+
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+int hex2u64(const char *ptr, u64 *long_val)
+{
+ const char *p = ptr;
+ *long_val = 0;
+
+ while (*p) {
+ const int hex_val = hex(*p);
+
+ if (hex_val < 0)
+ break;
+
+ *long_val = (*long_val << 4) | hex_val;
+ p++;
+ }
+
+ return p - ptr;
+}
+
+char *strxfrchar(char *s, char from, char to)
+{
+ char *p = s;
+
+ while ((p = strchr(p, from)) != NULL)
+ *p++ = to;
+
+ return s;
+}
+
+int machines__create_guest_kernel_maps(struct rb_root *self)
+{
+ int ret = 0;
+ struct dirent **namelist = NULL;
+ int i, items = 0;
+ char path[PATH_MAX];
+ pid_t pid;
+
+ if (symbol_conf.default_guest_vmlinux_name ||
+ symbol_conf.default_guest_modules ||
+ symbol_conf.default_guest_kallsyms) {
+ machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID);
+ }
+
+ if (symbol_conf.guestmount) {
+ items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL);
+ if (items <= 0)
+ return -ENOENT;
+ for (i = 0; i < items; i++) {
+ if (!isdigit(namelist[i]->d_name[0])) {
+ /* Filter out . and .. */
+ continue;
+ }
+ pid = atoi(namelist[i]->d_name);
+ sprintf(path, "%s/%s/proc/kallsyms",
+ symbol_conf.guestmount,
+ namelist[i]->d_name);
+ ret = access(path, R_OK);
+ if (ret) {
+ pr_debug("Can't access file %s\n", path);
+ goto failure;
+ }
+ machines__create_kernel_maps(self, pid);
+ }
+failure:
+ free(namelist);
+ }
+
+ return ret;
+}
+
+void machines__destroy_guest_kernel_maps(struct rb_root *self)
+{
+ struct rb_node *next = rb_first(self);
+
+ while (next) {
+ struct machine *pos = rb_entry(next, struct machine, rb_node);
+
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, self);
+ machine__delete(pos);
+ }
+}
+
+int machine__load_kallsyms(struct machine *self, const char *filename,
+ enum map_type type, symbol_filter_t filter)
+{
+ struct map *map = self->vmlinux_maps[type];
+ int ret = dso__load_kallsyms(map->dso, filename, map, filter);
+
+ if (ret > 0) {
+ dso__set_loaded(map->dso, type);
+ /*
+ * Since /proc/kallsyms will have multiple sessions for the
+ * kernel, with modules between them, fixup the end of all
+ * sections.
+ */
+ __map_groups__fixup_end(&self->kmaps, type);
+ }
+
+ return ret;
+}
+
+int machine__load_vmlinux_path(struct machine *self, enum map_type type,
+ symbol_filter_t filter)
+{
+ struct map *map = self->vmlinux_maps[type];
+ int ret = dso__load_vmlinux_path(map->dso, map, filter);
+
+ if (ret > 0) {
+ dso__set_loaded(map->dso, type);
+ map__reloc_vmlinux(map);
+ }
+
+ return ret;
+}
diff --git a/smartt-perf/util/symbol.h b/smartt-perf/util/symbol.h
new file mode 100644
index 0000000..670cd1c
--- /dev/null
+++ b/smartt-perf/util/symbol.h
@@ -0,0 +1,236 @@
+#ifndef __PERF_SYMBOL
+#define __PERF_SYMBOL 1
+
+#include <linux/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "map.h"
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <stdio.h>
+
+#ifdef HAVE_CPLUS_DEMANGLE
+extern char *cplus_demangle(const char *, int);
+
+static inline char *bfd_demangle(void __used *v, const char *c, int i)
+{
+ return cplus_demangle(c, i);
+}
+#else
+#ifdef NO_DEMANGLE
+static inline char *bfd_demangle(void __used *v, const char __used *c,
+ int __used i)
+{
+ return NULL;
+}
+#else
+#include <bfd.h>
+#endif
+#endif
+
+int hex2u64(const char *ptr, u64 *val);
+char *strxfrchar(char *s, char from, char to);
+
+/*
+ * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
+ * for newer versions we can use mmap to reduce memory usage:
+ */
+#ifdef LIBELF_NO_MMAP
+# define PERF_ELF_C_READ_MMAP ELF_C_READ
+#else
+# define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
+#endif
+
+#ifndef DMGL_PARAMS
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#endif
+
+#define BUILD_ID_SIZE 20
+
+struct symbol {
+ struct rb_node rb_node;
+ u64 start;
+ u64 end;
+ u16 namelen;
+ u8 binding;
+ char name[0];
+};
+
+void symbol__delete(struct symbol *self);
+
+struct strlist;
+
+struct symbol_conf {
+ unsigned short priv_size;
+ bool try_vmlinux_path,
+ use_modules,
+ sort_by_name,
+ show_nr_samples,
+ use_callchain,
+ exclude_other,
+ show_cpu_utilization,
+ initialized;
+ const char *vmlinux_name,
+ *kallsyms_name,
+ *source_prefix,
+ *field_sep;
+ const char *default_guest_vmlinux_name,
+ *default_guest_kallsyms,
+ *default_guest_modules;
+ const char *guestmount;
+ const char *dso_list_str,
+ *comm_list_str,
+ *sym_list_str,
+ *col_width_list_str;
+ struct strlist *dso_list,
+ *comm_list,
+ *sym_list;
+ const char *symfs;
+};
+
+extern struct symbol_conf symbol_conf;
+
+static inline void *symbol__priv(struct symbol *self)
+{
+ return ((void *)self) - symbol_conf.priv_size;
+}
+
+struct ref_reloc_sym {
+ const char *name;
+ u64 addr;
+ u64 unrelocated_addr;
+};
+
+struct map_symbol {
+ struct map *map;
+ struct symbol *sym;
+ bool unfolded;
+ bool has_children;
+};
+
+struct addr_location {
+ struct thread *thread;
+ struct map *map;
+ struct symbol *sym;
+ u64 addr;
+ char level;
+ bool filtered;
+ u8 cpumode;
+ s32 cpu;
+};
+
+enum dso_kernel_type {
+ DSO_TYPE_USER = 0,
+ DSO_TYPE_KERNEL,
+ DSO_TYPE_GUEST_KERNEL
+};
+
+struct dso {
+ struct list_head node;
+ struct rb_root symbols[MAP__NR_TYPES];
+ struct rb_root symbol_names[MAP__NR_TYPES];
+ enum dso_kernel_type kernel;
+ u8 adjust_symbols:1;
+ u8 slen_calculated:1;
+ u8 has_build_id:1;
+ u8 hit:1;
+ u8 annotate_warned:1;
+ u8 sname_alloc:1;
+ u8 lname_alloc:1;
+ unsigned char origin;
+ u8 sorted_by_name;
+ u8 loaded;
+ u8 build_id[BUILD_ID_SIZE];
+ const char *short_name;
+ char *long_name;
+ u16 long_name_len;
+ u16 short_name_len;
+ char name[0];
+};
+
+struct dso *dso__new(const char *name);
+struct dso *dso__new_kernel(const char *name);
+void dso__delete(struct dso *self);
+
+int dso__name_len(const struct dso *self);
+
+bool dso__loaded(const struct dso *self, enum map_type type);
+bool dso__sorted_by_name(const struct dso *self, enum map_type type);
+
+static inline void dso__set_loaded(struct dso *self, enum map_type type)
+{
+ self->loaded |= (1 << type);
+}
+
+void dso__sort_by_name(struct dso *self, enum map_type type);
+
+struct dso *__dsos__findnew(struct list_head *head, const char *name);
+
+int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
+int dso__load_vmlinux(struct dso *self, struct map *map,
+ const char *vmlinux, symbol_filter_t filter);
+int dso__load_vmlinux_path(struct dso *self, struct map *map,
+ symbol_filter_t filter);
+int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
+ symbol_filter_t filter);
+int machine__load_kallsyms(struct machine *self, const char *filename,
+ enum map_type type, symbol_filter_t filter);
+int machine__load_vmlinux_path(struct machine *self, enum map_type type,
+ symbol_filter_t filter);
+
+size_t __dsos__fprintf(struct list_head *head, FILE *fp);
+
+size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits);
+size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
+size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
+
+size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
+size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp);
+size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
+
+enum dso_origin {
+ DSO__ORIG_KERNEL = 0,
+ DSO__ORIG_GUEST_KERNEL,
+ DSO__ORIG_JAVA_JIT,
+ DSO__ORIG_BUILD_ID_CACHE,
+ DSO__ORIG_FEDORA,
+ DSO__ORIG_UBUNTU,
+ DSO__ORIG_BUILDID,
+ DSO__ORIG_DSO,
+ DSO__ORIG_GUEST_KMODULE,
+ DSO__ORIG_KMODULE,
+ DSO__ORIG_NOT_FOUND,
+};
+
+char dso__symtab_origin(const struct dso *self);
+void dso__set_long_name(struct dso *self, char *name);
+void dso__set_build_id(struct dso *self, void *build_id);
+void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine);
+struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
+struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
+ const char *name);
+
+int filename__read_build_id(const char *filename, void *bf, size_t size);
+int sysfs__read_build_id(const char *filename, void *bf, size_t size);
+bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
+int build_id__sprintf(const u8 *self, int len, char *bf);
+int kallsyms__parse(const char *filename, void *arg,
+ int (*process_symbol)(void *arg, const char *name,
+ char type, u64 start, u64 end));
+
+void machine__destroy_kernel_maps(struct machine *self);
+int __machine__create_kernel_maps(struct machine *self, struct dso *kernel);
+int machine__create_kernel_maps(struct machine *self);
+
+int machines__create_kernel_maps(struct rb_root *self, pid_t pid);
+int machines__create_guest_kernel_maps(struct rb_root *self);
+void machines__destroy_guest_kernel_maps(struct rb_root *self);
+
+int symbol__init(void);
+void symbol__exit(void);
+bool symbol_type__is_a(char symbol_type, enum map_type map_type);
+
+size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp);
+
+#endif /* __PERF_SYMBOL */
diff --git a/smartt-perf/util/thread.c b/smartt-perf/util/thread.c
new file mode 100644
index 0000000..00f4ead
--- /dev/null
+++ b/smartt-perf/util/thread.c
@@ -0,0 +1,195 @@
+#include "../perf.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "session.h"
+#include "thread.h"
+#include "util.h"
+#include "debug.h"
+
+/* Skip "." and ".." directories */
+static int filter(const struct dirent *dir)
+{
+ if (dir->d_name[0] == '.')
+ return 0;
+ else
+ return 1;
+}
+
+struct thread_map *thread_map__new_by_pid(pid_t pid)
+{
+ struct thread_map *threads;
+ char name[256];
+ int items;
+ struct dirent **namelist = NULL;
+ int i;
+
+ sprintf(name, "/proc/%d/task", pid);
+ items = scandir(name, &namelist, filter, NULL);
+ if (items <= 0)
+ return NULL;
+
+ threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
+ if (threads != NULL) {
+ for (i = 0; i < items; i++)
+ threads->map[i] = atoi(namelist[i]->d_name);
+ threads->nr = items;
+ }
+
+ for (i=0; i<items; i++)
+ free(namelist[i]);
+ free(namelist);
+
+ return threads;
+}
+
+struct thread_map *thread_map__new_by_tid(pid_t tid)
+{
+ struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
+
+ if (threads != NULL) {
+ threads->map[0] = tid;
+ threads->nr = 1;
+ }
+
+ return threads;
+}
+
+struct thread_map *thread_map__new(pid_t pid, pid_t tid)
+{
+ if (pid != -1)
+ return thread_map__new_by_pid(pid);
+ return thread_map__new_by_tid(tid);
+}
+
+static struct thread *thread__new(pid_t pid)
+{
+ struct thread *self = zalloc(sizeof(*self));
+
+ if (self != NULL) {
+ map_groups__init(&self->mg);
+ self->pid = pid;
+ self->comm = malloc(32);
+ if (self->comm)
+ snprintf(self->comm, 32, ":%d", self->pid);
+ }
+
+ return self;
+}
+
+void thread__delete(struct thread *self)
+{
+ map_groups__exit(&self->mg);
+ free(self->comm);
+ free(self);
+}
+
+int thread__set_comm(struct thread *self, const char *comm)
+{
+ int err;
+
+ if (self->comm)
+ free(self->comm);
+ self->comm = strdup(comm);
+ err = self->comm == NULL ? -ENOMEM : 0;
+ if (!err) {
+ self->comm_set = true;
+ map_groups__flush(&self->mg);
+ }
+ return err;
+}
+
+int thread__comm_len(struct thread *self)
+{
+ if (!self->comm_len) {
+ if (!self->comm)
+ return 0;
+ self->comm_len = strlen(self->comm);
+ }
+
+ return self->comm_len;
+}
+
+static size_t thread__fprintf(struct thread *self, FILE *fp)
+{
+ return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) +
+ map_groups__fprintf(&self->mg, verbose, fp);
+}
+
+struct thread *perf_session__findnew(struct perf_session *self, pid_t pid)
+{
+ struct rb_node **p = &self->threads.rb_node;
+ struct rb_node *parent = NULL;
+ struct thread *th;
+
+ /*
+ * Font-end cache - PID lookups come in blocks,
+ * so most of the time we dont have to look up
+ * the full rbtree:
+ */
+ if (self->last_match && self->last_match->pid == pid)
+ return self->last_match;
+
+ while (*p != NULL) {
+ parent = *p;
+ th = rb_entry(parent, struct thread, rb_node);
+
+ if (th->pid == pid) {
+ self->last_match = th;
+ return th;
+ }
+
+ if (pid < th->pid)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ th = thread__new(pid);
+ if (th != NULL) {
+ rb_link_node(&th->rb_node, parent, p);
+ rb_insert_color(&th->rb_node, &self->threads);
+ self->last_match = th;
+ }
+
+ return th;
+}
+
+void thread__insert_map(struct thread *self, struct map *map)
+{
+ map_groups__fixup_overlappings(&self->mg, map, verbose, stderr);
+ map_groups__insert(&self->mg, map);
+}
+
+int thread__fork(struct thread *self, struct thread *parent)
+{
+ int i;
+
+ if (parent->comm_set) {
+ if (self->comm)
+ free(self->comm);
+ self->comm = strdup(parent->comm);
+ if (!self->comm)
+ return -ENOMEM;
+ self->comm_set = true;
+ }
+
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ if (map_groups__clone(&self->mg, &parent->mg, i) < 0)
+ return -ENOMEM;
+ return 0;
+}
+
+size_t perf_session__fprintf(struct perf_session *self, FILE *fp)
+{
+ size_t ret = 0;
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) {
+ struct thread *pos = rb_entry(nd, struct thread, rb_node);
+
+ ret += thread__fprintf(pos, fp);
+ }
+
+ return ret;
+}
diff --git a/smartt-perf/util/thread.h b/smartt-perf/util/thread.h
new file mode 100644
index 0000000..d757410
--- /dev/null
+++ b/smartt-perf/util/thread.h
@@ -0,0 +1,62 @@
+#ifndef __PERF_THREAD_H
+#define __PERF_THREAD_H
+
+#include <linux/rbtree.h>
+#include <unistd.h>
+#include "symbol.h"
+
+struct thread {
+ union {
+ struct rb_node rb_node;
+ struct list_head node;
+ };
+ struct map_groups mg;
+ pid_t pid;
+ char shortname[3];
+ bool comm_set;
+ char *comm;
+ int comm_len;
+};
+
+struct thread_map {
+ int nr;
+ int map[];
+};
+
+struct perf_session;
+
+void thread__delete(struct thread *self);
+
+struct thread_map *thread_map__new_by_pid(pid_t pid);
+struct thread_map *thread_map__new_by_tid(pid_t tid);
+struct thread_map *thread_map__new(pid_t pid, pid_t tid);
+
+static inline void thread_map__delete(struct thread_map *threads)
+{
+ free(threads);
+}
+
+int thread__set_comm(struct thread *self, const char *comm);
+int thread__comm_len(struct thread *self);
+struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
+void thread__insert_map(struct thread *self, struct map *map);
+int thread__fork(struct thread *self, struct thread *parent);
+size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
+
+static inline struct map *thread__find_map(struct thread *self,
+ enum map_type type, u64 addr)
+{
+ return self ? map_groups__find(&self->mg, type, addr) : NULL;
+}
+
+void thread__find_addr_map(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al);
+
+void thread__find_addr_location(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al,
+ symbol_filter_t filter);
+#endif /* __PERF_THREAD_H */
diff --git a/smartt-perf/util/trace-event-info.c b/smartt-perf/util/trace-event-info.c
new file mode 100644
index 0000000..35729f4
--- /dev/null
+++ b/smartt-perf/util/trace-event-info.c
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+
+#include "../perf.h"
+#include "trace-event.h"
+#include "debugfs.h"
+#include "evsel.h"
+
+#define VERSION "0.5"
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+#define MAX_PATH 256
+
+#define TRACE_CTRL "tracing_on"
+#define TRACE "trace"
+#define AVAILABLE "available_tracers"
+#define CURRENT "current_tracer"
+#define ITER_CTRL "trace_options"
+#define MAX_LATENCY "tracing_max_latency"
+
+unsigned int page_size;
+
+static const char *output_file = "trace.info";
+static int output_fd;
+
+struct event_list {
+ struct event_list *next;
+ const char *event;
+};
+
+struct events {
+ struct events *sibling;
+ struct events *children;
+ struct events *next;
+ char *name;
+};
+
+
+
+static void die(const char *fmt, ...)
+{
+ va_list ap;
+ int ret = errno;
+
+ if (errno)
+ perror("trace-cmd");
+ else
+ ret = -1;
+
+ va_start(ap, fmt);
+ fprintf(stderr, " ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ exit(ret);
+}
+
+void *malloc_or_die(unsigned int size)
+{
+ void *data;
+
+ data = malloc(size);
+ if (!data)
+ die("malloc");
+ return data;
+}
+
+static const char *find_debugfs(void)
+{
+ const char *path = debugfs_mount(NULL);
+
+ if (!path)
+ die("Your kernel not support debugfs filesystem");
+
+ return path;
+}
+
+/*
+ * Finds the path to the debugfs/tracing
+ * Allocates the string and stores it.
+ */
+static const char *find_tracing_dir(void)
+{
+ static char *tracing;
+ static int tracing_found;
+ const char *debugfs;
+
+ if (tracing_found)
+ return tracing;
+
+ debugfs = find_debugfs();
+
+ tracing = malloc_or_die(strlen(debugfs) + 9);
+
+ sprintf(tracing, "%s/tracing", debugfs);
+
+ tracing_found = 1;
+ return tracing;
+}
+
+static char *get_tracing_file(const char *name)
+{
+ const char *tracing;
+ char *file;
+
+ tracing = find_tracing_dir();
+ if (!tracing)
+ return NULL;
+
+ file = malloc_or_die(strlen(tracing) + strlen(name) + 2);
+
+ sprintf(file, "%s/%s", tracing, name);
+ return file;
+}
+
+static void put_tracing_file(char *file)
+{
+ free(file);
+}
+
+static ssize_t calc_data_size;
+
+static ssize_t write_or_die(const void *buf, size_t len)
+{
+ int ret;
+
+ if (calc_data_size) {
+ calc_data_size += len;
+ return len;
+ }
+
+ ret = write(output_fd, buf, len);
+ if (ret < 0)
+ die("writing to '%s'", output_file);
+
+ return ret;
+}
+
+int bigendian(void)
+{
+ unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
+ unsigned int *ptr;
+
+ ptr = (unsigned int *)(void *)str;
+ return *ptr == 0x01020304;
+}
+
+static unsigned long long copy_file_fd(int fd)
+{
+ unsigned long long size = 0;
+ char buf[BUFSIZ];
+ int r;
+
+ do {
+ r = read(fd, buf, BUFSIZ);
+ if (r > 0) {
+ size += r;
+ write_or_die(buf, r);
+ }
+ } while (r > 0);
+
+ return size;
+}
+
+static unsigned long long copy_file(const char *file)
+{
+ unsigned long long size = 0;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ die("Can't read '%s'", file);
+ size = copy_file_fd(fd);
+ close(fd);
+
+ return size;
+}
+
+static unsigned long get_size_fd(int fd)
+{
+ unsigned long long size = 0;
+ char buf[BUFSIZ];
+ int r;
+
+ do {
+ r = read(fd, buf, BUFSIZ);
+ if (r > 0)
+ size += r;
+ } while (r > 0);
+
+ lseek(fd, 0, SEEK_SET);
+
+ return size;
+}
+
+static unsigned long get_size(const char *file)
+{
+ unsigned long long size = 0;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ die("Can't read '%s'", file);
+ size = get_size_fd(fd);
+ close(fd);
+
+ return size;
+}
+
+static void read_header_files(void)
+{
+ unsigned long long size, check_size;
+ char *path;
+ int fd;
+
+ path = get_tracing_file("events/header_page");
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ die("can't read '%s'", path);
+
+ /* unfortunately, you can not stat debugfs files for size */
+ size = get_size_fd(fd);
+
+ write_or_die("header_page", 12);
+ write_or_die(&size, 8);
+ check_size = copy_file_fd(fd);
+ close(fd);
+
+ if (size != check_size)
+ die("wrong size for '%s' size=%lld read=%lld",
+ path, size, check_size);
+ put_tracing_file(path);
+
+ path = get_tracing_file("events/header_event");
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ die("can't read '%s'", path);
+
+ size = get_size_fd(fd);
+
+ write_or_die("header_event", 13);
+ write_or_die(&size, 8);
+ check_size = copy_file_fd(fd);
+ if (size != check_size)
+ die("wrong size for '%s'", path);
+ put_tracing_file(path);
+ close(fd);
+}
+
+static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
+{
+ while (tps) {
+ if (!strcmp(sys, tps->name))
+ return true;
+ tps = tps->next;
+ }
+
+ return false;
+}
+
+static void copy_event_system(const char *sys, struct tracepoint_path *tps)
+{
+ unsigned long long size, check_size;
+ struct dirent *dent;
+ struct stat st;
+ char *format;
+ DIR *dir;
+ int count = 0;
+ int ret;
+
+ dir = opendir(sys);
+ if (!dir)
+ die("can't read directory '%s'", sys);
+
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ !name_in_tp_list(dent->d_name, tps))
+ continue;
+ format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
+ sprintf(format, "%s/%s/format", sys, dent->d_name);
+ ret = stat(format, &st);
+ free(format);
+ if (ret < 0)
+ continue;
+ count++;
+ }
+
+ write_or_die(&count, 4);
+
+ rewinddir(dir);
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ !name_in_tp_list(dent->d_name, tps))
+ continue;
+ format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
+ sprintf(format, "%s/%s/format", sys, dent->d_name);
+ ret = stat(format, &st);
+
+ if (ret >= 0) {
+ /* unfortunately, you can not stat debugfs files for size */
+ size = get_size(format);
+ write_or_die(&size, 8);
+ check_size = copy_file(format);
+ if (size != check_size)
+ die("error in size of file '%s'", format);
+ }
+
+ free(format);
+ }
+ closedir(dir);
+}
+
+static void read_ftrace_files(struct tracepoint_path *tps)
+{
+ char *path;
+
+ path = get_tracing_file("events/ftrace");
+
+ copy_event_system(path, tps);
+
+ put_tracing_file(path);
+}
+
+static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
+{
+ while (tps) {
+ if (!strcmp(sys, tps->system))
+ return true;
+ tps = tps->next;
+ }
+
+ return false;
+}
+
+static void read_event_files(struct tracepoint_path *tps)
+{
+ struct dirent *dent;
+ struct stat st;
+ char *path;
+ char *sys;
+ DIR *dir;
+ int count = 0;
+ int ret;
+
+ path = get_tracing_file("events");
+
+ dir = opendir(path);
+ if (!dir)
+ die("can't read directory '%s'", path);
+
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ strcmp(dent->d_name, "ftrace") == 0 ||
+ !system_in_tp_list(dent->d_name, tps))
+ continue;
+ count++;
+ }
+
+ write_or_die(&count, 4);
+
+ rewinddir(dir);
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ strcmp(dent->d_name, "ftrace") == 0 ||
+ !system_in_tp_list(dent->d_name, tps))
+ continue;
+ sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
+ sprintf(sys, "%s/%s", path, dent->d_name);
+ ret = stat(sys, &st);
+ if (ret >= 0) {
+ write_or_die(dent->d_name, strlen(dent->d_name) + 1);
+ copy_event_system(sys, tps);
+ }
+ free(sys);
+ }
+
+ closedir(dir);
+ put_tracing_file(path);
+}
+
+static void read_proc_kallsyms(void)
+{
+ unsigned int size, check_size;
+ const char *path = "/proc/kallsyms";
+ struct stat st;
+ int ret;
+
+ ret = stat(path, &st);
+ if (ret < 0) {
+ /* not found */
+ size = 0;
+ write_or_die(&size, 4);
+ return;
+ }
+ size = get_size(path);
+ write_or_die(&size, 4);
+ check_size = copy_file(path);
+ if (size != check_size)
+ die("error in size of file '%s'", path);
+
+}
+
+static void read_ftrace_printk(void)
+{
+ unsigned int size, check_size;
+ char *path;
+ struct stat st;
+ int ret;
+
+ path = get_tracing_file("printk_formats");
+ ret = stat(path, &st);
+ if (ret < 0) {
+ /* not found */
+ size = 0;
+ write_or_die(&size, 4);
+ goto out;
+ }
+ size = get_size(path);
+ write_or_die(&size, 4);
+ check_size = copy_file(path);
+ if (size != check_size)
+ die("error in size of file '%s'", path);
+out:
+ put_tracing_file(path);
+}
+
+static struct tracepoint_path *
+get_tracepoints_path(struct list_head *pattrs)
+{
+ struct tracepoint_path path, *ppath = &path;
+ struct perf_evsel *pos;
+ int nr_tracepoints = 0;
+
+ list_for_each_entry(pos, pattrs, node) {
+ if (pos->attr.type != PERF_TYPE_TRACEPOINT)
+ continue;
+ ++nr_tracepoints;
+ ppath->next = tracepoint_id_to_path(pos->attr.config);
+ if (!ppath->next)
+ die("%s\n", "No memory to alloc tracepoints list");
+ ppath = ppath->next;
+ }
+
+ return nr_tracepoints > 0 ? path.next : NULL;
+}
+
+bool have_tracepoints(struct list_head *pattrs)
+{
+ struct perf_evsel *pos;
+
+ list_for_each_entry(pos, pattrs, node)
+ if (pos->attr.type == PERF_TYPE_TRACEPOINT)
+ return true;
+
+ return false;
+}
+
+int read_tracing_data(int fd, struct list_head *pattrs)
+{
+ char buf[BUFSIZ];
+ struct tracepoint_path *tps = get_tracepoints_path(pattrs);
+
+ /*
+ * What? No tracepoints? No sense writing anything here, bail out.
+ */
+ if (tps == NULL)
+ return -1;
+
+ output_fd = fd;
+
+ buf[0] = 23;
+ buf[1] = 8;
+ buf[2] = 68;
+ memcpy(buf + 3, "tracing", 7);
+
+ write_or_die(buf, 10);
+
+ write_or_die(VERSION, strlen(VERSION) + 1);
+
+ /* save endian */
+ if (bigendian())
+ buf[0] = 1;
+ else
+ buf[0] = 0;
+
+ write_or_die(buf, 1);
+
+ /* save size of long */
+ buf[0] = sizeof(long);
+ write_or_die(buf, 1);
+
+ /* save page_size */
+ page_size = sysconf(_SC_PAGESIZE);
+ write_or_die(&page_size, 4);
+
+ read_header_files();
+ read_ftrace_files(tps);
+ read_event_files(tps);
+ read_proc_kallsyms();
+ read_ftrace_printk();
+
+ return 0;
+}
+
+ssize_t read_tracing_data_size(int fd, struct list_head *pattrs)
+{
+ ssize_t size;
+ int err = 0;
+
+ calc_data_size = 1;
+ err = read_tracing_data(fd, pattrs);
+ size = calc_data_size - 1;
+ calc_data_size = 0;
+
+ if (err < 0)
+ return err;
+
+ return size;
+}
diff --git a/smartt-perf/util/trace-event-parse.c b/smartt-perf/util/trace-event-parse.c
new file mode 100644
index 0000000..73a0222
--- /dev/null
+++ b/smartt-perf/util/trace-event-parse.c
@@ -0,0 +1,3233 @@
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The parts for function graph printing was taken and modified from the
+ * Linux Kernel that were written by Frederic Weisbecker.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#undef _GNU_SOURCE
+#include "../perf.h"
+#include "util.h"
+#include "trace-event.h"
+
+int header_page_ts_offset;
+int header_page_ts_size;
+int header_page_size_offset;
+int header_page_size_size;
+int header_page_overwrite_offset;
+int header_page_overwrite_size;
+int header_page_data_offset;
+int header_page_data_size;
+
+bool latency_format;
+
+static char *input_buf;
+static unsigned long long input_buf_ptr;
+static unsigned long long input_buf_siz;
+
+static int cpus;
+static int long_size;
+static int is_flag_field;
+static int is_symbolic_field;
+
+static struct format_field *
+find_any_field(struct event *event, const char *name);
+
+static void init_input_buf(char *buf, unsigned long long size)
+{
+ input_buf = buf;
+ input_buf_siz = size;
+ input_buf_ptr = 0;
+}
+
+struct cmdline {
+ char *comm;
+ int pid;
+};
+
+static struct cmdline *cmdlines;
+static int cmdline_count;
+
+static int cmdline_cmp(const void *a, const void *b)
+{
+ const struct cmdline *ca = a;
+ const struct cmdline *cb = b;
+
+ if (ca->pid < cb->pid)
+ return -1;
+ if (ca->pid > cb->pid)
+ return 1;
+
+ return 0;
+}
+
+void parse_cmdlines(char *file, int size __unused)
+{
+ struct cmdline_list {
+ struct cmdline_list *next;
+ char *comm;
+ int pid;
+ } *list = NULL, *item;
+ char *line;
+ char *next = NULL;
+ int i;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ item = malloc_or_die(sizeof(*item));
+ sscanf(line, "%d %as", &item->pid,
+ (float *)(void *)&item->comm); /* workaround gcc warning */
+ item->next = list;
+ list = item;
+ line = strtok_r(NULL, "\n", &next);
+ cmdline_count++;
+ }
+
+ cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count);
+
+ i = 0;
+ while (list) {
+ cmdlines[i].pid = list->pid;
+ cmdlines[i].comm = list->comm;
+ i++;
+ item = list;
+ list = list->next;
+ free(item);
+ }
+
+ qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+}
+
+static struct func_map {
+ unsigned long long addr;
+ char *func;
+ char *mod;
+} *func_list;
+static unsigned int func_count;
+
+static int func_cmp(const void *a, const void *b)
+{
+ const struct func_map *fa = a;
+ const struct func_map *fb = b;
+
+ if (fa->addr < fb->addr)
+ return -1;
+ if (fa->addr > fb->addr)
+ return 1;
+
+ return 0;
+}
+
+void parse_proc_kallsyms(char *file, unsigned int size __unused)
+{
+ struct func_list {
+ struct func_list *next;
+ unsigned long long addr;
+ char *func;
+ char *mod;
+ } *list = NULL, *item;
+ char *line;
+ char *next = NULL;
+ char *addr_str;
+ char ch;
+ int ret;
+ int i;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ item = malloc_or_die(sizeof(*item));
+ item->mod = NULL;
+ ret = sscanf(line, "%as %c %as\t[%as",
+ (float *)(void *)&addr_str, /* workaround gcc warning */
+ &ch,
+ (float *)(void *)&item->func,
+ (float *)(void *)&item->mod);
+ item->addr = strtoull(addr_str, NULL, 16);
+ free(addr_str);
+
+ /* truncate the extra ']' */
+ if (item->mod)
+ item->mod[strlen(item->mod) - 1] = 0;
+
+
+ item->next = list;
+ list = item;
+ line = strtok_r(NULL, "\n", &next);
+ func_count++;
+ }
+
+ func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1));
+
+ i = 0;
+ while (list) {
+ func_list[i].func = list->func;
+ func_list[i].addr = list->addr;
+ func_list[i].mod = list->mod;
+ i++;
+ item = list;
+ list = list->next;
+ free(item);
+ }
+
+ qsort(func_list, func_count, sizeof(*func_list), func_cmp);
+
+ /*
+ * Add a special record at the end.
+ */
+ func_list[func_count].func = NULL;
+ func_list[func_count].addr = 0;
+ func_list[func_count].mod = NULL;
+}
+
+/*
+ * We are searching for a record in between, not an exact
+ * match.
+ */
+static int func_bcmp(const void *a, const void *b)
+{
+ const struct func_map *fa = a;
+ const struct func_map *fb = b;
+
+ if ((fa->addr == fb->addr) ||
+
+ (fa->addr > fb->addr &&
+ fa->addr < (fb+1)->addr))
+ return 0;
+
+ if (fa->addr < fb->addr)
+ return -1;
+
+ return 1;
+}
+
+static struct func_map *find_func(unsigned long long addr)
+{
+ struct func_map *func;
+ struct func_map key;
+
+ key.addr = addr;
+
+ func = bsearch(&key, func_list, func_count, sizeof(*func_list),
+ func_bcmp);
+
+ return func;
+}
+
+void print_funcs(void)
+{
+ int i;
+
+ for (i = 0; i < (int)func_count; i++) {
+ printf("%016llx %s",
+ func_list[i].addr,
+ func_list[i].func);
+ if (func_list[i].mod)
+ printf(" [%s]\n", func_list[i].mod);
+ else
+ printf("\n");
+ }
+}
+
+static struct printk_map {
+ unsigned long long addr;
+ char *printk;
+} *printk_list;
+static unsigned int printk_count;
+
+static int printk_cmp(const void *a, const void *b)
+{
+ const struct func_map *fa = a;
+ const struct func_map *fb = b;
+
+ if (fa->addr < fb->addr)
+ return -1;
+ if (fa->addr > fb->addr)
+ return 1;
+
+ return 0;
+}
+
+static struct printk_map *find_printk(unsigned long long addr)
+{
+ struct printk_map *printk;
+ struct printk_map key;
+
+ key.addr = addr;
+
+ printk = bsearch(&key, printk_list, printk_count, sizeof(*printk_list),
+ printk_cmp);
+
+ return printk;
+}
+
+void parse_ftrace_printk(char *file, unsigned int size __unused)
+{
+ struct printk_list {
+ struct printk_list *next;
+ unsigned long long addr;
+ char *printk;
+ } *list = NULL, *item;
+ char *line;
+ char *next = NULL;
+ char *addr_str;
+ int i;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ addr_str = strsep(&line, ":");
+ if (!line) {
+ warning("error parsing print strings");
+ break;
+ }
+ item = malloc_or_die(sizeof(*item));
+ item->addr = strtoull(addr_str, NULL, 16);
+ /* fmt still has a space, skip it */
+ item->printk = strdup(line+1);
+ item->next = list;
+ list = item;
+ line = strtok_r(NULL, "\n", &next);
+ printk_count++;
+ }
+
+ printk_list = malloc_or_die(sizeof(*printk_list) * printk_count + 1);
+
+ i = 0;
+ while (list) {
+ printk_list[i].printk = list->printk;
+ printk_list[i].addr = list->addr;
+ i++;
+ item = list;
+ list = list->next;
+ free(item);
+ }
+
+ qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp);
+}
+
+void print_printk(void)
+{
+ int i;
+
+ for (i = 0; i < (int)printk_count; i++) {
+ printf("%016llx %s\n",
+ printk_list[i].addr,
+ printk_list[i].printk);
+ }
+}
+
+static struct event *alloc_event(void)
+{
+ struct event *event;
+
+ event = malloc_or_die(sizeof(*event));
+ memset(event, 0, sizeof(*event));
+
+ return event;
+}
+
+enum event_type {
+ EVENT_ERROR,
+ EVENT_NONE,
+ EVENT_SPACE,
+ EVENT_NEWLINE,
+ EVENT_OP,
+ EVENT_DELIM,
+ EVENT_ITEM,
+ EVENT_DQUOTE,
+ EVENT_SQUOTE,
+};
+
+static struct event *event_list;
+
+static void add_event(struct event *event)
+{
+ event->next = event_list;
+ event_list = event;
+}
+
+static int event_item_type(enum event_type type)
+{
+ switch (type) {
+ case EVENT_ITEM ... EVENT_SQUOTE:
+ return 1;
+ case EVENT_ERROR ... EVENT_DELIM:
+ default:
+ return 0;
+ }
+}
+
+static void free_arg(struct print_arg *arg)
+{
+ if (!arg)
+ return;
+
+ switch (arg->type) {
+ case PRINT_ATOM:
+ if (arg->atom.atom)
+ free(arg->atom.atom);
+ break;
+ case PRINT_NULL:
+ case PRINT_FIELD ... PRINT_OP:
+ default:
+ /* todo */
+ break;
+ }
+
+ free(arg);
+}
+
+static enum event_type get_type(int ch)
+{
+ if (ch == '\n')
+ return EVENT_NEWLINE;
+ if (isspace(ch))
+ return EVENT_SPACE;
+ if (isalnum(ch) || ch == '_')
+ return EVENT_ITEM;
+ if (ch == '\'')
+ return EVENT_SQUOTE;
+ if (ch == '"')
+ return EVENT_DQUOTE;
+ if (!isprint(ch))
+ return EVENT_NONE;
+ if (ch == '(' || ch == ')' || ch == ',')
+ return EVENT_DELIM;
+
+ return EVENT_OP;
+}
+
+static int __read_char(void)
+{
+ if (input_buf_ptr >= input_buf_siz)
+ return -1;
+
+ return input_buf[input_buf_ptr++];
+}
+
+static int __peek_char(void)
+{
+ if (input_buf_ptr >= input_buf_siz)
+ return -1;
+
+ return input_buf[input_buf_ptr];
+}
+
+static enum event_type __read_token(char **tok)
+{
+ char buf[BUFSIZ];
+ int ch, last_ch, quote_ch, next_ch;
+ int i = 0;
+ int tok_size = 0;
+ enum event_type type;
+
+ *tok = NULL;
+
+
+ ch = __read_char();
+ if (ch < 0)
+ return EVENT_NONE;
+
+ type = get_type(ch);
+ if (type == EVENT_NONE)
+ return type;
+
+ buf[i++] = ch;
+
+ switch (type) {
+ case EVENT_NEWLINE:
+ case EVENT_DELIM:
+ *tok = malloc_or_die(2);
+ (*tok)[0] = ch;
+ (*tok)[1] = 0;
+ return type;
+
+ case EVENT_OP:
+ switch (ch) {
+ case '-':
+ next_ch = __peek_char();
+ if (next_ch == '>') {
+ buf[i++] = __read_char();
+ break;
+ }
+ /* fall through */
+ case '+':
+ case '|':
+ case '&':
+ case '>':
+ case '<':
+ last_ch = ch;
+ ch = __peek_char();
+ if (ch != last_ch)
+ goto test_equal;
+ buf[i++] = __read_char();
+ switch (last_ch) {
+ case '>':
+ case '<':
+ goto test_equal;
+ default:
+ break;
+ }
+ break;
+ case '!':
+ case '=':
+ goto test_equal;
+ default: /* what should we do instead? */
+ break;
+ }
+ buf[i] = 0;
+ *tok = strdup(buf);
+ return type;
+
+ test_equal:
+ ch = __peek_char();
+ if (ch == '=')
+ buf[i++] = __read_char();
+ break;
+
+ case EVENT_DQUOTE:
+ case EVENT_SQUOTE:
+ /* don't keep quotes */
+ i--;
+ quote_ch = ch;
+ last_ch = 0;
+ do {
+ if (i == (BUFSIZ - 1)) {
+ buf[i] = 0;
+ if (*tok) {
+ *tok = realloc(*tok, tok_size + BUFSIZ);
+ if (!*tok)
+ return EVENT_NONE;
+ strcat(*tok, buf);
+ } else
+ *tok = strdup(buf);
+
+ if (!*tok)
+ return EVENT_NONE;
+ tok_size += BUFSIZ;
+ i = 0;
+ }
+ last_ch = ch;
+ ch = __read_char();
+ buf[i++] = ch;
+ /* the '\' '\' will cancel itself */
+ if (ch == '\\' && last_ch == '\\')
+ last_ch = 0;
+ } while (ch != quote_ch || last_ch == '\\');
+ /* remove the last quote */
+ i--;
+ goto out;
+
+ case EVENT_ERROR ... EVENT_SPACE:
+ case EVENT_ITEM:
+ default:
+ break;
+ }
+
+ while (get_type(__peek_char()) == type) {
+ if (i == (BUFSIZ - 1)) {
+ buf[i] = 0;
+ if (*tok) {
+ *tok = realloc(*tok, tok_size + BUFSIZ);
+ if (!*tok)
+ return EVENT_NONE;
+ strcat(*tok, buf);
+ } else
+ *tok = strdup(buf);
+
+ if (!*tok)
+ return EVENT_NONE;
+ tok_size += BUFSIZ;
+ i = 0;
+ }
+ ch = __read_char();
+ buf[i++] = ch;
+ }
+
+ out:
+ buf[i] = 0;
+ if (*tok) {
+ *tok = realloc(*tok, tok_size + i);
+ if (!*tok)
+ return EVENT_NONE;
+ strcat(*tok, buf);
+ } else
+ *tok = strdup(buf);
+ if (!*tok)
+ return EVENT_NONE;
+
+ return type;
+}
+
+static void free_token(char *tok)
+{
+ if (tok)
+ free(tok);
+}
+
+static enum event_type read_token(char **tok)
+{
+ enum event_type type;
+
+ for (;;) {
+ type = __read_token(tok);
+ if (type != EVENT_SPACE)
+ return type;
+
+ free_token(*tok);
+ }
+
+ /* not reached */
+ return EVENT_NONE;
+}
+
+/* no newline */
+static enum event_type read_token_item(char **tok)
+{
+ enum event_type type;
+
+ for (;;) {
+ type = __read_token(tok);
+ if (type != EVENT_SPACE && type != EVENT_NEWLINE)
+ return type;
+
+ free_token(*tok);
+ }
+
+ /* not reached */
+ return EVENT_NONE;
+}
+
+static int test_type(enum event_type type, enum event_type expect)
+{
+ if (type != expect) {
+ warning("Error: expected type %d but read %d",
+ expect, type);
+ return -1;
+ }
+ return 0;
+}
+
+static int __test_type_token(enum event_type type, char *token,
+ enum event_type expect, const char *expect_tok,
+ bool warn)
+{
+ if (type != expect) {
+ if (warn)
+ warning("Error: expected type %d but read %d",
+ expect, type);
+ return -1;
+ }
+
+ if (strcmp(token, expect_tok) != 0) {
+ if (warn)
+ warning("Error: expected '%s' but read '%s'",
+ expect_tok, token);
+ return -1;
+ }
+ return 0;
+}
+
+static int test_type_token(enum event_type type, char *token,
+ enum event_type expect, const char *expect_tok)
+{
+ return __test_type_token(type, token, expect, expect_tok, true);
+}
+
+static int __read_expect_type(enum event_type expect, char **tok, int newline_ok)
+{
+ enum event_type type;
+
+ if (newline_ok)
+ type = read_token(tok);
+ else
+ type = read_token_item(tok);
+ return test_type(type, expect);
+}
+
+static int read_expect_type(enum event_type expect, char **tok)
+{
+ return __read_expect_type(expect, tok, 1);
+}
+
+static int __read_expected(enum event_type expect, const char *str,
+ int newline_ok, bool warn)
+{
+ enum event_type type;
+ char *token;
+ int ret;
+
+ if (newline_ok)
+ type = read_token(&token);
+ else
+ type = read_token_item(&token);
+
+ ret = __test_type_token(type, token, expect, str, warn);
+
+ free_token(token);
+
+ return ret;
+}
+
+static int read_expected(enum event_type expect, const char *str)
+{
+ return __read_expected(expect, str, 1, true);
+}
+
+static int read_expected_item(enum event_type expect, const char *str)
+{
+ return __read_expected(expect, str, 0, true);
+}
+
+static char *event_read_name(void)
+{
+ char *token;
+
+ if (read_expected(EVENT_ITEM, "name") < 0)
+ return NULL;
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ return NULL;
+
+ if (read_expect_type(EVENT_ITEM, &token) < 0)
+ goto fail;
+
+ return token;
+
+ fail:
+ free_token(token);
+ return NULL;
+}
+
+static int event_read_id(void)
+{
+ char *token;
+ int id;
+
+ if (read_expected_item(EVENT_ITEM, "ID") < 0)
+ return -1;
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ return -1;
+
+ if (read_expect_type(EVENT_ITEM, &token) < 0)
+ goto fail;
+
+ id = strtoul(token, NULL, 0);
+ free_token(token);
+ return id;
+
+ fail:
+ free_token(token);
+ return -1;
+}
+
+static int field_is_string(struct format_field *field)
+{
+ if ((field->flags & FIELD_IS_ARRAY) &&
+ (!strstr(field->type, "char") || !strstr(field->type, "u8") ||
+ !strstr(field->type, "s8")))
+ return 1;
+
+ return 0;
+}
+
+static int field_is_dynamic(struct format_field *field)
+{
+ if (!strncmp(field->type, "__data_loc", 10))
+ return 1;
+
+ return 0;
+}
+
+static int event_read_fields(struct event *event, struct format_field **fields)
+{
+ struct format_field *field = NULL;
+ enum event_type type;
+ char *token;
+ char *last_token;
+ int count = 0;
+
+ do {
+ type = read_token(&token);
+ if (type == EVENT_NEWLINE) {
+ free_token(token);
+ return count;
+ }
+
+ count++;
+
+ if (test_type_token(type, token, EVENT_ITEM, "field"))
+ goto fail;
+ free_token(token);
+
+ type = read_token(&token);
+ /*
+ * The ftrace fields may still use the "special" name.
+ * Just ignore it.
+ */
+ if (event->flags & EVENT_FL_ISFTRACE &&
+ type == EVENT_ITEM && strcmp(token, "special") == 0) {
+ free_token(token);
+ type = read_token(&token);
+ }
+
+ if (test_type_token(type, token, EVENT_OP, ":") < 0)
+ return -1;
+
+ if (read_expect_type(EVENT_ITEM, &token) < 0)
+ goto fail;
+
+ last_token = token;
+
+ field = malloc_or_die(sizeof(*field));
+ memset(field, 0, sizeof(*field));
+
+ /* read the rest of the type */
+ for (;;) {
+ type = read_token(&token);
+ if (type == EVENT_ITEM ||
+ (type == EVENT_OP && strcmp(token, "*") == 0) ||
+ /*
+ * Some of the ftrace fields are broken and have
+ * an illegal "." in them.
+ */
+ (event->flags & EVENT_FL_ISFTRACE &&
+ type == EVENT_OP && strcmp(token, ".") == 0)) {
+
+ if (strcmp(token, "*") == 0)
+ field->flags |= FIELD_IS_POINTER;
+
+ if (field->type) {
+ field->type = realloc(field->type,
+ strlen(field->type) +
+ strlen(last_token) + 2);
+ strcat(field->type, " ");
+ strcat(field->type, last_token);
+ } else
+ field->type = last_token;
+ last_token = token;
+ continue;
+ }
+
+ break;
+ }
+
+ if (!field->type) {
+ die("no type found");
+ goto fail;
+ }
+ field->name = last_token;
+
+ if (test_type(type, EVENT_OP))
+ goto fail;
+
+ if (strcmp(token, "[") == 0) {
+ enum event_type last_type = type;
+ char *brackets = token;
+ int len;
+
+ field->flags |= FIELD_IS_ARRAY;
+
+ type = read_token(&token);
+ while (strcmp(token, "]") != 0) {
+ if (last_type == EVENT_ITEM &&
+ type == EVENT_ITEM)
+ len = 2;
+ else
+ len = 1;
+ last_type = type;
+
+ brackets = realloc(brackets,
+ strlen(brackets) +
+ strlen(token) + len);
+ if (len == 2)
+ strcat(brackets, " ");
+ strcat(brackets, token);
+ free_token(token);
+ type = read_token(&token);
+ if (type == EVENT_NONE) {
+ die("failed to find token");
+ goto fail;
+ }
+ }
+
+ free_token(token);
+
+ brackets = realloc(brackets, strlen(brackets) + 2);
+ strcat(brackets, "]");
+
+ /* add brackets to type */
+
+ type = read_token(&token);
+ /*
+ * If the next token is not an OP, then it is of
+ * the format: type [] item;
+ */
+ if (type == EVENT_ITEM) {
+ field->type = realloc(field->type,
+ strlen(field->type) +
+ strlen(field->name) +
+ strlen(brackets) + 2);
+ strcat(field->type, " ");
+ strcat(field->type, field->name);
+ free_token(field->name);
+ strcat(field->type, brackets);
+ field->name = token;
+ type = read_token(&token);
+ } else {
+ field->type = realloc(field->type,
+ strlen(field->type) +
+ strlen(brackets) + 1);
+ strcat(field->type, brackets);
+ }
+ free(brackets);
+ }
+
+ if (field_is_string(field)) {
+ field->flags |= FIELD_IS_STRING;
+ if (field_is_dynamic(field))
+ field->flags |= FIELD_IS_DYNAMIC;
+ }
+
+ if (test_type_token(type, token, EVENT_OP, ";"))
+ goto fail;
+ free_token(token);
+
+ if (read_expected(EVENT_ITEM, "offset") < 0)
+ goto fail_expect;
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ goto fail_expect;
+
+ if (read_expect_type(EVENT_ITEM, &token))
+ goto fail;
+ field->offset = strtoul(token, NULL, 0);
+ free_token(token);
+
+ if (read_expected(EVENT_OP, ";") < 0)
+ goto fail_expect;
+
+ if (read_expected(EVENT_ITEM, "size") < 0)
+ goto fail_expect;
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ goto fail_expect;
+
+ if (read_expect_type(EVENT_ITEM, &token))
+ goto fail;
+ field->size = strtoul(token, NULL, 0);
+ free_token(token);
+
+ if (read_expected(EVENT_OP, ";") < 0)
+ goto fail_expect;
+
+ type = read_token(&token);
+ if (type != EVENT_NEWLINE) {
+ /* newer versions of the kernel have a "signed" type */
+ if (test_type_token(type, token, EVENT_ITEM, "signed"))
+ goto fail;
+
+ free_token(token);
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ goto fail_expect;
+
+ if (read_expect_type(EVENT_ITEM, &token))
+ goto fail;
+
+ if (strtoul(token, NULL, 0))
+ field->flags |= FIELD_IS_SIGNED;
+
+ free_token(token);
+ if (read_expected(EVENT_OP, ";") < 0)
+ goto fail_expect;
+
+ if (read_expect_type(EVENT_NEWLINE, &token))
+ goto fail;
+ }
+
+ free_token(token);
+
+ *fields = field;
+ fields = &field->next;
+
+ } while (1);
+
+ return 0;
+
+fail:
+ free_token(token);
+fail_expect:
+ if (field)
+ free(field);
+ return -1;
+}
+
+static int event_read_format(struct event *event)
+{
+ char *token;
+ int ret;
+
+ if (read_expected_item(EVENT_ITEM, "format") < 0)
+ return -1;
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ return -1;
+
+ if (read_expect_type(EVENT_NEWLINE, &token))
+ goto fail;
+ free_token(token);
+
+ ret = event_read_fields(event, &event->format.common_fields);
+ if (ret < 0)
+ return ret;
+ event->format.nr_common = ret;
+
+ ret = event_read_fields(event, &event->format.fields);
+ if (ret < 0)
+ return ret;
+ event->format.nr_fields = ret;
+
+ return 0;
+
+ fail:
+ free_token(token);
+ return -1;
+}
+
+enum event_type
+process_arg_token(struct event *event, struct print_arg *arg,
+ char **tok, enum event_type type);
+
+static enum event_type
+process_arg(struct event *event, struct print_arg *arg, char **tok)
+{
+ enum event_type type;
+ char *token;
+
+ type = read_token(&token);
+ *tok = token;
+
+ return process_arg_token(event, arg, tok, type);
+}
+
+static enum event_type
+process_cond(struct event *event, struct print_arg *top, char **tok)
+{
+ struct print_arg *arg, *left, *right;
+ enum event_type type;
+ char *token = NULL;
+
+ arg = malloc_or_die(sizeof(*arg));
+ memset(arg, 0, sizeof(*arg));
+
+ left = malloc_or_die(sizeof(*left));
+
+ right = malloc_or_die(sizeof(*right));
+
+ arg->type = PRINT_OP;
+ arg->op.left = left;
+ arg->op.right = right;
+
+ *tok = NULL;
+ type = process_arg(event, left, &token);
+ if (test_type_token(type, token, EVENT_OP, ":"))
+ goto out_free;
+
+ arg->op.op = token;
+
+ type = process_arg(event, right, &token);
+
+ top->op.right = arg;
+
+ *tok = token;
+ return type;
+
+out_free:
+ free_token(*tok);
+ free(right);
+ free(left);
+ free_arg(arg);
+ return EVENT_ERROR;
+}
+
+static enum event_type
+process_array(struct event *event, struct print_arg *top, char **tok)
+{
+ struct print_arg *arg;
+ enum event_type type;
+ char *token = NULL;
+
+ arg = malloc_or_die(sizeof(*arg));
+ memset(arg, 0, sizeof(*arg));
+
+ *tok = NULL;
+ type = process_arg(event, arg, &token);
+ if (test_type_token(type, token, EVENT_OP, "]"))
+ goto out_free;
+
+ top->op.right = arg;
+
+ free_token(token);
+ type = read_token_item(&token);
+ *tok = token;
+
+ return type;
+
+out_free:
+ free_token(*tok);
+ free_arg(arg);
+ return EVENT_ERROR;
+}
+
+static int get_op_prio(char *op)
+{
+ if (!op[1]) {
+ switch (op[0]) {
+ case '*':
+ case '/':
+ case '%':
+ return 6;
+ case '+':
+ case '-':
+ return 7;
+ /* '>>' and '<<' are 8 */
+ case '<':
+ case '>':
+ return 9;
+ /* '==' and '!=' are 10 */
+ case '&':
+ return 11;
+ case '^':
+ return 12;
+ case '|':
+ return 13;
+ case '?':
+ return 16;
+ default:
+ die("unknown op '%c'", op[0]);
+ return -1;
+ }
+ } else {
+ if (strcmp(op, "++") == 0 ||
+ strcmp(op, "--") == 0) {
+ return 3;
+ } else if (strcmp(op, ">>") == 0 ||
+ strcmp(op, "<<") == 0) {
+ return 8;
+ } else if (strcmp(op, ">=") == 0 ||
+ strcmp(op, "<=") == 0) {
+ return 9;
+ } else if (strcmp(op, "==") == 0 ||
+ strcmp(op, "!=") == 0) {
+ return 10;
+ } else if (strcmp(op, "&&") == 0) {
+ return 14;
+ } else if (strcmp(op, "||") == 0) {
+ return 15;
+ } else {
+ die("unknown op '%s'", op);
+ return -1;
+ }
+ }
+}
+
+static void set_op_prio(struct print_arg *arg)
+{
+
+ /* single ops are the greatest */
+ if (!arg->op.left || arg->op.left->type == PRINT_NULL) {
+ arg->op.prio = 0;
+ return;
+ }
+
+ arg->op.prio = get_op_prio(arg->op.op);
+}
+
+static enum event_type
+process_op(struct event *event, struct print_arg *arg, char **tok)
+{
+ struct print_arg *left, *right = NULL;
+ enum event_type type;
+ char *token;
+
+ /* the op is passed in via tok */
+ token = *tok;
+
+ if (arg->type == PRINT_OP && !arg->op.left) {
+ /* handle single op */
+ if (token[1]) {
+ die("bad op token %s", token);
+ return EVENT_ERROR;
+ }
+ switch (token[0]) {
+ case '!':
+ case '+':
+ case '-':
+ break;
+ default:
+ die("bad op token %s", token);
+ return EVENT_ERROR;
+ }
+
+ /* make an empty left */
+ left = malloc_or_die(sizeof(*left));
+ left->type = PRINT_NULL;
+ arg->op.left = left;
+
+ right = malloc_or_die(sizeof(*right));
+ arg->op.right = right;
+
+ type = process_arg(event, right, tok);
+
+ } else if (strcmp(token, "?") == 0) {
+
+ left = malloc_or_die(sizeof(*left));
+ /* copy the top arg to the left */
+ *left = *arg;
+
+ arg->type = PRINT_OP;
+ arg->op.op = token;
+ arg->op.left = left;
+ arg->op.prio = 0;
+
+ type = process_cond(event, arg, tok);
+
+ } else if (strcmp(token, ">>") == 0 ||
+ strcmp(token, "<<") == 0 ||
+ strcmp(token, "&") == 0 ||
+ strcmp(token, "|") == 0 ||
+ strcmp(token, "&&") == 0 ||
+ strcmp(token, "||") == 0 ||
+ strcmp(token, "-") == 0 ||
+ strcmp(token, "+") == 0 ||
+ strcmp(token, "*") == 0 ||
+ strcmp(token, "^") == 0 ||
+ strcmp(token, "/") == 0 ||
+ strcmp(token, "<") == 0 ||
+ strcmp(token, ">") == 0 ||
+ strcmp(token, "==") == 0 ||
+ strcmp(token, "!=") == 0) {
+
+ left = malloc_or_die(sizeof(*left));
+
+ /* copy the top arg to the left */
+ *left = *arg;
+
+ arg->type = PRINT_OP;
+ arg->op.op = token;
+ arg->op.left = left;
+
+ set_op_prio(arg);
+
+ right = malloc_or_die(sizeof(*right));
+
+ type = read_token_item(&token);
+ *tok = token;
+
+ /* could just be a type pointer */
+ if ((strcmp(arg->op.op, "*") == 0) &&
+ type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
+ if (left->type != PRINT_ATOM)
+ die("bad pointer type");
+ left->atom.atom = realloc(left->atom.atom,
+ sizeof(left->atom.atom) + 3);
+ strcat(left->atom.atom, " *");
+ *arg = *left;
+ free(arg);
+
+ return type;
+ }
+
+ type = process_arg_token(event, right, tok, type);
+
+ arg->op.right = right;
+
+ } else if (strcmp(token, "[") == 0) {
+
+ left = malloc_or_die(sizeof(*left));
+ *left = *arg;
+
+ arg->type = PRINT_OP;
+ arg->op.op = token;
+ arg->op.left = left;
+
+ arg->op.prio = 0;
+ type = process_array(event, arg, tok);
+
+ } else {
+ warning("unknown op '%s'", token);
+ event->flags |= EVENT_FL_FAILED;
+ /* the arg is now the left side */
+ return EVENT_NONE;
+ }
+
+ if (type == EVENT_OP) {
+ int prio;
+
+ /* higher prios need to be closer to the root */
+ prio = get_op_prio(*tok);
+
+ if (prio > arg->op.prio)
+ return process_op(event, arg, tok);
+
+ return process_op(event, right, tok);
+ }
+
+ return type;
+}
+
+static enum event_type
+process_entry(struct event *event __unused, struct print_arg *arg,
+ char **tok)
+{
+ enum event_type type;
+ char *field;
+ char *token;
+
+ if (read_expected(EVENT_OP, "->") < 0)
+ return EVENT_ERROR;
+
+ if (read_expect_type(EVENT_ITEM, &token) < 0)
+ goto fail;
+ field = token;
+
+ arg->type = PRINT_FIELD;
+ arg->field.name = field;
+
+ if (is_flag_field) {
+ arg->field.field = find_any_field(event, arg->field.name);
+ arg->field.field->flags |= FIELD_IS_FLAG;
+ is_flag_field = 0;
+ } else if (is_symbolic_field) {
+ arg->field.field = find_any_field(event, arg->field.name);
+ arg->field.field->flags |= FIELD_IS_SYMBOLIC;
+ is_symbolic_field = 0;
+ }
+
+ type = read_token(&token);
+ *tok = token;
+
+ return type;
+
+fail:
+ free_token(token);
+ return EVENT_ERROR;
+}
+
+static char *arg_eval (struct print_arg *arg);
+
+static long long arg_num_eval(struct print_arg *arg)
+{
+ long long left, right;
+ long long val = 0;
+
+ switch (arg->type) {
+ case PRINT_ATOM:
+ val = strtoll(arg->atom.atom, NULL, 0);
+ break;
+ case PRINT_TYPE:
+ val = arg_num_eval(arg->typecast.item);
+ break;
+ case PRINT_OP:
+ switch (arg->op.op[0]) {
+ case '|':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+ if (arg->op.op[1])
+ val = left || right;
+ else
+ val = left | right;
+ break;
+ case '&':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+ if (arg->op.op[1])
+ val = left && right;
+ else
+ val = left & right;
+ break;
+ case '<':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+ switch (arg->op.op[1]) {
+ case 0:
+ val = left < right;
+ break;
+ case '<':
+ val = left << right;
+ break;
+ case '=':
+ val = left <= right;
+ break;
+ default:
+ die("unknown op '%s'", arg->op.op);
+ }
+ break;
+ case '>':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+ switch (arg->op.op[1]) {
+ case 0:
+ val = left > right;
+ break;
+ case '>':
+ val = left >> right;
+ break;
+ case '=':
+ val = left >= right;
+ break;
+ default:
+ die("unknown op '%s'", arg->op.op);
+ }
+ break;
+ case '=':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+
+ if (arg->op.op[1] != '=')
+ die("unknown op '%s'", arg->op.op);
+
+ val = left == right;
+ break;
+ case '!':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+
+ switch (arg->op.op[1]) {
+ case '=':
+ val = left != right;
+ break;
+ default:
+ die("unknown op '%s'", arg->op.op);
+ }
+ break;
+ default:
+ die("unknown op '%s'", arg->op.op);
+ }
+ break;
+
+ case PRINT_NULL:
+ case PRINT_FIELD ... PRINT_SYMBOL:
+ case PRINT_STRING:
+ default:
+ die("invalid eval type %d", arg->type);
+
+ }
+ return val;
+}
+
+static char *arg_eval (struct print_arg *arg)
+{
+ long long val;
+ static char buf[20];
+
+ switch (arg->type) {
+ case PRINT_ATOM:
+ return arg->atom.atom;
+ case PRINT_TYPE:
+ return arg_eval(arg->typecast.item);
+ case PRINT_OP:
+ val = arg_num_eval(arg);
+ sprintf(buf, "%lld", val);
+ return buf;
+
+ case PRINT_NULL:
+ case PRINT_FIELD ... PRINT_SYMBOL:
+ case PRINT_STRING:
+ default:
+ die("invalid eval type %d", arg->type);
+ break;
+ }
+
+ return NULL;
+}
+
+static enum event_type
+process_fields(struct event *event, struct print_flag_sym **list, char **tok)
+{
+ enum event_type type;
+ struct print_arg *arg = NULL;
+ struct print_flag_sym *field;
+ char *token = NULL;
+ char *value;
+
+ do {
+ free_token(token);
+ type = read_token_item(&token);
+ if (test_type_token(type, token, EVENT_OP, "{"))
+ break;
+
+ arg = malloc_or_die(sizeof(*arg));
+
+ free_token(token);
+ type = process_arg(event, arg, &token);
+ if (test_type_token(type, token, EVENT_DELIM, ","))
+ goto out_free;
+
+ field = malloc_or_die(sizeof(*field));
+ memset(field, 0, sizeof(*field));
+
+ value = arg_eval(arg);
+ field->value = strdup(value);
+
+ free_token(token);
+ type = process_arg(event, arg, &token);
+ if (test_type_token(type, token, EVENT_OP, "}"))
+ goto out_free;
+
+ value = arg_eval(arg);
+ field->str = strdup(value);
+ free_arg(arg);
+ arg = NULL;
+
+ *list = field;
+ list = &field->next;
+
+ free_token(token);
+ type = read_token_item(&token);
+ } while (type == EVENT_DELIM && strcmp(token, ",") == 0);
+
+ *tok = token;
+ return type;
+
+out_free:
+ free_arg(arg);
+ free_token(token);
+
+ return EVENT_ERROR;
+}
+
+static enum event_type
+process_flags(struct event *event, struct print_arg *arg, char **tok)
+{
+ struct print_arg *field;
+ enum event_type type;
+ char *token;
+
+ memset(arg, 0, sizeof(*arg));
+ arg->type = PRINT_FLAGS;
+
+ if (read_expected_item(EVENT_DELIM, "(") < 0)
+ return EVENT_ERROR;
+
+ field = malloc_or_die(sizeof(*field));
+
+ type = process_arg(event, field, &token);
+ if (test_type_token(type, token, EVENT_DELIM, ","))
+ goto out_free;
+
+ arg->flags.field = field;
+
+ type = read_token_item(&token);
+ if (event_item_type(type)) {
+ arg->flags.delim = token;
+ type = read_token_item(&token);
+ }
+
+ if (test_type_token(type, token, EVENT_DELIM, ","))
+ goto out_free;
+
+ type = process_fields(event, &arg->flags.flags, &token);
+ if (test_type_token(type, token, EVENT_DELIM, ")"))
+ goto out_free;
+
+ free_token(token);
+ type = read_token_item(tok);
+ return type;
+
+out_free:
+ free_token(token);
+ return EVENT_ERROR;
+}
+
+static enum event_type
+process_symbols(struct event *event, struct print_arg *arg, char **tok)
+{
+ struct print_arg *field;
+ enum event_type type;
+ char *token;
+
+ memset(arg, 0, sizeof(*arg));
+ arg->type = PRINT_SYMBOL;
+
+ if (read_expected_item(EVENT_DELIM, "(") < 0)
+ return EVENT_ERROR;
+
+ field = malloc_or_die(sizeof(*field));
+
+ type = process_arg(event, field, &token);
+ if (test_type_token(type, token, EVENT_DELIM, ","))
+ goto out_free;
+
+ arg->symbol.field = field;
+
+ type = process_fields(event, &arg->symbol.symbols, &token);
+ if (test_type_token(type, token, EVENT_DELIM, ")"))
+ goto out_free;
+
+ free_token(token);
+ type = read_token_item(tok);
+ return type;
+
+out_free:
+ free_token(token);
+ return EVENT_ERROR;
+}
+
+static enum event_type
+process_paren(struct event *event, struct print_arg *arg, char **tok)
+{
+ struct print_arg *item_arg;
+ enum event_type type;
+ char *token;
+
+ type = process_arg(event, arg, &token);
+
+ if (type == EVENT_ERROR)
+ return EVENT_ERROR;
+
+ if (type == EVENT_OP)
+ type = process_op(event, arg, &token);
+
+ if (type == EVENT_ERROR)
+ return EVENT_ERROR;
+
+ if (test_type_token(type, token, EVENT_DELIM, ")")) {
+ free_token(token);
+ return EVENT_ERROR;
+ }
+
+ free_token(token);
+ type = read_token_item(&token);
+
+ /*
+ * If the next token is an item or another open paren, then
+ * this was a typecast.
+ */
+ if (event_item_type(type) ||
+ (type == EVENT_DELIM && strcmp(token, "(") == 0)) {
+
+ /* make this a typecast and contine */
+
+ /* prevous must be an atom */
+ if (arg->type != PRINT_ATOM)
+ die("previous needed to be PRINT_ATOM");
+
+ item_arg = malloc_or_die(sizeof(*item_arg));
+
+ arg->type = PRINT_TYPE;
+ arg->typecast.type = arg->atom.atom;
+ arg->typecast.item = item_arg;
+ type = process_arg_token(event, item_arg, &token, type);
+
+ }
+
+ *tok = token;
+ return type;
+}
+
+
+static enum event_type
+process_str(struct event *event __unused, struct print_arg *arg, char **tok)
+{
+ enum event_type type;
+ char *token;
+
+ if (read_expected(EVENT_DELIM, "(") < 0)
+ return EVENT_ERROR;
+
+ if (read_expect_type(EVENT_ITEM, &token) < 0)
+ goto fail;
+
+ arg->type = PRINT_STRING;
+ arg->string.string = token;
+ arg->string.offset = -1;
+
+ if (read_expected(EVENT_DELIM, ")") < 0)
+ return EVENT_ERROR;
+
+ type = read_token(&token);
+ *tok = token;
+
+ return type;
+fail:
+ free_token(token);
+ return EVENT_ERROR;
+}
+
+enum event_type
+process_arg_token(struct event *event, struct print_arg *arg,
+ char **tok, enum event_type type)
+{
+ char *token;
+ char *atom;
+
+ token = *tok;
+
+ switch (type) {
+ case EVENT_ITEM:
+ if (strcmp(token, "REC") == 0) {
+ free_token(token);
+ type = process_entry(event, arg, &token);
+ } else if (strcmp(token, "__print_flags") == 0) {
+ free_token(token);
+ is_flag_field = 1;
+ type = process_flags(event, arg, &token);
+ } else if (strcmp(token, "__print_symbolic") == 0) {
+ free_token(token);
+ is_symbolic_field = 1;
+ type = process_symbols(event, arg, &token);
+ } else if (strcmp(token, "__get_str") == 0) {
+ free_token(token);
+ type = process_str(event, arg, &token);
+ } else {
+ atom = token;
+ /* test the next token */
+ type = read_token_item(&token);
+
+ /* atoms can be more than one token long */
+ while (type == EVENT_ITEM) {
+ atom = realloc(atom, strlen(atom) + strlen(token) + 2);
+ strcat(atom, " ");
+ strcat(atom, token);
+ free_token(token);
+ type = read_token_item(&token);
+ }
+
+ /* todo, test for function */
+
+ arg->type = PRINT_ATOM;
+ arg->atom.atom = atom;
+ }
+ break;
+ case EVENT_DQUOTE:
+ case EVENT_SQUOTE:
+ arg->type = PRINT_ATOM;
+ arg->atom.atom = token;
+ type = read_token_item(&token);
+ break;
+ case EVENT_DELIM:
+ if (strcmp(token, "(") == 0) {
+ free_token(token);
+ type = process_paren(event, arg, &token);
+ break;
+ }
+ case EVENT_OP:
+ /* handle single ops */
+ arg->type = PRINT_OP;
+ arg->op.op = token;
+ arg->op.left = NULL;
+ type = process_op(event, arg, &token);
+
+ break;
+
+ case EVENT_ERROR ... EVENT_NEWLINE:
+ default:
+ die("unexpected type %d", type);
+ }
+ *tok = token;
+
+ return type;
+}
+
+static int event_read_print_args(struct event *event, struct print_arg **list)
+{
+ enum event_type type = EVENT_ERROR;
+ struct print_arg *arg;
+ char *token;
+ int args = 0;
+
+ do {
+ if (type == EVENT_NEWLINE) {
+ free_token(token);
+ type = read_token_item(&token);
+ continue;
+ }
+
+ arg = malloc_or_die(sizeof(*arg));
+ memset(arg, 0, sizeof(*arg));
+
+ type = process_arg(event, arg, &token);
+
+ if (type == EVENT_ERROR) {
+ free_arg(arg);
+ return -1;
+ }
+
+ *list = arg;
+ args++;
+
+ if (type == EVENT_OP) {
+ type = process_op(event, arg, &token);
+ list = &arg->next;
+ continue;
+ }
+
+ if (type == EVENT_DELIM && strcmp(token, ",") == 0) {
+ free_token(token);
+ *list = arg;
+ list = &arg->next;
+ continue;
+ }
+ break;
+ } while (type != EVENT_NONE);
+
+ if (type != EVENT_NONE)
+ free_token(token);
+
+ return args;
+}
+
+static int event_read_print(struct event *event)
+{
+ enum event_type type;
+ char *token;
+ int ret;
+
+ if (read_expected_item(EVENT_ITEM, "print") < 0)
+ return -1;
+
+ if (read_expected(EVENT_ITEM, "fmt") < 0)
+ return -1;
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ return -1;
+
+ if (read_expect_type(EVENT_DQUOTE, &token) < 0)
+ goto fail;
+
+ concat:
+ event->print_fmt.format = token;
+ event->print_fmt.args = NULL;
+
+ /* ok to have no arg */
+ type = read_token_item(&token);
+
+ if (type == EVENT_NONE)
+ return 0;
+
+ /* Handle concatination of print lines */
+ if (type == EVENT_DQUOTE) {
+ char *cat;
+
+ cat = malloc_or_die(strlen(event->print_fmt.format) +
+ strlen(token) + 1);
+ strcpy(cat, event->print_fmt.format);
+ strcat(cat, token);
+ free_token(token);
+ free_token(event->print_fmt.format);
+ event->print_fmt.format = NULL;
+ token = cat;
+ goto concat;
+ }
+
+ if (test_type_token(type, token, EVENT_DELIM, ","))
+ goto fail;
+
+ free_token(token);
+
+ ret = event_read_print_args(event, &event->print_fmt.args);
+ if (ret < 0)
+ return -1;
+
+ return ret;
+
+ fail:
+ free_token(token);
+ return -1;
+}
+
+static struct format_field *
+find_common_field(struct event *event, const char *name)
+{
+ struct format_field *format;
+
+ for (format = event->format.common_fields;
+ format; format = format->next) {
+ if (strcmp(format->name, name) == 0)
+ break;
+ }
+
+ return format;
+}
+
+static struct format_field *
+find_field(struct event *event, const char *name)
+{
+ struct format_field *format;
+
+ for (format = event->format.fields;
+ format; format = format->next) {
+ if (strcmp(format->name, name) == 0)
+ break;
+ }
+
+ return format;
+}
+
+static struct format_field *
+find_any_field(struct event *event, const char *name)
+{
+ struct format_field *format;
+
+ format = find_common_field(event, name);
+ if (format)
+ return format;
+ return find_field(event, name);
+}
+
+unsigned long long read_size(void *ptr, int size)
+{
+ switch (size) {
+ case 1:
+ return *(unsigned char *)ptr;
+ case 2:
+ return data2host2(ptr);
+ case 4:
+ return data2host4(ptr);
+ case 8:
+ return data2host8(ptr);
+ default:
+ /* BUG! */
+ return 0;
+ }
+}
+
+unsigned long long
+raw_field_value(struct event *event, const char *name, void *data)
+{
+ struct format_field *field;
+
+ field = find_any_field(event, name);
+ if (!field)
+ return 0ULL;
+
+ return read_size(data + field->offset, field->size);
+}
+
+void *raw_field_ptr(struct event *event, const char *name, void *data)
+{
+ struct format_field *field;
+
+ field = find_any_field(event, name);
+ if (!field)
+ return NULL;
+
+ if (field->flags & FIELD_IS_DYNAMIC) {
+ int offset;
+
+ offset = *(int *)(data + field->offset);
+ offset &= 0xffff;
+
+ return data + offset;
+ }
+
+ return data + field->offset;
+}
+
+static int get_common_info(const char *type, int *offset, int *size)
+{
+ struct event *event;
+ struct format_field *field;
+
+ /*
+ * All events should have the same common elements.
+ * Pick any event to find where the type is;
+ */
+ if (!event_list)
+ die("no event_list!");
+
+ event = event_list;
+ field = find_common_field(event, type);
+ if (!field)
+ die("field '%s' not found", type);
+
+ *offset = field->offset;
+ *size = field->size;
+
+ return 0;
+}
+
+static int __parse_common(void *data, int *size, int *offset,
+ const char *name)
+{
+ int ret;
+
+ if (!*size) {
+ ret = get_common_info(name, offset, size);
+ if (ret < 0)
+ return ret;
+ }
+ return read_size(data + *offset, *size);
+}
+
+int trace_parse_common_type(void *data)
+{
+ static int type_offset;
+ static int type_size;
+
+ return __parse_common(data, &type_size, &type_offset,
+ "common_type");
+}
+
+int trace_parse_common_pid(void *data)
+{
+ static int pid_offset;
+ static int pid_size;
+
+ return __parse_common(data, &pid_size, &pid_offset,
+ "common_pid");
+}
+
+int parse_common_pc(void *data)
+{
+ static int pc_offset;
+ static int pc_size;
+
+ return __parse_common(data, &pc_size, &pc_offset,
+ "common_preempt_count");
+}
+
+int parse_common_flags(void *data)
+{
+ static int flags_offset;
+ static int flags_size;
+
+ return __parse_common(data, &flags_size, &flags_offset,
+ "common_flags");
+}
+
+int parse_common_lock_depth(void *data)
+{
+ static int ld_offset;
+ static int ld_size;
+ int ret;
+
+ ret = __parse_common(data, &ld_size, &ld_offset,
+ "common_lock_depth");
+ if (ret < 0)
+ return -1;
+
+ return ret;
+}
+
+struct event *trace_find_event(int id)
+{
+ struct event *event;
+
+ for (event = event_list; event; event = event->next) {
+ if (event->id == id)
+ break;
+ }
+ return event;
+}
+
+struct event *trace_find_next_event(struct event *event)
+{
+ if (!event)
+ return event_list;
+
+ return event->next;
+}
+
+static unsigned long long eval_num_arg(void *data, int size,
+ struct event *event, struct print_arg *arg)
+{
+ unsigned long long val = 0;
+ unsigned long long left, right;
+ struct print_arg *larg;
+
+ switch (arg->type) {
+ case PRINT_NULL:
+ /* ?? */
+ return 0;
+ case PRINT_ATOM:
+ return strtoull(arg->atom.atom, NULL, 0);
+ case PRINT_FIELD:
+ if (!arg->field.field) {
+ arg->field.field = find_any_field(event, arg->field.name);
+ if (!arg->field.field)
+ die("field %s not found", arg->field.name);
+ }
+ /* must be a number */
+ val = read_size(data + arg->field.field->offset,
+ arg->field.field->size);
+ break;
+ case PRINT_FLAGS:
+ case PRINT_SYMBOL:
+ break;
+ case PRINT_TYPE:
+ return eval_num_arg(data, size, event, arg->typecast.item);
+ case PRINT_STRING:
+ return 0;
+ break;
+ case PRINT_OP:
+ if (strcmp(arg->op.op, "[") == 0) {
+ /*
+ * Arrays are special, since we don't want
+ * to read the arg as is.
+ */
+ if (arg->op.left->type != PRINT_FIELD)
+ goto default_op; /* oops, all bets off */
+ larg = arg->op.left;
+ if (!larg->field.field) {
+ larg->field.field =
+ find_any_field(event, larg->field.name);
+ if (!larg->field.field)
+ die("field %s not found", larg->field.name);
+ }
+ right = eval_num_arg(data, size, event, arg->op.right);
+ val = read_size(data + larg->field.field->offset +
+ right * long_size, long_size);
+ break;
+ }
+ default_op:
+ left = eval_num_arg(data, size, event, arg->op.left);
+ right = eval_num_arg(data, size, event, arg->op.right);
+ switch (arg->op.op[0]) {
+ case '|':
+ if (arg->op.op[1])
+ val = left || right;
+ else
+ val = left | right;
+ break;
+ case '&':
+ if (arg->op.op[1])
+ val = left && right;
+ else
+ val = left & right;
+ break;
+ case '<':
+ switch (arg->op.op[1]) {
+ case 0:
+ val = left < right;
+ break;
+ case '<':
+ val = left << right;
+ break;
+ case '=':
+ val = left <= right;
+ break;
+ default:
+ die("unknown op '%s'", arg->op.op);
+ }
+ break;
+ case '>':
+ switch (arg->op.op[1]) {
+ case 0:
+ val = left > right;
+ break;
+ case '>':
+ val = left >> right;
+ break;
+ case '=':
+ val = left >= right;
+ break;
+ default:
+ die("unknown op '%s'", arg->op.op);
+ }
+ break;
+ case '=':
+ if (arg->op.op[1] != '=')
+ die("unknown op '%s'", arg->op.op);
+ val = left == right;
+ break;
+ case '-':
+ val = left - right;
+ break;
+ case '+':
+ val = left + right;
+ break;
+ default:
+ die("unknown op '%s'", arg->op.op);
+ }
+ break;
+ default: /* not sure what to do there */
+ return 0;
+ }
+ return val;
+}
+
+struct flag {
+ const char *name;
+ unsigned long long value;
+};
+
+static const struct flag flags[] = {
+ { "HI_SOFTIRQ", 0 },
+ { "TIMER_SOFTIRQ", 1 },
+ { "NET_TX_SOFTIRQ", 2 },
+ { "NET_RX_SOFTIRQ", 3 },
+ { "BLOCK_SOFTIRQ", 4 },
+ { "BLOCK_IOPOLL_SOFTIRQ", 5 },
+ { "TASKLET_SOFTIRQ", 6 },
+ { "SCHED_SOFTIRQ", 7 },
+ { "HRTIMER_SOFTIRQ", 8 },
+ { "RCU_SOFTIRQ", 9 },
+
+ { "HRTIMER_NORESTART", 0 },
+ { "HRTIMER_RESTART", 1 },
+};
+
+unsigned long long eval_flag(const char *flag)
+{
+ int i;
+
+ /*
+ * Some flags in the format files do not get converted.
+ * If the flag is not numeric, see if it is something that
+ * we already know about.
+ */
+ if (isdigit(flag[0]))
+ return strtoull(flag, NULL, 0);
+
+ for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++)
+ if (strcmp(flags[i].name, flag) == 0)
+ return flags[i].value;
+
+ return 0;
+}
+
+static void print_str_arg(void *data, int size,
+ struct event *event, struct print_arg *arg)
+{
+ struct print_flag_sym *flag;
+ unsigned long long val, fval;
+ char *str;
+ int print;
+
+ switch (arg->type) {
+ case PRINT_NULL:
+ /* ?? */
+ return;
+ case PRINT_ATOM:
+ printf("%s", arg->atom.atom);
+ return;
+ case PRINT_FIELD:
+ if (!arg->field.field) {
+ arg->field.field = find_any_field(event, arg->field.name);
+ if (!arg->field.field)
+ die("field %s not found", arg->field.name);
+ }
+ str = malloc_or_die(arg->field.field->size + 1);
+ memcpy(str, data + arg->field.field->offset,
+ arg->field.field->size);
+ str[arg->field.field->size] = 0;
+ printf("%s", str);
+ free(str);
+ break;
+ case PRINT_FLAGS:
+ val = eval_num_arg(data, size, event, arg->flags.field);
+ print = 0;
+ for (flag = arg->flags.flags; flag; flag = flag->next) {
+ fval = eval_flag(flag->value);
+ if (!val && !fval) {
+ printf("%s", flag->str);
+ break;
+ }
+ if (fval && (val & fval) == fval) {
+ if (print && arg->flags.delim)
+ printf("%s", arg->flags.delim);
+ printf("%s", flag->str);
+ print = 1;
+ val &= ~fval;
+ }
+ }
+ break;
+ case PRINT_SYMBOL:
+ val = eval_num_arg(data, size, event, arg->symbol.field);
+ for (flag = arg->symbol.symbols; flag; flag = flag->next) {
+ fval = eval_flag(flag->value);
+ if (val == fval) {
+ printf("%s", flag->str);
+ break;
+ }
+ }
+ break;
+
+ case PRINT_TYPE:
+ break;
+ case PRINT_STRING: {
+ int str_offset;
+
+ if (arg->string.offset == -1) {
+ struct format_field *f;
+
+ f = find_any_field(event, arg->string.string);
+ arg->string.offset = f->offset;
+ }
+ str_offset = *(int *)(data + arg->string.offset);
+ str_offset &= 0xffff;
+ printf("%s", ((char *)data) + str_offset);
+ break;
+ }
+ case PRINT_OP:
+ /*
+ * The only op for string should be ? :
+ */
+ if (arg->op.op[0] != '?')
+ return;
+ val = eval_num_arg(data, size, event, arg->op.left);
+ if (val)
+ print_str_arg(data, size, event, arg->op.right->op.left);
+ else
+ print_str_arg(data, size, event, arg->op.right->op.right);
+ break;
+ default:
+ /* well... */
+ break;
+ }
+}
+
+static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event *event)
+{
+ static struct format_field *field, *ip_field;
+ struct print_arg *args, *arg, **next;
+ unsigned long long ip, val;
+ char *ptr;
+ void *bptr;
+
+ if (!field) {
+ field = find_field(event, "buf");
+ if (!field)
+ die("can't find buffer field for binary printk");
+ ip_field = find_field(event, "ip");
+ if (!ip_field)
+ die("can't find ip field for binary printk");
+ }
+
+ ip = read_size(data + ip_field->offset, ip_field->size);
+
+ /*
+ * The first arg is the IP pointer.
+ */
+ args = malloc_or_die(sizeof(*args));
+ arg = args;
+ arg->next = NULL;
+ next = &arg->next;
+
+ arg->type = PRINT_ATOM;
+ arg->atom.atom = malloc_or_die(32);
+ sprintf(arg->atom.atom, "%lld", ip);
+
+ /* skip the first "%pf : " */
+ for (ptr = fmt + 6, bptr = data + field->offset;
+ bptr < data + size && *ptr; ptr++) {
+ int ls = 0;
+
+ if (*ptr == '%') {
+ process_again:
+ ptr++;
+ switch (*ptr) {
+ case '%':
+ break;
+ case 'l':
+ ls++;
+ goto process_again;
+ case 'L':
+ ls = 2;
+ goto process_again;
+ case '0' ... '9':
+ goto process_again;
+ case 'p':
+ ls = 1;
+ /* fall through */
+ case 'd':
+ case 'u':
+ case 'x':
+ case 'i':
+ /* the pointers are always 4 bytes aligned */
+ bptr = (void *)(((unsigned long)bptr + 3) &
+ ~3);
+ switch (ls) {
+ case 0:
+ case 1:
+ ls = long_size;
+ break;
+ case 2:
+ ls = 8;
+ default:
+ break;
+ }
+ val = read_size(bptr, ls);
+ bptr += ls;
+ arg = malloc_or_die(sizeof(*arg));
+ arg->next = NULL;
+ arg->type = PRINT_ATOM;
+ arg->atom.atom = malloc_or_die(32);
+ sprintf(arg->atom.atom, "%lld", val);
+ *next = arg;
+ next = &arg->next;
+ break;
+ case 's':
+ arg = malloc_or_die(sizeof(*arg));
+ arg->next = NULL;
+ arg->type = PRINT_STRING;
+ arg->string.string = strdup(bptr);
+ bptr += strlen(bptr) + 1;
+ *next = arg;
+ next = &arg->next;
+ default:
+ break;
+ }
+ }
+ }
+
+ return args;
+}
+
+static void free_args(struct print_arg *args)
+{
+ struct print_arg *next;
+
+ while (args) {
+ next = args->next;
+
+ if (args->type == PRINT_ATOM)
+ free(args->atom.atom);
+ else
+ free(args->string.string);
+ free(args);
+ args = next;
+ }
+}
+
+static char *get_bprint_format(void *data, int size __unused, struct event *event)
+{
+ unsigned long long addr;
+ static struct format_field *field;
+ struct printk_map *printk;
+ char *format;
+ char *p;
+
+ if (!field) {
+ field = find_field(event, "fmt");
+ if (!field)
+ die("can't find format field for binary printk");
+ printf("field->offset = %d size=%d\n", field->offset, field->size);
+ }
+
+ addr = read_size(data + field->offset, field->size);
+
+ printk = find_printk(addr);
+ if (!printk) {
+ format = malloc_or_die(45);
+ sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n",
+ addr);
+ return format;
+ }
+
+ p = printk->printk;
+ /* Remove any quotes. */
+ if (*p == '"')
+ p++;
+ format = malloc_or_die(strlen(p) + 10);
+ sprintf(format, "%s : %s", "%pf", p);
+ /* remove ending quotes and new line since we will add one too */
+ p = format + strlen(format) - 1;
+ if (*p == '"')
+ *p = 0;
+
+ p -= 2;
+ if (strcmp(p, "\\n") == 0)
+ *p = 0;
+
+ return format;
+}
+
+static void pretty_print(void *data, int size, struct event *event)
+{
+ struct print_fmt *print_fmt = &event->print_fmt;
+ struct print_arg *arg = print_fmt->args;
+ struct print_arg *args = NULL;
+ const char *ptr = print_fmt->format;
+ unsigned long long val;
+ struct func_map *func;
+ const char *saveptr;
+ char *bprint_fmt = NULL;
+ char format[32];
+ int show_func;
+ int len;
+ int ls;
+
+ if (event->flags & EVENT_FL_ISFUNC)
+ ptr = " %pF <-- %pF";
+
+ if (event->flags & EVENT_FL_ISBPRINT) {
+ bprint_fmt = get_bprint_format(data, size, event);
+ args = make_bprint_args(bprint_fmt, data, size, event);
+ arg = args;
+ ptr = bprint_fmt;
+ }
+
+ for (; *ptr; ptr++) {
+ ls = 0;
+ if (*ptr == '\\') {
+ ptr++;
+ switch (*ptr) {
+ case 'n':
+ printf("\n");
+ break;
+ case 't':
+ printf("\t");
+ break;
+ case 'r':
+ printf("\r");
+ break;
+ case '\\':
+ printf("\\");
+ break;
+ default:
+ printf("%c", *ptr);
+ break;
+ }
+
+ } else if (*ptr == '%') {
+ saveptr = ptr;
+ show_func = 0;
+ cont_process:
+ ptr++;
+ switch (*ptr) {
+ case '%':
+ printf("%%");
+ break;
+ case 'l':
+ ls++;
+ goto cont_process;
+ case 'L':
+ ls = 2;
+ goto cont_process;
+ case 'z':
+ case 'Z':
+ case '0' ... '9':
+ goto cont_process;
+ case 'p':
+ if (long_size == 4)
+ ls = 1;
+ else
+ ls = 2;
+
+ if (*(ptr+1) == 'F' ||
+ *(ptr+1) == 'f') {
+ ptr++;
+ show_func = *ptr;
+ }
+
+ /* fall through */
+ case 'd':
+ case 'i':
+ case 'x':
+ case 'X':
+ case 'u':
+ if (!arg)
+ die("no argument match");
+
+ len = ((unsigned long)ptr + 1) -
+ (unsigned long)saveptr;
+
+ /* should never happen */
+ if (len > 32)
+ die("bad format!");
+
+ memcpy(format, saveptr, len);
+ format[len] = 0;
+
+ val = eval_num_arg(data, size, event, arg);
+ arg = arg->next;
+
+ if (show_func) {
+ func = find_func(val);
+ if (func) {
+ printf("%s", func->func);
+ if (show_func == 'F')
+ printf("+0x%llx",
+ val - func->addr);
+ break;
+ }
+ }
+ switch (ls) {
+ case 0:
+ printf(format, (int)val);
+ break;
+ case 1:
+ printf(format, (long)val);
+ break;
+ case 2:
+ printf(format, (long long)val);
+ break;
+ default:
+ die("bad count (%d)", ls);
+ }
+ break;
+ case 's':
+ if (!arg)
+ die("no matching argument");
+
+ print_str_arg(data, size, event, arg);
+ arg = arg->next;
+ break;
+ default:
+ printf(">%c<", *ptr);
+
+ }
+ } else
+ printf("%c", *ptr);
+ }
+
+ if (args) {
+ free_args(args);
+ free(bprint_fmt);
+ }
+}
+
+static inline int log10_cpu(int nb)
+{
+ if (nb / 100)
+ return 3;
+ if (nb / 10)
+ return 2;
+ return 1;
+}
+
+static void print_lat_fmt(void *data, int size __unused)
+{
+ unsigned int lat_flags;
+ unsigned int pc;
+ int lock_depth;
+ int hardirq;
+ int softirq;
+
+ lat_flags = parse_common_flags(data);
+ pc = parse_common_pc(data);
+ lock_depth = parse_common_lock_depth(data);
+
+ hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
+ softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
+
+ printf("%c%c%c",
+ (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' :
+ (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ?
+ 'X' : '.',
+ (lat_flags & TRACE_FLAG_NEED_RESCHED) ?
+ 'N' : '.',
+ (hardirq && softirq) ? 'H' :
+ hardirq ? 'h' : softirq ? 's' : '.');
+
+ if (pc)
+ printf("%x", pc);
+ else
+ printf(".");
+
+ if (lock_depth < 0)
+ printf(".");
+ else
+ printf("%d", lock_depth);
+}
+
+/* taken from Linux, written by Frederic Weisbecker */
+static void print_graph_cpu(int cpu)
+{
+ int i;
+ int log10_this = log10_cpu(cpu);
+ int log10_all = log10_cpu(cpus);
+
+
+ /*
+ * Start with a space character - to make it stand out
+ * to the right a bit when trace output is pasted into
+ * email:
+ */
+ printf(" ");
+
+ /*
+ * Tricky - we space the CPU field according to the max
+ * number of online CPUs. On a 2-cpu system it would take
+ * a maximum of 1 digit - on a 128 cpu system it would
+ * take up to 3 digits:
+ */
+ for (i = 0; i < log10_all - log10_this; i++)
+ printf(" ");
+
+ printf("%d) ", cpu);
+}
+
+#define TRACE_GRAPH_PROCINFO_LENGTH 14
+#define TRACE_GRAPH_INDENT 2
+
+static void print_graph_proc(int pid, const char *comm)
+{
+ /* sign + log10(MAX_INT) + '\0' */
+ char pid_str[11];
+ int spaces = 0;
+ int len;
+ int i;
+
+ sprintf(pid_str, "%d", pid);
+
+ /* 1 stands for the "-" character */
+ len = strlen(comm) + strlen(pid_str) + 1;
+
+ if (len < TRACE_GRAPH_PROCINFO_LENGTH)
+ spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
+
+ /* First spaces to align center */
+ for (i = 0; i < spaces / 2; i++)
+ printf(" ");
+
+ printf("%s-%s", comm, pid_str);
+
+ /* Last spaces to align center */
+ for (i = 0; i < spaces - (spaces / 2); i++)
+ printf(" ");
+}
+
+static struct record *
+get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func,
+ struct record *next)
+{
+ struct format_field *field;
+ struct event *event;
+ unsigned long val;
+ int type;
+ int pid;
+
+ type = trace_parse_common_type(next->data);
+ event = trace_find_event(type);
+ if (!event)
+ return NULL;
+
+ if (!(event->flags & EVENT_FL_ISFUNCRET))
+ return NULL;
+
+ pid = trace_parse_common_pid(next->data);
+ field = find_field(event, "func");
+ if (!field)
+ die("function return does not have field func");
+
+ val = read_size(next->data + field->offset, field->size);
+
+ if (cur_pid != pid || cur_func != val)
+ return NULL;
+
+ /* this is a leaf, now advance the iterator */
+ return trace_read_data(cpu);
+}
+
+/* Signal a overhead of time execution to the output */
+static void print_graph_overhead(unsigned long long duration)
+{
+ /* Non nested entry or return */
+ if (duration == ~0ULL)
+ return (void)printf(" ");
+
+ /* Duration exceeded 100 msecs */
+ if (duration > 100000ULL)
+ return (void)printf("! ");
+
+ /* Duration exceeded 10 msecs */
+ if (duration > 10000ULL)
+ return (void)printf("+ ");
+
+ printf(" ");
+}
+
+static void print_graph_duration(unsigned long long duration)
+{
+ unsigned long usecs = duration / 1000;
+ unsigned long nsecs_rem = duration % 1000;
+ /* log10(ULONG_MAX) + '\0' */
+ char msecs_str[21];
+ char nsecs_str[5];
+ int len;
+ int i;
+
+ sprintf(msecs_str, "%lu", usecs);
+
+ /* Print msecs */
+ len = printf("%lu", usecs);
+
+ /* Print nsecs (we don't want to exceed 7 numbers) */
+ if (len < 7) {
+ snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem);
+ len += printf(".%s", nsecs_str);
+ }
+
+ printf(" us ");
+
+ /* Print remaining spaces to fit the row's width */
+ for (i = len; i < 7; i++)
+ printf(" ");
+
+ printf("| ");
+}
+
+static void
+print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec)
+{
+ unsigned long long rettime, calltime;
+ unsigned long long duration, depth;
+ unsigned long long val;
+ struct format_field *field;
+ struct func_map *func;
+ struct event *ret_event;
+ int type;
+ int i;
+
+ type = trace_parse_common_type(ret_rec->data);
+ ret_event = trace_find_event(type);
+
+ field = find_field(ret_event, "rettime");
+ if (!field)
+ die("can't find rettime in return graph");
+ rettime = read_size(ret_rec->data + field->offset, field->size);
+
+ field = find_field(ret_event, "calltime");
+ if (!field)
+ die("can't find rettime in return graph");
+ calltime = read_size(ret_rec->data + field->offset, field->size);
+
+ duration = rettime - calltime;
+
+ /* Overhead */
+ print_graph_overhead(duration);
+
+ /* Duration */
+ print_graph_duration(duration);
+
+ field = find_field(event, "depth");
+ if (!field)
+ die("can't find depth in entry graph");
+ depth = read_size(data + field->offset, field->size);
+
+ /* Function */
+ for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
+ printf(" ");
+
+ field = find_field(event, "func");
+ if (!field)
+ die("can't find func in entry graph");
+ val = read_size(data + field->offset, field->size);
+ func = find_func(val);
+
+ if (func)
+ printf("%s();", func->func);
+ else
+ printf("%llx();", val);
+}
+
+static void print_graph_nested(struct event *event, void *data)
+{
+ struct format_field *field;
+ unsigned long long depth;
+ unsigned long long val;
+ struct func_map *func;
+ int i;
+
+ /* No overhead */
+ print_graph_overhead(-1);
+
+ /* No time */
+ printf(" | ");
+
+ field = find_field(event, "depth");
+ if (!field)
+ die("can't find depth in entry graph");
+ depth = read_size(data + field->offset, field->size);
+
+ /* Function */
+ for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
+ printf(" ");
+
+ field = find_field(event, "func");
+ if (!field)
+ die("can't find func in entry graph");
+ val = read_size(data + field->offset, field->size);
+ func = find_func(val);
+
+ if (func)
+ printf("%s() {", func->func);
+ else
+ printf("%llx() {", val);
+}
+
+static void
+pretty_print_func_ent(void *data, int size, struct event *event,
+ int cpu, int pid, const char *comm,
+ unsigned long secs, unsigned long usecs)
+{
+ struct format_field *field;
+ struct record *rec;
+ void *copy_data;
+ unsigned long val;
+
+ printf("%5lu.%06lu | ", secs, usecs);
+
+ print_graph_cpu(cpu);
+ print_graph_proc(pid, comm);
+
+ printf(" | ");
+
+ if (latency_format) {
+ print_lat_fmt(data, size);
+ printf(" | ");
+ }
+
+ field = find_field(event, "func");
+ if (!field)
+ die("function entry does not have func field");
+
+ val = read_size(data + field->offset, field->size);
+
+ /*
+ * peek_data may unmap the data pointer. Copy it first.
+ */
+ copy_data = malloc_or_die(size);
+ memcpy(copy_data, data, size);
+ data = copy_data;
+
+ rec = trace_peek_data(cpu);
+ if (rec) {
+ rec = get_return_for_leaf(cpu, pid, val, rec);
+ if (rec) {
+ print_graph_entry_leaf(event, data, rec);
+ goto out_free;
+ }
+ }
+ print_graph_nested(event, data);
+out_free:
+ free(data);
+}
+
+static void
+pretty_print_func_ret(void *data, int size __unused, struct event *event,
+ int cpu, int pid, const char *comm,
+ unsigned long secs, unsigned long usecs)
+{
+ unsigned long long rettime, calltime;
+ unsigned long long duration, depth;
+ struct format_field *field;
+ int i;
+
+ printf("%5lu.%06lu | ", secs, usecs);
+
+ print_graph_cpu(cpu);
+ print_graph_proc(pid, comm);
+
+ printf(" | ");
+
+ if (latency_format) {
+ print_lat_fmt(data, size);
+ printf(" | ");
+ }
+
+ field = find_field(event, "rettime");
+ if (!field)
+ die("can't find rettime in return graph");
+ rettime = read_size(data + field->offset, field->size);
+
+ field = find_field(event, "calltime");
+ if (!field)
+ die("can't find calltime in return graph");
+ calltime = read_size(data + field->offset, field->size);
+
+ duration = rettime - calltime;
+
+ /* Overhead */
+ print_graph_overhead(duration);
+
+ /* Duration */
+ print_graph_duration(duration);
+
+ field = find_field(event, "depth");
+ if (!field)
+ die("can't find depth in entry graph");
+ depth = read_size(data + field->offset, field->size);
+
+ /* Function */
+ for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
+ printf(" ");
+
+ printf("}");
+}
+
+static void
+pretty_print_func_graph(void *data, int size, struct event *event,
+ int cpu, int pid, const char *comm,
+ unsigned long secs, unsigned long usecs)
+{
+ if (event->flags & EVENT_FL_ISFUNCENT)
+ pretty_print_func_ent(data, size, event,
+ cpu, pid, comm, secs, usecs);
+ else if (event->flags & EVENT_FL_ISFUNCRET)
+ pretty_print_func_ret(data, size, event,
+ cpu, pid, comm, secs, usecs);
+ printf("\n");
+}
+
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm)
+{
+ struct event *event;
+ unsigned long secs;
+ unsigned long usecs;
+ int type;
+ int pid;
+
+ secs = nsecs / NSECS_PER_SEC;
+ nsecs -= secs * NSECS_PER_SEC;
+ usecs = nsecs / NSECS_PER_USEC;
+
+ type = trace_parse_common_type(data);
+
+ event = trace_find_event(type);
+ if (!event) {
+ warning("ug! no event found for type %d", type);
+ return;
+ }
+
+ pid = trace_parse_common_pid(data);
+
+ if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET))
+ return pretty_print_func_graph(data, size, event, cpu,
+ pid, comm, secs, usecs);
+
+ if (latency_format) {
+ printf("%8.8s-%-5d %3d",
+ comm, pid, cpu);
+ print_lat_fmt(data, size);
+ } else
+ printf("%16s-%-5d [%03d]", comm, pid, cpu);
+
+ printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
+
+ if (event->flags & EVENT_FL_FAILED) {
+ printf("EVENT '%s' FAILED TO PARSE\n",
+ event->name);
+ return;
+ }
+
+ pretty_print(data, size, event);
+ printf("\n");
+}
+
+static void print_fields(struct print_flag_sym *field)
+{
+ printf("{ %s, %s }", field->value, field->str);
+ if (field->next) {
+ printf(", ");
+ print_fields(field->next);
+ }
+}
+
+static void print_args(struct print_arg *args)
+{
+ int print_paren = 1;
+
+ switch (args->type) {
+ case PRINT_NULL:
+ printf("null");
+ break;
+ case PRINT_ATOM:
+ printf("%s", args->atom.atom);
+ break;
+ case PRINT_FIELD:
+ printf("REC->%s", args->field.name);
+ break;
+ case PRINT_FLAGS:
+ printf("__print_flags(");
+ print_args(args->flags.field);
+ printf(", %s, ", args->flags.delim);
+ print_fields(args->flags.flags);
+ printf(")");
+ break;
+ case PRINT_SYMBOL:
+ printf("__print_symbolic(");
+ print_args(args->symbol.field);
+ printf(", ");
+ print_fields(args->symbol.symbols);
+ printf(")");
+ break;
+ case PRINT_STRING:
+ printf("__get_str(%s)", args->string.string);
+ break;
+ case PRINT_TYPE:
+ printf("(%s)", args->typecast.type);
+ print_args(args->typecast.item);
+ break;
+ case PRINT_OP:
+ if (strcmp(args->op.op, ":") == 0)
+ print_paren = 0;
+ if (print_paren)
+ printf("(");
+ print_args(args->op.left);
+ printf(" %s ", args->op.op);
+ print_args(args->op.right);
+ if (print_paren)
+ printf(")");
+ break;
+ default:
+ /* we should warn... */
+ return;
+ }
+ if (args->next) {
+ printf("\n");
+ print_args(args->next);
+ }
+}
+
+int parse_ftrace_file(char *buf, unsigned long size)
+{
+ struct format_field *field;
+ struct print_arg *arg, **list;
+ struct event *event;
+ int ret;
+
+ init_input_buf(buf, size);
+
+ event = alloc_event();
+ if (!event)
+ return -ENOMEM;
+
+ event->flags |= EVENT_FL_ISFTRACE;
+
+ event->name = event_read_name();
+ if (!event->name)
+ die("failed to read ftrace event name");
+
+ if (strcmp(event->name, "function") == 0)
+ event->flags |= EVENT_FL_ISFUNC;
+
+ else if (strcmp(event->name, "funcgraph_entry") == 0)
+ event->flags |= EVENT_FL_ISFUNCENT;
+
+ else if (strcmp(event->name, "funcgraph_exit") == 0)
+ event->flags |= EVENT_FL_ISFUNCRET;
+
+ else if (strcmp(event->name, "bprint") == 0)
+ event->flags |= EVENT_FL_ISBPRINT;
+
+ event->id = event_read_id();
+ if (event->id < 0)
+ die("failed to read ftrace event id");
+
+ add_event(event);
+
+ ret = event_read_format(event);
+ if (ret < 0)
+ die("failed to read ftrace event format");
+
+ ret = event_read_print(event);
+ if (ret < 0)
+ die("failed to read ftrace event print fmt");
+
+ /* New ftrace handles args */
+ if (ret > 0)
+ return 0;
+ /*
+ * The arguments for ftrace files are parsed by the fields.
+ * Set up the fields as their arguments.
+ */
+ list = &event->print_fmt.args;
+ for (field = event->format.fields; field; field = field->next) {
+ arg = malloc_or_die(sizeof(*arg));
+ memset(arg, 0, sizeof(*arg));
+ *list = arg;
+ list = &arg->next;
+ arg->type = PRINT_FIELD;
+ arg->field.name = field->name;
+ arg->field.field = field;
+ }
+ return 0;
+}
+
+int parse_event_file(char *buf, unsigned long size, char *sys)
+{
+ struct event *event;
+ int ret;
+
+ init_input_buf(buf, size);
+
+ event = alloc_event();
+ if (!event)
+ return -ENOMEM;
+
+ event->name = event_read_name();
+ if (!event->name)
+ die("failed to read event name");
+
+ event->id = event_read_id();
+ if (event->id < 0)
+ die("failed to read event id");
+
+ ret = event_read_format(event);
+ if (ret < 0) {
+ warning("failed to read event format for %s", event->name);
+ goto event_failed;
+ }
+
+ ret = event_read_print(event);
+ if (ret < 0) {
+ warning("failed to read event print fmt for %s", event->name);
+ goto event_failed;
+ }
+
+ event->system = strdup(sys);
+
+#define PRINT_ARGS 0
+ if (PRINT_ARGS && event->print_fmt.args)
+ print_args(event->print_fmt.args);
+
+ add_event(event);
+ return 0;
+
+ event_failed:
+ event->flags |= EVENT_FL_FAILED;
+ /* still add it even if it failed */
+ add_event(event);
+ return -1;
+}
+
+void parse_set_info(int nr_cpus, int long_sz)
+{
+ cpus = nr_cpus;
+ long_size = long_sz;
+}
+
+int common_pc(struct scripting_context *context)
+{
+ return parse_common_pc(context->event_data);
+}
+
+int common_flags(struct scripting_context *context)
+{
+ return parse_common_flags(context->event_data);
+}
+
+int common_lock_depth(struct scripting_context *context)
+{
+ return parse_common_lock_depth(context->event_data);
+}
diff --git a/smartt-perf/util/trace-event-read.c b/smartt-perf/util/trace-event-read.c
new file mode 100644
index 0000000..f55cc3a
--- /dev/null
+++ b/smartt-perf/util/trace-event-read.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define _FILE_OFFSET_BITS 64
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "../perf.h"
+#include "util.h"
+#include "trace-event.h"
+
+static int input_fd;
+
+static int read_page;
+
+int file_bigendian;
+int host_bigendian;
+static int long_size;
+
+static unsigned long page_size;
+
+static ssize_t calc_data_size;
+static bool repipe;
+
+static int do_read(int fd, void *buf, int size)
+{
+ int rsize = size;
+
+ while (size) {
+ int ret = read(fd, buf, size);
+
+ if (ret <= 0)
+ return -1;
+
+ if (repipe) {
+ int retw = write(STDOUT_FILENO, buf, ret);
+
+ if (retw <= 0 || retw != ret)
+ die("repiping input file");
+ }
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return rsize;
+}
+
+static int read_or_die(void *data, int size)
+{
+ int r;
+
+ r = do_read(input_fd, data, size);
+ if (r <= 0)
+ die("reading input file (size expected=%d received=%d)",
+ size, r);
+
+ if (calc_data_size)
+ calc_data_size += r;
+
+ return r;
+}
+
+/* If it fails, the next read will report it */
+static void skip(int size)
+{
+ char buf[BUFSIZ];
+ int r;
+
+ while (size) {
+ r = size > BUFSIZ ? BUFSIZ : size;
+ read_or_die(buf, r);
+ size -= r;
+ };
+}
+
+static unsigned int read4(void)
+{
+ unsigned int data;
+
+ read_or_die(&data, 4);
+ return __data2host4(data);
+}
+
+static unsigned long long read8(void)
+{
+ unsigned long long data;
+
+ read_or_die(&data, 8);
+ return __data2host8(data);
+}
+
+static char *read_string(void)
+{
+ char buf[BUFSIZ];
+ char *str = NULL;
+ int size = 0;
+ off_t r;
+ char c;
+
+ for (;;) {
+ r = read(input_fd, &c, 1);
+ if (r < 0)
+ die("reading input file");
+
+ if (!r)
+ die("no data");
+
+ if (repipe) {
+ int retw = write(STDOUT_FILENO, &c, 1);
+
+ if (retw <= 0 || retw != r)
+ die("repiping input file string");
+ }
+
+ buf[size++] = c;
+
+ if (!c)
+ break;
+ }
+
+ if (calc_data_size)
+ calc_data_size += size;
+
+ str = malloc_or_die(size);
+ memcpy(str, buf, size);
+
+ return str;
+}
+
+static void read_proc_kallsyms(void)
+{
+ unsigned int size;
+ char *buf;
+
+ size = read4();
+ if (!size)
+ return;
+
+ buf = malloc_or_die(size + 1);
+ read_or_die(buf, size);
+ buf[size] = '\0';
+
+ parse_proc_kallsyms(buf, size);
+
+ free(buf);
+}
+
+static void read_ftrace_printk(void)
+{
+ unsigned int size;
+ char *buf;
+
+ size = read4();
+ if (!size)
+ return;
+
+ buf = malloc_or_die(size);
+ read_or_die(buf, size);
+
+ parse_ftrace_printk(buf, size);
+
+ free(buf);
+}
+
+static void read_header_files(void)
+{
+ unsigned long long size;
+ char *header_event;
+ char buf[BUFSIZ];
+
+ read_or_die(buf, 12);
+
+ if (memcmp(buf, "header_page", 12) != 0)
+ die("did not read header page");
+
+ size = read8();
+ skip(size);
+
+ /*
+ * The size field in the page is of type long,
+ * use that instead, since it represents the kernel.
+ */
+ long_size = header_page_size_size;
+
+ read_or_die(buf, 13);
+ if (memcmp(buf, "header_event", 13) != 0)
+ die("did not read header event");
+
+ size = read8();
+ header_event = malloc_or_die(size);
+ read_or_die(header_event, size);
+ free(header_event);
+}
+
+static void read_ftrace_file(unsigned long long size)
+{
+ char *buf;
+
+ buf = malloc_or_die(size);
+ read_or_die(buf, size);
+ parse_ftrace_file(buf, size);
+ free(buf);
+}
+
+static void read_event_file(char *sys, unsigned long long size)
+{
+ char *buf;
+
+ buf = malloc_or_die(size);
+ read_or_die(buf, size);
+ parse_event_file(buf, size, sys);
+ free(buf);
+}
+
+static void read_ftrace_files(void)
+{
+ unsigned long long size;
+ int count;
+ int i;
+
+ count = read4();
+
+ for (i = 0; i < count; i++) {
+ size = read8();
+ read_ftrace_file(size);
+ }
+}
+
+static void read_event_files(void)
+{
+ unsigned long long size;
+ char *sys;
+ int systems;
+ int count;
+ int i,x;
+
+ systems = read4();
+
+ for (i = 0; i < systems; i++) {
+ sys = read_string();
+
+ count = read4();
+ for (x=0; x < count; x++) {
+ size = read8();
+ read_event_file(sys, size);
+ }
+ }
+}
+
+struct cpu_data {
+ unsigned long long offset;
+ unsigned long long size;
+ unsigned long long timestamp;
+ struct record *next;
+ char *page;
+ int cpu;
+ int index;
+ int page_size;
+};
+
+static struct cpu_data *cpu_data;
+
+static void update_cpu_data_index(int cpu)
+{
+ cpu_data[cpu].offset += page_size;
+ cpu_data[cpu].size -= page_size;
+ cpu_data[cpu].index = 0;
+}
+
+static void get_next_page(int cpu)
+{
+ off_t save_seek;
+ off_t ret;
+
+ if (!cpu_data[cpu].page)
+ return;
+
+ if (read_page) {
+ if (cpu_data[cpu].size <= page_size) {
+ free(cpu_data[cpu].page);
+ cpu_data[cpu].page = NULL;
+ return;
+ }
+
+ update_cpu_data_index(cpu);
+
+ /* other parts of the code may expect the pointer to not move */
+ save_seek = lseek(input_fd, 0, SEEK_CUR);
+
+ ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET);
+ if (ret == (off_t)-1)
+ die("failed to lseek");
+ ret = read(input_fd, cpu_data[cpu].page, page_size);
+ if (ret < 0)
+ die("failed to read page");
+
+ /* reset the file pointer back */
+ lseek(input_fd, save_seek, SEEK_SET);
+
+ return;
+ }
+
+ munmap(cpu_data[cpu].page, page_size);
+ cpu_data[cpu].page = NULL;
+
+ if (cpu_data[cpu].size <= page_size)
+ return;
+
+ update_cpu_data_index(cpu);
+
+ cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE,
+ input_fd, cpu_data[cpu].offset);
+ if (cpu_data[cpu].page == MAP_FAILED)
+ die("failed to mmap cpu %d at offset 0x%llx",
+ cpu, cpu_data[cpu].offset);
+}
+
+static unsigned int type_len4host(unsigned int type_len_ts)
+{
+ if (file_bigendian)
+ return (type_len_ts >> 27) & ((1 << 5) - 1);
+ else
+ return type_len_ts & ((1 << 5) - 1);
+}
+
+static unsigned int ts4host(unsigned int type_len_ts)
+{
+ if (file_bigendian)
+ return type_len_ts & ((1 << 27) - 1);
+ else
+ return type_len_ts >> 5;
+}
+
+static int calc_index(void *ptr, int cpu)
+{
+ return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page;
+}
+
+struct record *trace_peek_data(int cpu)
+{
+ struct record *data;
+ void *page = cpu_data[cpu].page;
+ int idx = cpu_data[cpu].index;
+ void *ptr = page + idx;
+ unsigned long long extend;
+ unsigned int type_len_ts;
+ unsigned int type_len;
+ unsigned int delta;
+ unsigned int length = 0;
+
+ if (cpu_data[cpu].next)
+ return cpu_data[cpu].next;
+
+ if (!page)
+ return NULL;
+
+ if (!idx) {
+ /* FIXME: handle header page */
+ if (header_page_ts_size != 8)
+ die("expected a long long type for timestamp");
+ cpu_data[cpu].timestamp = data2host8(ptr);
+ ptr += 8;
+ switch (header_page_size_size) {
+ case 4:
+ cpu_data[cpu].page_size = data2host4(ptr);
+ ptr += 4;
+ break;
+ case 8:
+ cpu_data[cpu].page_size = data2host8(ptr);
+ ptr += 8;
+ break;
+ default:
+ die("bad long size");
+ }
+ ptr = cpu_data[cpu].page + header_page_data_offset;
+ }
+
+read_again:
+ idx = calc_index(ptr, cpu);
+
+ if (idx >= cpu_data[cpu].page_size) {
+ get_next_page(cpu);
+ return trace_peek_data(cpu);
+ }
+
+ type_len_ts = data2host4(ptr);
+ ptr += 4;
+
+ type_len = type_len4host(type_len_ts);
+ delta = ts4host(type_len_ts);
+
+ switch (type_len) {
+ case RINGBUF_TYPE_PADDING:
+ if (!delta)
+ die("error, hit unexpected end of page");
+ length = data2host4(ptr);
+ ptr += 4;
+ length *= 4;
+ ptr += length;
+ goto read_again;
+
+ case RINGBUF_TYPE_TIME_EXTEND:
+ extend = data2host4(ptr);
+ ptr += 4;
+ extend <<= TS_SHIFT;
+ extend += delta;
+ cpu_data[cpu].timestamp += extend;
+ goto read_again;
+
+ case RINGBUF_TYPE_TIME_STAMP:
+ ptr += 12;
+ break;
+ case 0:
+ length = data2host4(ptr);
+ ptr += 4;
+ die("here! length=%d", length);
+ break;
+ default:
+ length = type_len * 4;
+ break;
+ }
+
+ cpu_data[cpu].timestamp += delta;
+
+ data = malloc_or_die(sizeof(*data));
+ memset(data, 0, sizeof(*data));
+
+ data->ts = cpu_data[cpu].timestamp;
+ data->size = length;
+ data->data = ptr;
+ ptr += length;
+
+ cpu_data[cpu].index = calc_index(ptr, cpu);
+ cpu_data[cpu].next = data;
+
+ return data;
+}
+
+struct record *trace_read_data(int cpu)
+{
+ struct record *data;
+
+ data = trace_peek_data(cpu);
+ cpu_data[cpu].next = NULL;
+
+ return data;
+}
+
+ssize_t trace_report(int fd, bool __repipe)
+{
+ char buf[BUFSIZ];
+ char test[] = { 23, 8, 68 };
+ char *version;
+ int show_version = 0;
+ int show_funcs = 0;
+ int show_printk = 0;
+ ssize_t size;
+
+ calc_data_size = 1;
+ repipe = __repipe;
+
+ input_fd = fd;
+
+ read_or_die(buf, 3);
+ if (memcmp(buf, test, 3) != 0)
+ die("no trace data in the file");
+
+ read_or_die(buf, 7);
+ if (memcmp(buf, "tracing", 7) != 0)
+ die("not a trace file (missing 'tracing' tag)");
+
+ version = read_string();
+ if (show_version)
+ printf("version = %s\n", version);
+ free(version);
+
+ read_or_die(buf, 1);
+ file_bigendian = buf[0];
+ host_bigendian = bigendian();
+
+ read_or_die(buf, 1);
+ long_size = buf[0];
+
+ page_size = read4();
+
+ read_header_files();
+
+ read_ftrace_files();
+ read_event_files();
+ read_proc_kallsyms();
+ read_ftrace_printk();
+
+ size = calc_data_size - 1;
+ calc_data_size = 0;
+ repipe = false;
+
+ if (show_funcs) {
+ print_funcs();
+ return size;
+ }
+ if (show_printk) {
+ print_printk();
+ return size;
+ }
+
+ return size;
+}
diff --git a/smartt-perf/util/trace-event-scripting.c b/smartt-perf/util/trace-event-scripting.c
new file mode 100644
index 0000000..f7af2fc
--- /dev/null
+++ b/smartt-perf/util/trace-event-scripting.c
@@ -0,0 +1,167 @@
+/*
+ * trace-event-scripting. Scripting engine common and initialization code.
+ *
+ * Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "../perf.h"
+#include "util.h"
+#include "trace-event.h"
+
+struct scripting_context *scripting_context;
+
+static int stop_script_unsupported(void)
+{
+ return 0;
+}
+
+static void process_event_unsupported(int cpu __unused,
+ void *data __unused,
+ int size __unused,
+ unsigned long long nsecs __unused,
+ char *comm __unused)
+{
+}
+
+static void print_python_unsupported_msg(void)
+{
+ fprintf(stderr, "Python scripting not supported."
+ " Install libpython and rebuild perf to enable it.\n"
+ "For example:\n # apt-get install python-dev (ubuntu)"
+ "\n # yum install python-devel (Fedora)"
+ "\n etc.\n");
+}
+
+static int python_start_script_unsupported(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
+{
+ print_python_unsupported_msg();
+
+ return -1;
+}
+
+static int python_generate_script_unsupported(const char *outfile __unused)
+{
+ print_python_unsupported_msg();
+
+ return -1;
+}
+
+struct scripting_ops python_scripting_unsupported_ops = {
+ .name = "Python",
+ .start_script = python_start_script_unsupported,
+ .stop_script = stop_script_unsupported,
+ .process_event = process_event_unsupported,
+ .generate_script = python_generate_script_unsupported,
+};
+
+static void register_python_scripting(struct scripting_ops *scripting_ops)
+{
+ int err;
+ err = script_spec_register("Python", scripting_ops);
+ if (err)
+ die("error registering Python script extension");
+
+ err = script_spec_register("py", scripting_ops);
+ if (err)
+ die("error registering py script extension");
+
+ scripting_context = malloc(sizeof(struct scripting_context));
+}
+
+#ifdef NO_LIBPYTHON
+void setup_python_scripting(void)
+{
+ register_python_scripting(&python_scripting_unsupported_ops);
+}
+#else
+extern struct scripting_ops python_scripting_ops;
+
+void setup_python_scripting(void)
+{
+ register_python_scripting(&python_scripting_ops);
+}
+#endif
+
+static void print_perl_unsupported_msg(void)
+{
+ fprintf(stderr, "Perl scripting not supported."
+ " Install libperl and rebuild perf to enable it.\n"
+ "For example:\n # apt-get install libperl-dev (ubuntu)"
+ "\n # yum install 'perl(ExtUtils::Embed)' (Fedora)"
+ "\n etc.\n");
+}
+
+static int perl_start_script_unsupported(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
+{
+ print_perl_unsupported_msg();
+
+ return -1;
+}
+
+static int perl_generate_script_unsupported(const char *outfile __unused)
+{
+ print_perl_unsupported_msg();
+
+ return -1;
+}
+
+struct scripting_ops perl_scripting_unsupported_ops = {
+ .name = "Perl",
+ .start_script = perl_start_script_unsupported,
+ .stop_script = stop_script_unsupported,
+ .process_event = process_event_unsupported,
+ .generate_script = perl_generate_script_unsupported,
+};
+
+static void register_perl_scripting(struct scripting_ops *scripting_ops)
+{
+ int err;
+ err = script_spec_register("Perl", scripting_ops);
+ if (err)
+ die("error registering Perl script extension");
+
+ err = script_spec_register("pl", scripting_ops);
+ if (err)
+ die("error registering pl script extension");
+
+ scripting_context = malloc(sizeof(struct scripting_context));
+}
+
+#ifdef NO_LIBPERL
+void setup_perl_scripting(void)
+{
+ register_perl_scripting(&perl_scripting_unsupported_ops);
+}
+#else
+extern struct scripting_ops perl_scripting_ops;
+
+void setup_perl_scripting(void)
+{
+ register_perl_scripting(&perl_scripting_ops);
+}
+#endif
diff --git a/smartt-perf/util/trace-event.h b/smartt-perf/util/trace-event.h
new file mode 100644
index 0000000..b5f12ca
--- /dev/null
+++ b/smartt-perf/util/trace-event.h
@@ -0,0 +1,299 @@
+#ifndef __PERF_TRACE_EVENTS_H
+#define __PERF_TRACE_EVENTS_H
+
+#include <stdbool.h>
+#include "parse-events.h"
+
+#define __unused __attribute__((unused))
+
+
+#ifndef PAGE_MASK
+#define PAGE_MASK (page_size - 1)
+#endif
+
+enum {
+ RINGBUF_TYPE_PADDING = 29,
+ RINGBUF_TYPE_TIME_EXTEND = 30,
+ RINGBUF_TYPE_TIME_STAMP = 31,
+};
+
+#ifndef TS_SHIFT
+#define TS_SHIFT 27
+#endif
+
+#define NSECS_PER_SEC 1000000000ULL
+#define NSECS_PER_USEC 1000ULL
+
+enum format_flags {
+ FIELD_IS_ARRAY = 1,
+ FIELD_IS_POINTER = 2,
+ FIELD_IS_SIGNED = 4,
+ FIELD_IS_STRING = 8,
+ FIELD_IS_DYNAMIC = 16,
+ FIELD_IS_FLAG = 32,
+ FIELD_IS_SYMBOLIC = 64,
+};
+
+struct format_field {
+ struct format_field *next;
+ char *type;
+ char *name;
+ int offset;
+ int size;
+ unsigned long flags;
+};
+
+struct format {
+ int nr_common;
+ int nr_fields;
+ struct format_field *common_fields;
+ struct format_field *fields;
+};
+
+struct print_arg_atom {
+ char *atom;
+};
+
+struct print_arg_string {
+ char *string;
+ int offset;
+};
+
+struct print_arg_field {
+ char *name;
+ struct format_field *field;
+};
+
+struct print_flag_sym {
+ struct print_flag_sym *next;
+ char *value;
+ char *str;
+};
+
+struct print_arg_typecast {
+ char *type;
+ struct print_arg *item;
+};
+
+struct print_arg_flags {
+ struct print_arg *field;
+ char *delim;
+ struct print_flag_sym *flags;
+};
+
+struct print_arg_symbol {
+ struct print_arg *field;
+ struct print_flag_sym *symbols;
+};
+
+struct print_arg;
+
+struct print_arg_op {
+ char *op;
+ int prio;
+ struct print_arg *left;
+ struct print_arg *right;
+};
+
+struct print_arg_func {
+ char *name;
+ struct print_arg *args;
+};
+
+enum print_arg_type {
+ PRINT_NULL,
+ PRINT_ATOM,
+ PRINT_FIELD,
+ PRINT_FLAGS,
+ PRINT_SYMBOL,
+ PRINT_TYPE,
+ PRINT_STRING,
+ PRINT_OP,
+};
+
+struct print_arg {
+ struct print_arg *next;
+ enum print_arg_type type;
+ union {
+ struct print_arg_atom atom;
+ struct print_arg_field field;
+ struct print_arg_typecast typecast;
+ struct print_arg_flags flags;
+ struct print_arg_symbol symbol;
+ struct print_arg_func func;
+ struct print_arg_string string;
+ struct print_arg_op op;
+ };
+};
+
+struct print_fmt {
+ char *format;
+ struct print_arg *args;
+};
+
+struct event {
+ struct event *next;
+ char *name;
+ int id;
+ int flags;
+ struct format format;
+ struct print_fmt print_fmt;
+ char *system;
+};
+
+enum {
+ EVENT_FL_ISFTRACE = 0x01,
+ EVENT_FL_ISPRINT = 0x02,
+ EVENT_FL_ISBPRINT = 0x04,
+ EVENT_FL_ISFUNC = 0x08,
+ EVENT_FL_ISFUNCENT = 0x10,
+ EVENT_FL_ISFUNCRET = 0x20,
+
+ EVENT_FL_FAILED = 0x80000000
+};
+
+struct record {
+ unsigned long long ts;
+ int size;
+ void *data;
+};
+
+struct record *trace_peek_data(int cpu);
+struct record *trace_read_data(int cpu);
+
+void parse_set_info(int nr_cpus, int long_sz);
+
+ssize_t trace_report(int fd, bool repipe);
+
+void *malloc_or_die(unsigned int size);
+
+void parse_cmdlines(char *file, int size);
+void parse_proc_kallsyms(char *file, unsigned int size);
+void parse_ftrace_printk(char *file, unsigned int size);
+
+void print_funcs(void);
+void print_printk(void);
+
+int parse_ftrace_file(char *buf, unsigned long size);
+int parse_event_file(char *buf, unsigned long size, char *sys);
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm);
+
+extern int file_bigendian;
+extern int host_bigendian;
+
+int bigendian(void);
+
+static inline unsigned short __data2host2(unsigned short data)
+{
+ unsigned short swap;
+
+ if (host_bigendian == file_bigendian)
+ return data;
+
+ swap = ((data & 0xffULL) << 8) |
+ ((data & (0xffULL << 8)) >> 8);
+
+ return swap;
+}
+
+static inline unsigned int __data2host4(unsigned int data)
+{
+ unsigned int swap;
+
+ if (host_bigendian == file_bigendian)
+ return data;
+
+ swap = ((data & 0xffULL) << 24) |
+ ((data & (0xffULL << 8)) << 8) |
+ ((data & (0xffULL << 16)) >> 8) |
+ ((data & (0xffULL << 24)) >> 24);
+
+ return swap;
+}
+
+static inline unsigned long long __data2host8(unsigned long long data)
+{
+ unsigned long long swap;
+
+ if (host_bigendian == file_bigendian)
+ return data;
+
+ swap = ((data & 0xffULL) << 56) |
+ ((data & (0xffULL << 8)) << 40) |
+ ((data & (0xffULL << 16)) << 24) |
+ ((data & (0xffULL << 24)) << 8) |
+ ((data & (0xffULL << 32)) >> 8) |
+ ((data & (0xffULL << 40)) >> 24) |
+ ((data & (0xffULL << 48)) >> 40) |
+ ((data & (0xffULL << 56)) >> 56);
+
+ return swap;
+}
+
+#define data2host2(ptr) __data2host2(*(unsigned short *)ptr)
+#define data2host4(ptr) __data2host4(*(unsigned int *)ptr)
+#define data2host8(ptr) ({ \
+ unsigned long long __val; \
+ \
+ memcpy(&__val, (ptr), sizeof(unsigned long long)); \
+ __data2host8(__val); \
+})
+
+extern int header_page_ts_offset;
+extern int header_page_ts_size;
+extern int header_page_size_offset;
+extern int header_page_size_size;
+extern int header_page_data_offset;
+extern int header_page_data_size;
+
+extern bool latency_format;
+
+int trace_parse_common_type(void *data);
+int trace_parse_common_pid(void *data);
+int parse_common_pc(void *data);
+int parse_common_flags(void *data);
+int parse_common_lock_depth(void *data);
+struct event *trace_find_event(int id);
+struct event *trace_find_next_event(struct event *event);
+unsigned long long read_size(void *ptr, int size);
+unsigned long long
+raw_field_value(struct event *event, const char *name, void *data);
+void *raw_field_ptr(struct event *event, const char *name, void *data);
+unsigned long long eval_flag(const char *flag);
+
+int read_tracing_data(int fd, struct list_head *pattrs);
+ssize_t read_tracing_data_size(int fd, struct list_head *pattrs);
+
+/* taken from kernel/trace/trace.h */
+enum trace_flag_type {
+ TRACE_FLAG_IRQS_OFF = 0x01,
+ TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
+ TRACE_FLAG_NEED_RESCHED = 0x04,
+ TRACE_FLAG_HARDIRQ = 0x08,
+ TRACE_FLAG_SOFTIRQ = 0x10,
+};
+
+struct scripting_ops {
+ const char *name;
+ int (*start_script) (const char *script, int argc, const char **argv);
+ int (*stop_script) (void);
+ void (*process_event) (int cpu, void *data, int size,
+ unsigned long long nsecs, char *comm);
+ int (*generate_script) (const char *outfile);
+};
+
+int script_spec_register(const char *spec, struct scripting_ops *ops);
+
+void setup_perl_scripting(void);
+void setup_python_scripting(void);
+
+struct scripting_context {
+ void *event_data;
+};
+
+int common_pc(struct scripting_context *context);
+int common_flags(struct scripting_context *context);
+int common_lock_depth(struct scripting_context *context);
+
+#endif /* __PERF_TRACE_EVENTS_H */
diff --git a/smartt-perf/util/types.h b/smartt-perf/util/types.h
new file mode 100644
index 0000000..5f3689a
--- /dev/null
+++ b/smartt-perf/util/types.h
@@ -0,0 +1,19 @@
+#ifndef __PERF_TYPES_H
+#define __PERF_TYPES_H
+
+#include <stdint.h>
+
+/*
+ * We define u64 as uint64_t for every architecture
+ * so that we can print it with "%"PRIx64 without getting warnings.
+ */
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef unsigned int u32;
+typedef signed int s32;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned char u8;
+typedef signed char s8;
+
+#endif /* __PERF_TYPES_H */
diff --git a/smartt-perf/util/ui/browser.c b/smartt-perf/util/ui/browser.c
new file mode 100644
index 0000000..8bc010e
--- /dev/null
+++ b/smartt-perf/util/ui/browser.c
@@ -0,0 +1,337 @@
+#include "libslang.h"
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <stdlib.h>
+#include <sys/ttydefaults.h>
+#include "browser.h"
+#include "helpline.h"
+#include "../color.h"
+#include "../util.h"
+#include <stdio.h>
+
+static int ui_browser__percent_color(double percent, bool current)
+{
+ if (current)
+ return HE_COLORSET_SELECTED;
+ if (percent >= MIN_RED)
+ return HE_COLORSET_TOP;
+ if (percent >= MIN_GREEN)
+ return HE_COLORSET_MEDIUM;
+ return HE_COLORSET_NORMAL;
+}
+
+void ui_browser__set_color(struct ui_browser *self __used, int color)
+{
+ SLsmg_set_color(color);
+}
+
+void ui_browser__set_percent_color(struct ui_browser *self,
+ double percent, bool current)
+{
+ int color = ui_browser__percent_color(percent, current);
+ ui_browser__set_color(self, color);
+}
+
+void ui_browser__gotorc(struct ui_browser *self, int y, int x)
+{
+ SLsmg_gotorc(self->y + y, self->x + x);
+}
+
+void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
+{
+ struct list_head *head = self->entries;
+ struct list_head *pos;
+
+ switch (whence) {
+ case SEEK_SET:
+ pos = head->next;
+ break;
+ case SEEK_CUR:
+ pos = self->top;
+ break;
+ case SEEK_END:
+ pos = head->prev;
+ break;
+ default:
+ return;
+ }
+
+ if (offset > 0) {
+ while (offset-- != 0)
+ pos = pos->next;
+ } else {
+ while (offset++ != 0)
+ pos = pos->prev;
+ }
+
+ self->top = pos;
+}
+
+void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
+{
+ struct rb_root *root = self->entries;
+ struct rb_node *nd;
+
+ switch (whence) {
+ case SEEK_SET:
+ nd = rb_first(root);
+ break;
+ case SEEK_CUR:
+ nd = self->top;
+ break;
+ case SEEK_END:
+ nd = rb_last(root);
+ break;
+ default:
+ return;
+ }
+
+ if (offset > 0) {
+ while (offset-- != 0)
+ nd = rb_next(nd);
+ } else {
+ while (offset++ != 0)
+ nd = rb_prev(nd);
+ }
+
+ self->top = nd;
+}
+
+unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
+{
+ struct rb_node *nd;
+ int row = 0;
+
+ if (self->top == NULL)
+ self->top = rb_first(self->entries);
+
+ nd = self->top;
+
+ while (nd != NULL) {
+ ui_browser__gotorc(self, row, 0);
+ self->write(self, nd, row);
+ if (++row == self->height)
+ break;
+ nd = rb_next(nd);
+ }
+
+ return row;
+}
+
+bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
+{
+ return self->top_idx + row == self->index;
+}
+
+void ui_browser__refresh_dimensions(struct ui_browser *self)
+{
+ int cols, rows;
+ newtGetScreenSize(&cols, &rows);
+
+ self->width = cols - 1;
+ self->height = rows - 2;
+ self->y = 1;
+ self->x = 0;
+}
+
+void ui_browser__reset_index(struct ui_browser *self)
+{
+ self->index = self->top_idx = 0;
+ self->seek(self, 0, SEEK_SET);
+}
+
+void ui_browser__add_exit_key(struct ui_browser *self, int key)
+{
+ newtFormAddHotKey(self->form, key);
+}
+
+void ui_browser__add_exit_keys(struct ui_browser *self, int keys[])
+{
+ int i = 0;
+
+ while (keys[i] && i < 64) {
+ ui_browser__add_exit_key(self, keys[i]);
+ ++i;
+ }
+}
+
+int ui_browser__show(struct ui_browser *self, const char *title,
+ const char *helpline, ...)
+{
+ va_list ap;
+ int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP,
+ NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ',
+ NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 };
+
+ if (self->form != NULL)
+ newtFormDestroy(self->form);
+
+ ui_browser__refresh_dimensions(self);
+ self->form = newtForm(NULL, NULL, 0);
+ if (self->form == NULL)
+ return -1;
+
+ self->sb = newtVerticalScrollbar(self->width, 1, self->height,
+ HE_COLORSET_NORMAL,
+ HE_COLORSET_SELECTED);
+ if (self->sb == NULL)
+ return -1;
+
+ SLsmg_gotorc(0, 0);
+ ui_browser__set_color(self, NEWT_COLORSET_ROOT);
+ slsmg_write_nstring(title, self->width);
+
+ ui_browser__add_exit_keys(self, keys);
+ newtFormAddComponent(self->form, self->sb);
+
+ va_start(ap, helpline);
+ ui_helpline__vpush(helpline, ap);
+ va_end(ap);
+ return 0;
+}
+
+void ui_browser__hide(struct ui_browser *self)
+{
+ newtFormDestroy(self->form);
+ self->form = NULL;
+ ui_helpline__pop();
+}
+
+int ui_browser__refresh(struct ui_browser *self)
+{
+ int row;
+
+ newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
+ row = self->refresh(self);
+ ui_browser__set_color(self, HE_COLORSET_NORMAL);
+ SLsmg_fill_region(self->y + row, self->x,
+ self->height - row, self->width, ' ');
+
+ return 0;
+}
+
+int ui_browser__run(struct ui_browser *self)
+{
+ struct newtExitStruct es;
+
+ if (ui_browser__refresh(self) < 0)
+ return -1;
+
+ while (1) {
+ off_t offset;
+
+ newtFormRun(self->form, &es);
+
+ if (es.reason != NEWT_EXIT_HOTKEY)
+ break;
+ switch (es.u.key) {
+ case NEWT_KEY_DOWN:
+ if (self->index == self->nr_entries - 1)
+ break;
+ ++self->index;
+ if (self->index == self->top_idx + self->height) {
+ ++self->top_idx;
+ self->seek(self, +1, SEEK_CUR);
+ }
+ break;
+ case NEWT_KEY_UP:
+ if (self->index == 0)
+ break;
+ --self->index;
+ if (self->index < self->top_idx) {
+ --self->top_idx;
+ self->seek(self, -1, SEEK_CUR);
+ }
+ break;
+ case NEWT_KEY_PGDN:
+ case ' ':
+ if (self->top_idx + self->height > self->nr_entries - 1)
+ break;
+
+ offset = self->height;
+ if (self->index + offset > self->nr_entries - 1)
+ offset = self->nr_entries - 1 - self->index;
+ self->index += offset;
+ self->top_idx += offset;
+ self->seek(self, +offset, SEEK_CUR);
+ break;
+ case NEWT_KEY_PGUP:
+ if (self->top_idx == 0)
+ break;
+
+ if (self->top_idx < self->height)
+ offset = self->top_idx;
+ else
+ offset = self->height;
+
+ self->index -= offset;
+ self->top_idx -= offset;
+ self->seek(self, -offset, SEEK_CUR);
+ break;
+ case NEWT_KEY_HOME:
+ ui_browser__reset_index(self);
+ break;
+ case NEWT_KEY_END:
+ offset = self->height - 1;
+ if (offset >= self->nr_entries)
+ offset = self->nr_entries - 1;
+
+ self->index = self->nr_entries - 1;
+ self->top_idx = self->index - offset;
+ self->seek(self, -offset, SEEK_END);
+ break;
+ default:
+ return es.u.key;
+ }
+ if (ui_browser__refresh(self) < 0)
+ return -1;
+ }
+ return -1;
+}
+
+unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
+{
+ struct list_head *pos;
+ struct list_head *head = self->entries;
+ int row = 0;
+
+ if (self->top == NULL || self->top == self->entries)
+ self->top = head->next;
+
+ pos = self->top;
+
+ list_for_each_from(pos, head) {
+ ui_browser__gotorc(self, row, 0);
+ self->write(self, pos, row);
+ if (++row == self->height)
+ break;
+ }
+
+ return row;
+}
+
+static struct newtPercentTreeColors {
+ const char *topColorFg, *topColorBg;
+ const char *mediumColorFg, *mediumColorBg;
+ const char *normalColorFg, *normalColorBg;
+ const char *selColorFg, *selColorBg;
+ const char *codeColorFg, *codeColorBg;
+} defaultPercentTreeColors = {
+ "red", "lightgray",
+ "green", "lightgray",
+ "black", "lightgray",
+ "lightgray", "magenta",
+ "blue", "lightgray",
+};
+
+void ui_browser__init(void)
+{
+ struct newtPercentTreeColors *c = &defaultPercentTreeColors;
+
+ sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
+ sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
+ sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
+ sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
+ sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
+}
diff --git a/smartt-perf/util/ui/browser.h b/smartt-perf/util/ui/browser.h
new file mode 100644
index 0000000..0dc7e4d
--- /dev/null
+++ b/smartt-perf/util/ui/browser.h
@@ -0,0 +1,51 @@
+#ifndef _PERF_UI_BROWSER_H_
+#define _PERF_UI_BROWSER_H_ 1
+
+#include <stdbool.h>
+#include <newt.h>
+#include <sys/types.h>
+#include "../types.h"
+
+#define HE_COLORSET_TOP 50
+#define HE_COLORSET_MEDIUM 51
+#define HE_COLORSET_NORMAL 52
+#define HE_COLORSET_SELECTED 53
+#define HE_COLORSET_CODE 54
+
+struct ui_browser {
+ newtComponent form, sb;
+ u64 index, top_idx;
+ void *top, *entries;
+ u16 y, x, width, height;
+ void *priv;
+ unsigned int (*refresh)(struct ui_browser *self);
+ void (*write)(struct ui_browser *self, void *entry, int row);
+ void (*seek)(struct ui_browser *self, off_t offset, int whence);
+ u32 nr_entries;
+};
+
+
+void ui_browser__set_color(struct ui_browser *self, int color);
+void ui_browser__set_percent_color(struct ui_browser *self,
+ double percent, bool current);
+bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
+void ui_browser__refresh_dimensions(struct ui_browser *self);
+void ui_browser__reset_index(struct ui_browser *self);
+
+void ui_browser__gotorc(struct ui_browser *self, int y, int x);
+void ui_browser__add_exit_key(struct ui_browser *self, int key);
+void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]);
+int ui_browser__show(struct ui_browser *self, const char *title,
+ const char *helpline, ...);
+void ui_browser__hide(struct ui_browser *self);
+int ui_browser__refresh(struct ui_browser *self);
+int ui_browser__run(struct ui_browser *self);
+
+void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
+unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
+
+void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
+unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
+
+void ui_browser__init(void);
+#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/smartt-perf/util/ui/browsers/annotate.c b/smartt-perf/util/ui/browsers/annotate.c
new file mode 100644
index 0000000..82b78f9
--- /dev/null
+++ b/smartt-perf/util/ui/browsers/annotate.c
@@ -0,0 +1,237 @@
+#include "../browser.h"
+#include "../helpline.h"
+#include "../libslang.h"
+#include "../../hist.h"
+#include "../../sort.h"
+#include "../../symbol.h"
+
+static void ui__error_window(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
+ va_end(ap);
+}
+
+struct annotate_browser {
+ struct ui_browser b;
+ struct rb_root entries;
+ struct rb_node *curr_hot;
+};
+
+struct objdump_line_rb_node {
+ struct rb_node rb_node;
+ double percent;
+ u32 idx;
+};
+
+static inline
+struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
+{
+ return (struct objdump_line_rb_node *)(self + 1);
+}
+
+static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
+{
+ struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
+ bool current_entry = ui_browser__is_current_entry(self, row);
+ int width = self->width;
+
+ if (ol->offset != -1) {
+ struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
+ ui_browser__set_percent_color(self, olrb->percent, current_entry);
+ slsmg_printf(" %7.2f ", olrb->percent);
+ if (!current_entry)
+ ui_browser__set_color(self, HE_COLORSET_CODE);
+ } else {
+ ui_browser__set_percent_color(self, 0, current_entry);
+ slsmg_write_nstring(" ", 9);
+ }
+
+ SLsmg_write_char(':');
+ slsmg_write_nstring(" ", 8);
+ if (!*ol->line)
+ slsmg_write_nstring(" ", width - 18);
+ else
+ slsmg_write_nstring(ol->line, width - 18);
+}
+
+static double objdump_line__calc_percent(struct objdump_line *self,
+ struct list_head *head,
+ struct symbol *sym)
+{
+ double percent = 0.0;
+
+ if (self->offset != -1) {
+ int len = sym->end - sym->start;
+ unsigned int hits = 0;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_ext *sym_ext = priv->ext;
+ struct sym_hist *h = priv->hist;
+ s64 offset = self->offset;
+ struct objdump_line *next = objdump__get_next_ip_line(head, self);
+
+
+ while (offset < (s64)len &&
+ (next == NULL || offset < next->offset)) {
+ if (sym_ext) {
+ percent += sym_ext[offset].percent;
+ } else
+ hits += h->ip[offset];
+
+ ++offset;
+ }
+
+ if (sym_ext == NULL && h->sum)
+ percent = 100.0 * hits / h->sum;
+ }
+
+ return percent;
+}
+
+static void objdump__insert_line(struct rb_root *self,
+ struct objdump_line_rb_node *line)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct objdump_line_rb_node *l;
+
+ while (*p != NULL) {
+ parent = *p;
+ l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
+ if (line->percent < l->percent)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&line->rb_node, parent, p);
+ rb_insert_color(&line->rb_node, self);
+}
+
+static void annotate_browser__set_top(struct annotate_browser *self,
+ struct rb_node *nd)
+{
+ struct objdump_line_rb_node *rbpos;
+ struct objdump_line *pos;
+ unsigned back;
+
+ ui_browser__refresh_dimensions(&self->b);
+ back = self->b.height / 2;
+ rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
+ pos = ((struct objdump_line *)rbpos) - 1;
+ self->b.top_idx = self->b.index = rbpos->idx;
+
+ while (self->b.top_idx != 0 && back != 0) {
+ pos = list_entry(pos->node.prev, struct objdump_line, node);
+
+ --self->b.top_idx;
+ --back;
+ }
+
+ self->b.top = pos;
+ self->curr_hot = nd;
+}
+
+static int annotate_browser__run(struct annotate_browser *self)
+{
+ struct rb_node *nd;
+ struct hist_entry *he = self->b.priv;
+ int key;
+
+ if (ui_browser__show(&self->b, he->ms.sym->name,
+ "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
+ return -1;
+ /*
+ * To allow builtin-annotate to cycle thru multiple symbols by
+ * examining the exit key for this function.
+ */
+ ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT);
+
+ nd = self->curr_hot;
+ if (nd) {
+ int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 };
+ ui_browser__add_exit_keys(&self->b, tabs);
+ }
+
+ while (1) {
+ key = ui_browser__run(&self->b);
+
+ switch (key) {
+ case NEWT_KEY_TAB:
+ nd = rb_prev(nd);
+ if (nd == NULL)
+ nd = rb_last(&self->entries);
+ annotate_browser__set_top(self, nd);
+ break;
+ case NEWT_KEY_UNTAB:
+ nd = rb_next(nd);
+ if (nd == NULL)
+ nd = rb_first(&self->entries);
+ annotate_browser__set_top(self, nd);
+ break;
+ default:
+ goto out;
+ }
+ }
+out:
+ ui_browser__hide(&self->b);
+ return key;
+}
+
+int hist_entry__tui_annotate(struct hist_entry *self)
+{
+ struct objdump_line *pos, *n;
+ struct objdump_line_rb_node *rbpos;
+ LIST_HEAD(head);
+ struct annotate_browser browser = {
+ .b = {
+ .entries = &head,
+ .refresh = ui_browser__list_head_refresh,
+ .seek = ui_browser__list_head_seek,
+ .write = annotate_browser__write,
+ .priv = self,
+ },
+ };
+ int ret;
+
+ if (self->ms.sym == NULL)
+ return -1;
+
+ if (self->ms.map->dso->annotate_warned)
+ return -1;
+
+ if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
+ ui__error_window(ui_helpline__last_msg);
+ return -1;
+ }
+
+ ui_helpline__push("Press <- or ESC to exit");
+
+ list_for_each_entry(pos, &head, node) {
+ size_t line_len = strlen(pos->line);
+ if (browser.b.width < line_len)
+ browser.b.width = line_len;
+ rbpos = objdump_line__rb(pos);
+ rbpos->idx = browser.b.nr_entries++;
+ rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
+ if (rbpos->percent < 0.01)
+ continue;
+ objdump__insert_line(&browser.entries, rbpos);
+ }
+
+ /*
+ * Position the browser at the hottest line.
+ */
+ browser.curr_hot = rb_last(&browser.entries);
+ if (browser.curr_hot)
+ annotate_browser__set_top(&browser, browser.curr_hot);
+
+ browser.b.width += 18; /* Percentage */
+ ret = annotate_browser__run(&browser);
+ list_for_each_entry_safe(pos, n, &head, node) {
+ list_del(&pos->node);
+ objdump_line__free(pos);
+ }
+ return ret;
+}
diff --git a/smartt-perf/util/ui/browsers/hists.c b/smartt-perf/util/ui/browsers/hists.c
new file mode 100644
index 0000000..60c463c
--- /dev/null
+++ b/smartt-perf/util/ui/browsers/hists.c
@@ -0,0 +1,1013 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#undef _GNU_SOURCE
+#include "../libslang.h"
+#include <stdlib.h>
+#include <string.h>
+#include <newt.h>
+#include <linux/rbtree.h>
+
+#include "../../hist.h"
+#include "../../pstack.h"
+#include "../../sort.h"
+#include "../../util.h"
+
+#include "../browser.h"
+#include "../helpline.h"
+#include "../util.h"
+#include "map.h"
+
+struct hist_browser {
+ struct ui_browser b;
+ struct hists *hists;
+ struct hist_entry *he_selection;
+ struct map_symbol *selection;
+};
+
+static void hist_browser__refresh_dimensions(struct hist_browser *self)
+{
+ /* 3 == +/- toggle symbol before actual hist_entry rendering */
+ self->b.width = 3 + (hists__sort_list_width(self->hists) +
+ sizeof("[k]"));
+}
+
+static void hist_browser__reset(struct hist_browser *self)
+{
+ self->b.nr_entries = self->hists->nr_entries;
+ hist_browser__refresh_dimensions(self);
+ ui_browser__reset_index(&self->b);
+}
+
+static char tree__folded_sign(bool unfolded)
+{
+ return unfolded ? '-' : '+';
+}
+
+static char map_symbol__folded(const struct map_symbol *self)
+{
+ return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
+}
+
+static char hist_entry__folded(const struct hist_entry *self)
+{
+ return map_symbol__folded(&self->ms);
+}
+
+static char callchain_list__folded(const struct callchain_list *self)
+{
+ return map_symbol__folded(&self->ms);
+}
+
+static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
+{
+ self->unfolded = unfold ? self->has_children : false;
+}
+
+static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
+{
+ int n = 0;
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+ struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
+ struct callchain_list *chain;
+ char folded_sign = ' '; /* No children */
+
+ list_for_each_entry(chain, &child->val, list) {
+ ++n;
+ /* We need this because we may not have children */
+ folded_sign = callchain_list__folded(chain);
+ if (folded_sign == '+')
+ break;
+ }
+
+ if (folded_sign == '-') /* Have children and they're unfolded */
+ n += callchain_node__count_rows_rb_tree(child);
+ }
+
+ return n;
+}
+
+static int callchain_node__count_rows(struct callchain_node *node)
+{
+ struct callchain_list *chain;
+ bool unfolded = false;
+ int n = 0;
+
+ list_for_each_entry(chain, &node->val, list) {
+ ++n;
+ unfolded = chain->ms.unfolded;
+ }
+
+ if (unfolded)
+ n += callchain_node__count_rows_rb_tree(node);
+
+ return n;
+}
+
+static int callchain__count_rows(struct rb_root *chain)
+{
+ struct rb_node *nd;
+ int n = 0;
+
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+ n += callchain_node__count_rows(node);
+ }
+
+ return n;
+}
+
+static bool map_symbol__toggle_fold(struct map_symbol *self)
+{
+ if (!self->has_children)
+ return false;
+
+ self->unfolded = !self->unfolded;
+ return true;
+}
+
+static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
+{
+ struct rb_node *nd = rb_first(&self->rb_root);
+
+ for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+ struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
+ struct callchain_list *chain;
+ bool first = true;
+
+ list_for_each_entry(chain, &child->val, list) {
+ if (first) {
+ first = false;
+ chain->ms.has_children = chain->list.next != &child->val ||
+ !RB_EMPTY_ROOT(&child->rb_root);
+ } else
+ chain->ms.has_children = chain->list.next == &child->val &&
+ !RB_EMPTY_ROOT(&child->rb_root);
+ }
+
+ callchain_node__init_have_children_rb_tree(child);
+ }
+}
+
+static void callchain_node__init_have_children(struct callchain_node *self)
+{
+ struct callchain_list *chain;
+
+ list_for_each_entry(chain, &self->val, list)
+ chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
+
+ callchain_node__init_have_children_rb_tree(self);
+}
+
+static void callchain__init_have_children(struct rb_root *self)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+ callchain_node__init_have_children(node);
+ }
+}
+
+static void hist_entry__init_have_children(struct hist_entry *self)
+{
+ if (!self->init_have_children) {
+ self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
+ callchain__init_have_children(&self->sorted_chain);
+ self->init_have_children = true;
+ }
+}
+
+static bool hist_browser__toggle_fold(struct hist_browser *self)
+{
+ if (map_symbol__toggle_fold(self->selection)) {
+ struct hist_entry *he = self->he_selection;
+
+ hist_entry__init_have_children(he);
+ self->hists->nr_entries -= he->nr_rows;
+
+ if (he->ms.unfolded)
+ he->nr_rows = callchain__count_rows(&he->sorted_chain);
+ else
+ he->nr_rows = 0;
+ self->hists->nr_entries += he->nr_rows;
+ self->b.nr_entries = self->hists->nr_entries;
+
+ return true;
+ }
+
+ /* If it doesn't have children, no toggling performed */
+ return false;
+}
+
+static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
+{
+ int n = 0;
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+ struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
+ struct callchain_list *chain;
+ bool has_children = false;
+
+ list_for_each_entry(chain, &child->val, list) {
+ ++n;
+ map_symbol__set_folding(&chain->ms, unfold);
+ has_children = chain->ms.has_children;
+ }
+
+ if (has_children)
+ n += callchain_node__set_folding_rb_tree(child, unfold);
+ }
+
+ return n;
+}
+
+static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
+{
+ struct callchain_list *chain;
+ bool has_children = false;
+ int n = 0;
+
+ list_for_each_entry(chain, &node->val, list) {
+ ++n;
+ map_symbol__set_folding(&chain->ms, unfold);
+ has_children = chain->ms.has_children;
+ }
+
+ if (has_children)
+ n += callchain_node__set_folding_rb_tree(node, unfold);
+
+ return n;
+}
+
+static int callchain__set_folding(struct rb_root *chain, bool unfold)
+{
+ struct rb_node *nd;
+ int n = 0;
+
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+ n += callchain_node__set_folding(node, unfold);
+ }
+
+ return n;
+}
+
+static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
+{
+ hist_entry__init_have_children(self);
+ map_symbol__set_folding(&self->ms, unfold);
+
+ if (self->ms.has_children) {
+ int n = callchain__set_folding(&self->sorted_chain, unfold);
+ self->nr_rows = unfold ? n : 0;
+ } else
+ self->nr_rows = 0;
+}
+
+static void hists__set_folding(struct hists *self, bool unfold)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = 0;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
+ hist_entry__set_folding(he, unfold);
+ self->nr_entries += 1 + he->nr_rows;
+ }
+}
+
+static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
+{
+ hists__set_folding(self->hists, unfold);
+ self->b.nr_entries = self->hists->nr_entries;
+ /* Go to the start, we may be way after valid entries after a collapse */
+ ui_browser__reset_index(&self->b);
+}
+
+static int hist_browser__run(struct hist_browser *self, const char *title)
+{
+ int key;
+ int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
+ NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, };
+
+ self->b.entries = &self->hists->entries;
+ self->b.nr_entries = self->hists->nr_entries;
+
+ hist_browser__refresh_dimensions(self);
+
+ if (ui_browser__show(&self->b, title,
+ "Press '?' for help on key bindings") < 0)
+ return -1;
+
+ ui_browser__add_exit_keys(&self->b, exit_keys);
+
+ while (1) {
+ key = ui_browser__run(&self->b);
+
+ switch (key) {
+ case 'D': { /* Debug */
+ static int seq;
+ struct hist_entry *h = rb_entry(self->b.top,
+ struct hist_entry, rb_node);
+ ui_helpline__pop();
+ ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
+ seq++, self->b.nr_entries,
+ self->hists->nr_entries,
+ self->b.height,
+ self->b.index,
+ self->b.top_idx,
+ h->row_offset, h->nr_rows);
+ }
+ break;
+ case 'C':
+ /* Collapse the whole world. */
+ hist_browser__set_folding(self, false);
+ break;
+ case 'E':
+ /* Expand the whole world. */
+ hist_browser__set_folding(self, true);
+ break;
+ case NEWT_KEY_ENTER:
+ if (hist_browser__toggle_fold(self))
+ break;
+ /* fall thru */
+ default:
+ goto out;
+ }
+ }
+out:
+ ui_browser__hide(&self->b);
+ return key;
+}
+
+static char *callchain_list__sym_name(struct callchain_list *self,
+ char *bf, size_t bfsize)
+{
+ if (self->ms.sym)
+ return self->ms.sym->name;
+
+ snprintf(bf, bfsize, "%#" PRIx64, self->ip);
+ return bf;
+}
+
+#define LEVEL_OFFSET_STEP 3
+
+static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
+ struct callchain_node *chain_node,
+ u64 total, int level,
+ unsigned short row,
+ off_t *row_offset,
+ bool *is_current_entry)
+{
+ struct rb_node *node;
+ int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
+ u64 new_total, remaining;
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = chain_node->children_hit;
+ else
+ new_total = total;
+
+ remaining = new_total;
+ node = rb_first(&chain_node->rb_root);
+ while (node) {
+ struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
+ struct rb_node *next = rb_next(node);
+ u64 cumul = cumul_hits(child);
+ struct callchain_list *chain;
+ char folded_sign = ' ';
+ int first = true;
+ int extra_offset = 0;
+
+ remaining -= cumul;
+
+ list_for_each_entry(chain, &child->val, list) {
+ char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
+ const char *str;
+ int color;
+ bool was_first = first;
+
+ if (first)
+ first = false;
+ else
+ extra_offset = LEVEL_OFFSET_STEP;
+
+ folded_sign = callchain_list__folded(chain);
+ if (*row_offset != 0) {
+ --*row_offset;
+ goto do_next;
+ }
+
+ alloc_str = NULL;
+ str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ if (was_first) {
+ double percent = cumul * 100.0 / new_total;
+
+ if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
+ str = "Not enough memory!";
+ else
+ str = alloc_str;
+ }
+
+ color = HE_COLORSET_NORMAL;
+ width = self->b.width - (offset + extra_offset + 2);
+ if (ui_browser__is_current_entry(&self->b, row)) {
+ self->selection = &chain->ms;
+ color = HE_COLORSET_SELECTED;
+ *is_current_entry = true;
+ }
+
+ ui_browser__set_color(&self->b, color);
+ ui_browser__gotorc(&self->b, row, 0);
+ slsmg_write_nstring(" ", offset + extra_offset);
+ slsmg_printf("%c ", folded_sign);
+ slsmg_write_nstring(str, width);
+ free(alloc_str);
+
+ if (++row == self->b.height)
+ goto out;
+do_next:
+ if (folded_sign == '+')
+ break;
+ }
+
+ if (folded_sign == '-') {
+ const int new_level = level + (extra_offset ? 2 : 1);
+ row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
+ new_level, row, row_offset,
+ is_current_entry);
+ }
+ if (row == self->b.height)
+ goto out;
+ node = next;
+ }
+out:
+ return row - first_row;
+}
+
+static int hist_browser__show_callchain_node(struct hist_browser *self,
+ struct callchain_node *node,
+ int level, unsigned short row,
+ off_t *row_offset,
+ bool *is_current_entry)
+{
+ struct callchain_list *chain;
+ int first_row = row,
+ offset = level * LEVEL_OFFSET_STEP,
+ width = self->b.width - offset;
+ char folded_sign = ' ';
+
+ list_for_each_entry(chain, &node->val, list) {
+ char ipstr[BITS_PER_LONG / 4 + 1], *s;
+ int color;
+
+ folded_sign = callchain_list__folded(chain);
+
+ if (*row_offset != 0) {
+ --*row_offset;
+ continue;
+ }
+
+ color = HE_COLORSET_NORMAL;
+ if (ui_browser__is_current_entry(&self->b, row)) {
+ self->selection = &chain->ms;
+ color = HE_COLORSET_SELECTED;
+ *is_current_entry = true;
+ }
+
+ s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ ui_browser__gotorc(&self->b, row, 0);
+ ui_browser__set_color(&self->b, color);
+ slsmg_write_nstring(" ", offset);
+ slsmg_printf("%c ", folded_sign);
+ slsmg_write_nstring(s, width - 2);
+
+ if (++row == self->b.height)
+ goto out;
+ }
+
+ if (folded_sign == '-')
+ row += hist_browser__show_callchain_node_rb_tree(self, node,
+ self->hists->stats.total_period,
+ level + 1, row,
+ row_offset,
+ is_current_entry);
+out:
+ return row - first_row;
+}
+
+static int hist_browser__show_callchain(struct hist_browser *self,
+ struct rb_root *chain,
+ int level, unsigned short row,
+ off_t *row_offset,
+ bool *is_current_entry)
+{
+ struct rb_node *nd;
+ int first_row = row;
+
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+
+ row += hist_browser__show_callchain_node(self, node, level,
+ row, row_offset,
+ is_current_entry);
+ if (row == self->b.height)
+ break;
+ }
+
+ return row - first_row;
+}
+
+static int hist_browser__show_entry(struct hist_browser *self,
+ struct hist_entry *entry,
+ unsigned short row)
+{
+ char s[256];
+ double percent;
+ int printed = 0;
+ int color, width = self->b.width;
+ char folded_sign = ' ';
+ bool current_entry = ui_browser__is_current_entry(&self->b, row);
+ off_t row_offset = entry->row_offset;
+
+ if (current_entry) {
+ self->he_selection = entry;
+ self->selection = &entry->ms;
+ }
+
+ if (symbol_conf.use_callchain) {
+ hist_entry__init_have_children(entry);
+ folded_sign = hist_entry__folded(entry);
+ }
+
+ if (row_offset == 0) {
+ hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
+ 0, false, self->hists->stats.total_period);
+ percent = (entry->period * 100.0) / self->hists->stats.total_period;
+
+ color = HE_COLORSET_SELECTED;
+ if (!current_entry) {
+ if (percent >= MIN_RED)
+ color = HE_COLORSET_TOP;
+ else if (percent >= MIN_GREEN)
+ color = HE_COLORSET_MEDIUM;
+ else
+ color = HE_COLORSET_NORMAL;
+ }
+
+ ui_browser__set_color(&self->b, color);
+ ui_browser__gotorc(&self->b, row, 0);
+ if (symbol_conf.use_callchain) {
+ slsmg_printf("%c ", folded_sign);
+ width -= 2;
+ }
+ slsmg_write_nstring(s, width);
+ ++row;
+ ++printed;
+ } else
+ --row_offset;
+
+ if (folded_sign == '-' && row != self->b.height) {
+ printed += hist_browser__show_callchain(self, &entry->sorted_chain,
+ 1, row, &row_offset,
+ &current_entry);
+ if (current_entry)
+ self->he_selection = entry;
+ }
+
+ return printed;
+}
+
+static unsigned int hist_browser__refresh(struct ui_browser *self)
+{
+ unsigned row = 0;
+ struct rb_node *nd;
+ struct hist_browser *hb = container_of(self, struct hist_browser, b);
+
+ if (self->top == NULL)
+ self->top = rb_first(&hb->hists->entries);
+
+ for (nd = self->top; nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (h->filtered)
+ continue;
+
+ row += hist_browser__show_entry(hb, h, row);
+ if (row == self->height)
+ break;
+ }
+
+ return row;
+}
+
+static struct rb_node *hists__filter_entries(struct rb_node *nd)
+{
+ while (nd != NULL) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ if (!h->filtered)
+ return nd;
+
+ nd = rb_next(nd);
+ }
+
+ return NULL;
+}
+
+static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
+{
+ while (nd != NULL) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ if (!h->filtered)
+ return nd;
+
+ nd = rb_prev(nd);
+ }
+
+ return NULL;
+}
+
+static void ui_browser__hists_seek(struct ui_browser *self,
+ off_t offset, int whence)
+{
+ struct hist_entry *h;
+ struct rb_node *nd;
+ bool first = true;
+
+ switch (whence) {
+ case SEEK_SET:
+ nd = hists__filter_entries(rb_first(self->entries));
+ break;
+ case SEEK_CUR:
+ nd = self->top;
+ goto do_offset;
+ case SEEK_END:
+ nd = hists__filter_prev_entries(rb_last(self->entries));
+ first = false;
+ break;
+ default:
+ return;
+ }
+
+ /*
+ * Moves not relative to the first visible entry invalidates its
+ * row_offset:
+ */
+ h = rb_entry(self->top, struct hist_entry, rb_node);
+ h->row_offset = 0;
+
+ /*
+ * Here we have to check if nd is expanded (+), if it is we can't go
+ * the next top level hist_entry, instead we must compute an offset of
+ * what _not_ to show and not change the first visible entry.
+ *
+ * This offset increments when we are going from top to bottom and
+ * decreases when we're going from bottom to top.
+ *
+ * As we don't have backpointers to the top level in the callchains
+ * structure, we need to always print the whole hist_entry callchain,
+ * skipping the first ones that are before the first visible entry
+ * and stop when we printed enough lines to fill the screen.
+ */
+do_offset:
+ if (offset > 0) {
+ do {
+ h = rb_entry(nd, struct hist_entry, rb_node);
+ if (h->ms.unfolded) {
+ u16 remaining = h->nr_rows - h->row_offset;
+ if (offset > remaining) {
+ offset -= remaining;
+ h->row_offset = 0;
+ } else {
+ h->row_offset += offset;
+ offset = 0;
+ self->top = nd;
+ break;
+ }
+ }
+ nd = hists__filter_entries(rb_next(nd));
+ if (nd == NULL)
+ break;
+ --offset;
+ self->top = nd;
+ } while (offset != 0);
+ } else if (offset < 0) {
+ while (1) {
+ h = rb_entry(nd, struct hist_entry, rb_node);
+ if (h->ms.unfolded) {
+ if (first) {
+ if (-offset > h->row_offset) {
+ offset += h->row_offset;
+ h->row_offset = 0;
+ } else {
+ h->row_offset += offset;
+ offset = 0;
+ self->top = nd;
+ break;
+ }
+ } else {
+ if (-offset > h->nr_rows) {
+ offset += h->nr_rows;
+ h->row_offset = 0;
+ } else {
+ h->row_offset = h->nr_rows + offset;
+ offset = 0;
+ self->top = nd;
+ break;
+ }
+ }
+ }
+
+ nd = hists__filter_prev_entries(rb_prev(nd));
+ if (nd == NULL)
+ break;
+ ++offset;
+ self->top = nd;
+ if (offset == 0) {
+ /*
+ * Last unfiltered hist_entry, check if it is
+ * unfolded, if it is then we should have
+ * row_offset at its last entry.
+ */
+ h = rb_entry(nd, struct hist_entry, rb_node);
+ if (h->ms.unfolded)
+ h->row_offset = h->nr_rows;
+ break;
+ }
+ first = false;
+ }
+ } else {
+ self->top = nd;
+ h = rb_entry(nd, struct hist_entry, rb_node);
+ h->row_offset = 0;
+ }
+}
+
+static struct hist_browser *hist_browser__new(struct hists *hists)
+{
+ struct hist_browser *self = zalloc(sizeof(*self));
+
+ if (self) {
+ self->hists = hists;
+ self->b.refresh = hist_browser__refresh;
+ self->b.seek = ui_browser__hists_seek;
+ }
+
+ return self;
+}
+
+static void hist_browser__delete(struct hist_browser *self)
+{
+ free(self);
+}
+
+static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
+{
+ return self->he_selection;
+}
+
+static struct thread *hist_browser__selected_thread(struct hist_browser *self)
+{
+ return self->he_selection->thread;
+}
+
+static int hists__browser_title(struct hists *self, char *bf, size_t size,
+ const char *ev_name, const struct dso *dso,
+ const struct thread *thread)
+{
+ char unit;
+ int printed;
+ unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
+
+ nr_events = convert_unit(nr_events, &unit);
+ printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
+
+ if (thread)
+ printed += snprintf(bf + printed, size - printed,
+ ", Thread: %s(%d)",
+ (thread->comm_set ? thread->comm : ""),
+ thread->pid);
+ if (dso)
+ printed += snprintf(bf + printed, size - printed,
+ ", DSO: %s", dso->short_name);
+ return printed;
+}
+
+int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
+{
+ struct hist_browser *browser = hist_browser__new(self);
+ struct pstack *fstack;
+ const struct thread *thread_filter = NULL;
+ const struct dso *dso_filter = NULL;
+ char msg[160];
+ int key = -1;
+
+ if (browser == NULL)
+ return -1;
+
+ fstack = pstack__new(2);
+ if (fstack == NULL)
+ goto out;
+
+ ui_helpline__push(helpline);
+
+ hists__browser_title(self, msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
+ while (1) {
+ const struct thread *thread;
+ const struct dso *dso;
+ char *options[16];
+ int nr_options = 0, choice = 0, i,
+ annotate = -2, zoom_dso = -2, zoom_thread = -2,
+ browse_map = -2;
+
+ key = hist_browser__run(browser, msg);
+
+ thread = hist_browser__selected_thread(browser);
+ dso = browser->selection->map ? browser->selection->map->dso : NULL;
+
+ switch (key) {
+ case NEWT_KEY_TAB:
+ case NEWT_KEY_UNTAB:
+ /*
+ * Exit the browser, let hists__browser_tree
+ * go to the next or previous
+ */
+ goto out_free_stack;
+ case 'a':
+ if (browser->selection->map == NULL &&
+ browser->selection->map->dso->annotate_warned)
+ continue;
+ goto do_annotate;
+ case 'd':
+ goto zoom_dso;
+ case 't':
+ goto zoom_thread;
+ case NEWT_KEY_F1:
+ case 'h':
+ case '?':
+ ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
+ "<- Zoom out\n"
+ "a Annotate current symbol\n"
+ "h/?/F1 Show this window\n"
+ "C Collapse all callchains\n"
+ "E Expand all callchains\n"
+ "d Zoom into current DSO\n"
+ "t Zoom into current Thread\n"
+ "q/CTRL+C Exit browser");
+ continue;
+ case NEWT_KEY_ENTER:
+ case NEWT_KEY_RIGHT:
+ /* menu */
+ break;
+ case NEWT_KEY_LEFT: {
+ const void *top;
+
+ if (pstack__empty(fstack))
+ continue;
+ top = pstack__pop(fstack);
+ if (top == &dso_filter)
+ goto zoom_out_dso;
+ if (top == &thread_filter)
+ goto zoom_out_thread;
+ continue;
+ }
+ case NEWT_KEY_ESCAPE:
+ if (!ui__dialog_yesno("Do you really want to exit?"))
+ continue;
+ /* Fall thru */
+ default:
+ goto out_free_stack;
+ }
+
+ if (browser->selection->sym != NULL &&
+ !browser->selection->map->dso->annotate_warned &&
+ asprintf(&options[nr_options], "Annotate %s",
+ browser->selection->sym->name) > 0)
+ annotate = nr_options++;
+
+ if (thread != NULL &&
+ asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
+ (thread_filter ? "out of" : "into"),
+ (thread->comm_set ? thread->comm : ""),
+ thread->pid) > 0)
+ zoom_thread = nr_options++;
+
+ if (dso != NULL &&
+ asprintf(&options[nr_options], "Zoom %s %s DSO",
+ (dso_filter ? "out of" : "into"),
+ (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
+ zoom_dso = nr_options++;
+
+ if (browser->selection->map != NULL &&
+ asprintf(&options[nr_options], "Browse map details") > 0)
+ browse_map = nr_options++;
+
+ options[nr_options++] = (char *)"Exit";
+
+ choice = ui__popup_menu(nr_options, options);
+
+ for (i = 0; i < nr_options - 1; ++i)
+ free(options[i]);
+
+ if (choice == nr_options - 1)
+ break;
+
+ if (choice == -1)
+ continue;
+
+ if (choice == annotate) {
+ struct hist_entry *he;
+do_annotate:
+ if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
+ browser->selection->map->dso->annotate_warned = 1;
+ ui_helpline__puts("No vmlinux file found, can't "
+ "annotate with just a "
+ "kallsyms file");
+ continue;
+ }
+
+ he = hist_browser__selected_entry(browser);
+ if (he == NULL)
+ continue;
+
+ hist_entry__tui_annotate(he);
+ } else if (choice == browse_map)
+ map__browse(browser->selection->map);
+ else if (choice == zoom_dso) {
+zoom_dso:
+ if (dso_filter) {
+ pstack__remove(fstack, &dso_filter);
+zoom_out_dso:
+ ui_helpline__pop();
+ dso_filter = NULL;
+ } else {
+ if (dso == NULL)
+ continue;
+ ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
+ dso->kernel ? "the Kernel" : dso->short_name);
+ dso_filter = dso;
+ pstack__push(fstack, &dso_filter);
+ }
+ hists__filter_by_dso(self, dso_filter);
+ hists__browser_title(self, msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
+ hist_browser__reset(browser);
+ } else if (choice == zoom_thread) {
+zoom_thread:
+ if (thread_filter) {
+ pstack__remove(fstack, &thread_filter);
+zoom_out_thread:
+ ui_helpline__pop();
+ thread_filter = NULL;
+ } else {
+ ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
+ thread->comm_set ? thread->comm : "",
+ thread->pid);
+ thread_filter = thread;
+ pstack__push(fstack, &thread_filter);
+ }
+ hists__filter_by_thread(self, thread_filter);
+ hists__browser_title(self, msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
+ hist_browser__reset(browser);
+ }
+ }
+out_free_stack:
+ pstack__delete(fstack);
+out:
+ hist_browser__delete(browser);
+ return key;
+}
+
+int hists__tui_browse_tree(struct rb_root *self, const char *help)
+{
+ struct rb_node *first = rb_first(self), *nd = first, *next;
+ int key = 0;
+
+ while (nd) {
+ struct hists *hists = rb_entry(nd, struct hists, rb_node);
+ const char *ev_name = __event_name(hists->type, hists->config);
+
+ key = hists__browse(hists, help, ev_name);
+ switch (key) {
+ case NEWT_KEY_TAB:
+ next = rb_next(nd);
+ if (next)
+ nd = next;
+ break;
+ case NEWT_KEY_UNTAB:
+ if (nd == first)
+ continue;
+ nd = rb_prev(nd);
+ default:
+ return key;
+ }
+ }
+
+ return key;
+}
diff --git a/smartt-perf/util/ui/browsers/map.c b/smartt-perf/util/ui/browsers/map.c
new file mode 100644
index 0000000..e515836
--- /dev/null
+++ b/smartt-perf/util/ui/browsers/map.c
@@ -0,0 +1,156 @@
+#include "../libslang.h"
+#include <elf.h>
+#include <inttypes.h>
+#include <sys/ttydefaults.h>
+#include <ctype.h>
+#include <string.h>
+#include <linux/bitops.h>
+#include "../../debug.h"
+#include "../../symbol.h"
+#include "../browser.h"
+#include "../helpline.h"
+#include "map.h"
+
+static int ui_entry__read(const char *title, char *bf, size_t size, int width)
+{
+ struct newtExitStruct es;
+ newtComponent form, entry;
+ const char *result;
+ int err = -1;
+
+ newtCenteredWindow(width, 1, title);
+ form = newtForm(NULL, NULL, 0);
+ if (form == NULL)
+ return -1;
+
+ entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
+ if (entry == NULL)
+ goto out_free_form;
+
+ newtFormAddComponent(form, entry);
+ newtFormAddHotKey(form, NEWT_KEY_ENTER);
+ newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
+ newtFormAddHotKey(form, NEWT_KEY_LEFT);
+ newtFormAddHotKey(form, CTRL('c'));
+ newtFormRun(form, &es);
+
+ if (result != NULL) {
+ strncpy(bf, result, size);
+ err = 0;
+ }
+out_free_form:
+ newtPopWindow();
+ newtFormDestroy(form);
+ return 0;
+}
+
+struct map_browser {
+ struct ui_browser b;
+ struct map *map;
+ u8 addrlen;
+};
+
+static void map_browser__write(struct ui_browser *self, void *nd, int row)
+{
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ struct map_browser *mb = container_of(self, struct map_browser, b);
+ bool current_entry = ui_browser__is_current_entry(self, row);
+ int width;
+
+ ui_browser__set_percent_color(self, 0, current_entry);
+ slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
+ mb->addrlen, sym->start, mb->addrlen, sym->end,
+ sym->binding == STB_GLOBAL ? 'g' :
+ sym->binding == STB_LOCAL ? 'l' : 'w');
+ width = self->width - ((mb->addrlen * 2) + 4);
+ if (width > 0)
+ slsmg_write_nstring(sym->name, width);
+}
+
+/* FIXME uber-kludgy, see comment on cmd_report... */
+static u32 *symbol__browser_index(struct symbol *self)
+{
+ return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
+}
+
+static int map_browser__search(struct map_browser *self)
+{
+ char target[512];
+ struct symbol *sym;
+ int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
+
+ if (err)
+ return err;
+
+ if (target[0] == '0' && tolower(target[1]) == 'x') {
+ u64 addr = strtoull(target, NULL, 16);
+ sym = map__find_symbol(self->map, addr, NULL);
+ } else
+ sym = map__find_symbol_by_name(self->map, target, NULL);
+
+ if (sym != NULL) {
+ u32 *idx = symbol__browser_index(sym);
+
+ self->b.top = &sym->rb_node;
+ self->b.index = self->b.top_idx = *idx;
+ } else
+ ui_helpline__fpush("%s not found!", target);
+
+ return 0;
+}
+
+static int map_browser__run(struct map_browser *self)
+{
+ int key;
+
+ if (ui_browser__show(&self->b, self->map->dso->long_name,
+ "Press <- or ESC to exit, %s / to search",
+ verbose ? "" : "restart with -v to use") < 0)
+ return -1;
+
+ if (verbose)
+ ui_browser__add_exit_key(&self->b, '/');
+
+ while (1) {
+ key = ui_browser__run(&self->b);
+
+ if (verbose && key == '/')
+ map_browser__search(self);
+ else
+ break;
+ }
+
+ ui_browser__hide(&self->b);
+ return key;
+}
+
+int map__browse(struct map *self)
+{
+ struct map_browser mb = {
+ .b = {
+ .entries = &self->dso->symbols[self->type],
+ .refresh = ui_browser__rb_tree_refresh,
+ .seek = ui_browser__rb_tree_seek,
+ .write = map_browser__write,
+ },
+ .map = self,
+ };
+ struct rb_node *nd;
+ char tmp[BITS_PER_LONG / 4];
+ u64 maxaddr = 0;
+
+ for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
+ struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+
+ if (maxaddr < pos->end)
+ maxaddr = pos->end;
+ if (verbose) {
+ u32 *idx = symbol__browser_index(pos);
+ *idx = mb.b.nr_entries;
+ }
+ ++mb.b.nr_entries;
+ }
+
+ mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
+ return map_browser__run(&mb);
+}
diff --git a/smartt-perf/util/ui/browsers/map.h b/smartt-perf/util/ui/browsers/map.h
new file mode 100644
index 0000000..df8581a
--- /dev/null
+++ b/smartt-perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
+#ifndef _PERF_UI_MAP_BROWSER_H_
+#define _PERF_UI_MAP_BROWSER_H_ 1
+struct map;
+
+int map__browse(struct map *self);
+#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/smartt-perf/util/ui/helpline.c b/smartt-perf/util/ui/helpline.c
new file mode 100644
index 0000000..8d79daa
--- /dev/null
+++ b/smartt-perf/util/ui/helpline.c
@@ -0,0 +1,69 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <newt.h>
+
+#include "../debug.h"
+#include "helpline.h"
+
+void ui_helpline__pop(void)
+{
+ newtPopHelpLine();
+}
+
+void ui_helpline__push(const char *msg)
+{
+ newtPushHelpLine(msg);
+}
+
+void ui_helpline__vpush(const char *fmt, va_list ap)
+{
+ char *s;
+
+ if (vasprintf(&s, fmt, ap) < 0)
+ vfprintf(stderr, fmt, ap);
+ else {
+ ui_helpline__push(s);
+ free(s);
+ }
+}
+
+void ui_helpline__fpush(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ ui_helpline__vpush(fmt, ap);
+ va_end(ap);
+}
+
+void ui_helpline__puts(const char *msg)
+{
+ ui_helpline__pop();
+ ui_helpline__push(msg);
+}
+
+void ui_helpline__init(void)
+{
+ ui_helpline__puts(" ");
+}
+
+char ui_helpline__last_msg[1024];
+
+int ui_helpline__show_help(const char *format, va_list ap)
+{
+ int ret;
+ static int backlog;
+
+ ret = vsnprintf(ui_helpline__last_msg + backlog,
+ sizeof(ui_helpline__last_msg) - backlog, format, ap);
+ backlog += ret;
+
+ if (ui_helpline__last_msg[backlog - 1] == '\n') {
+ ui_helpline__puts(ui_helpline__last_msg);
+ newtRefresh();
+ backlog = 0;
+ }
+
+ return ret;
+}
diff --git a/smartt-perf/util/ui/helpline.h b/smartt-perf/util/ui/helpline.h
new file mode 100644
index 0000000..ab6028d
--- /dev/null
+++ b/smartt-perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
+#ifndef _PERF_UI_HELPLINE_H_
+#define _PERF_UI_HELPLINE_H_ 1
+
+void ui_helpline__init(void);
+void ui_helpline__pop(void);
+void ui_helpline__push(const char *msg);
+void ui_helpline__vpush(const char *fmt, va_list ap);
+void ui_helpline__fpush(const char *fmt, ...);
+void ui_helpline__puts(const char *msg);
+
+#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/smartt-perf/util/ui/libslang.h b/smartt-perf/util/ui/libslang.h
new file mode 100644
index 0000000..5623da8
--- /dev/null
+++ b/smartt-perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
+#ifndef _PERF_UI_SLANG_H_
+#define _PERF_UI_SLANG_H_ 1
+/*
+ * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
+ * the build if it isn't defined. Use the equivalent one that glibc
+ * has on features.h.
+ */
+#include <features.h>
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
+#endif
+#include <slang.h>
+
+#if SLANG_VERSION < 20104
+#define slsmg_printf(msg, args...) \
+ SLsmg_printf((char *)msg, ##args)
+#define slsmg_write_nstring(msg, len) \
+ SLsmg_write_nstring((char *)msg, len)
+#define sltt_set_color(obj, name, fg, bg) \
+ SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
+#else
+#define slsmg_printf SLsmg_printf
+#define slsmg_write_nstring SLsmg_write_nstring
+#define sltt_set_color SLtt_set_color
+#endif
+
+#endif /* _PERF_UI_SLANG_H_ */
diff --git a/smartt-perf/util/ui/progress.c b/smartt-perf/util/ui/progress.c
new file mode 100644
index 0000000..d7fc399
--- /dev/null
+++ b/smartt-perf/util/ui/progress.c
@@ -0,0 +1,60 @@
+#include <stdlib.h>
+#include <newt.h>
+#include "../cache.h"
+#include "progress.h"
+
+struct ui_progress {
+ newtComponent form, scale;
+};
+
+struct ui_progress *ui_progress__new(const char *title, u64 total)
+{
+ struct ui_progress *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ int cols;
+
+ if (use_browser <= 0)
+ return self;
+ newtGetScreenSize(&cols, NULL);
+ cols -= 4;
+ newtCenteredWindow(cols, 1, title);
+ self->form = newtForm(NULL, NULL, 0);
+ if (self->form == NULL)
+ goto out_free_self;
+ self->scale = newtScale(0, 0, cols, total);
+ if (self->scale == NULL)
+ goto out_free_form;
+ newtFormAddComponent(self->form, self->scale);
+ newtRefresh();
+ }
+
+ return self;
+
+out_free_form:
+ newtFormDestroy(self->form);
+out_free_self:
+ free(self);
+ return NULL;
+}
+
+void ui_progress__update(struct ui_progress *self, u64 curr)
+{
+ /*
+ * FIXME: We should have a per UI backend way of showing progress,
+ * stdio will just show a percentage as NN%, etc.
+ */
+ if (use_browser <= 0)
+ return;
+ newtScaleSet(self->scale, curr);
+ newtRefresh();
+}
+
+void ui_progress__delete(struct ui_progress *self)
+{
+ if (use_browser > 0) {
+ newtFormDestroy(self->form);
+ newtPopWindow();
+ }
+ free(self);
+}
diff --git a/smartt-perf/util/ui/progress.h b/smartt-perf/util/ui/progress.h
new file mode 100644
index 0000000..a3820a0
--- /dev/null
+++ b/smartt-perf/util/ui/progress.h
@@ -0,0 +1,11 @@
+#ifndef _PERF_UI_PROGRESS_H_
+#define _PERF_UI_PROGRESS_H_ 1
+
+struct ui_progress;
+
+struct ui_progress *ui_progress__new(const char *title, u64 total);
+void ui_progress__delete(struct ui_progress *self);
+
+void ui_progress__update(struct ui_progress *self, u64 curr);
+
+#endif
diff --git a/smartt-perf/util/ui/setup.c b/smartt-perf/util/ui/setup.c
new file mode 100644
index 0000000..6620850
--- /dev/null
+++ b/smartt-perf/util/ui/setup.c
@@ -0,0 +1,42 @@
+#include <newt.h>
+#include <signal.h>
+#include <stdbool.h>
+
+#include "../cache.h"
+#include "../debug.h"
+#include "browser.h"
+#include "helpline.h"
+
+static void newt_suspend(void *d __used)
+{
+ newtSuspend();
+ raise(SIGTSTP);
+ newtResume();
+}
+
+void setup_browser(void)
+{
+ if (!isatty(1) || !use_browser || dump_trace) {
+ use_browser = 0;
+ setup_pager();
+ return;
+ }
+
+ use_browser = 1;
+ newtInit();
+ newtCls();
+ newtSetSuspendCallback(newt_suspend, NULL);
+ ui_helpline__init();
+ ui_browser__init();
+}
+
+void exit_browser(bool wait_for_ok)
+{
+ if (use_browser > 0) {
+ if (wait_for_ok) {
+ char title[] = "Fatal Error", ok[] = "Ok";
+ newtWinMessage(title, ok, ui_helpline__last_msg);
+ }
+ newtFinished();
+ }
+}
diff --git a/smartt-perf/util/ui/util.c b/smartt-perf/util/ui/util.c
new file mode 100644
index 0000000..7b5a892
--- /dev/null
+++ b/smartt-perf/util/ui/util.c
@@ -0,0 +1,127 @@
+#include <newt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/ttydefaults.h>
+
+#include "../cache.h"
+#include "../debug.h"
+#include "browser.h"
+#include "helpline.h"
+#include "util.h"
+
+static void newt_form__set_exit_keys(newtComponent self)
+{
+ newtFormAddHotKey(self, NEWT_KEY_LEFT);
+ newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
+ newtFormAddHotKey(self, 'Q');
+ newtFormAddHotKey(self, 'q');
+ newtFormAddHotKey(self, CTRL('c'));
+}
+
+static newtComponent newt_form__new(void)
+{
+ newtComponent self = newtForm(NULL, NULL, 0);
+ if (self)
+ newt_form__set_exit_keys(self);
+ return self;
+}
+
+int ui__popup_menu(int argc, char * const argv[])
+{
+ struct newtExitStruct es;
+ int i, rc = -1, max_len = 5;
+ newtComponent listbox, form = newt_form__new();
+
+ if (form == NULL)
+ return -1;
+
+ listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
+ if (listbox == NULL)
+ goto out_destroy_form;
+
+ newtFormAddComponent(form, listbox);
+
+ for (i = 0; i < argc; ++i) {
+ int len = strlen(argv[i]);
+ if (len > max_len)
+ max_len = len;
+ if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
+ goto out_destroy_form;
+ }
+
+ newtCenteredWindow(max_len, argc, NULL);
+ newtFormRun(form, &es);
+ rc = newtListboxGetCurrent(listbox) - NULL;
+ if (es.reason == NEWT_EXIT_HOTKEY)
+ rc = -1;
+ newtPopWindow();
+out_destroy_form:
+ newtFormDestroy(form);
+ return rc;
+}
+
+int ui__help_window(const char *text)
+{
+ struct newtExitStruct es;
+ newtComponent tb, form = newt_form__new();
+ int rc = -1;
+ int max_len = 0, nr_lines = 0;
+ const char *t;
+
+ if (form == NULL)
+ return -1;
+
+ t = text;
+ while (1) {
+ const char *sep = strchr(t, '\n');
+ int len;
+
+ if (sep == NULL)
+ sep = strchr(t, '\0');
+ len = sep - t;
+ if (max_len < len)
+ max_len = len;
+ ++nr_lines;
+ if (*sep == '\0')
+ break;
+ t = sep + 1;
+ }
+
+ tb = newtTextbox(0, 0, max_len, nr_lines, 0);
+ if (tb == NULL)
+ goto out_destroy_form;
+
+ newtTextboxSetText(tb, text);
+ newtFormAddComponent(form, tb);
+ newtCenteredWindow(max_len, nr_lines, NULL);
+ newtFormRun(form, &es);
+ newtPopWindow();
+ rc = 0;
+out_destroy_form:
+ newtFormDestroy(form);
+ return rc;
+}
+
+static const char yes[] = "Yes", no[] = "No",
+ warning_str[] = "Warning!", ok[] = "Ok";
+
+bool ui__dialog_yesno(const char *msg)
+{
+ /* newtWinChoice should really be accepting const char pointers... */
+ return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
+}
+
+void ui__warning(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ if (use_browser > 0)
+ newtWinMessagev((char *)warning_str, (char *)ok,
+ (char *)format, args);
+ else
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
diff --git a/smartt-perf/util/ui/util.h b/smartt-perf/util/ui/util.h
new file mode 100644
index 0000000..afcbc1d
--- /dev/null
+++ b/smartt-perf/util/ui/util.h
@@ -0,0 +1,10 @@
+#ifndef _PERF_UI_UTIL_H_
+#define _PERF_UI_UTIL_H_ 1
+
+#include <stdbool.h>
+
+int ui__popup_menu(int argc, char * const argv[]);
+int ui__help_window(const char *text);
+bool ui__dialog_yesno(const char *msg);
+
+#endif /* _PERF_UI_UTIL_H_ */
diff --git a/smartt-perf/util/usage.c b/smartt-perf/util/usage.c
new file mode 100644
index 0000000..e16bf9a
--- /dev/null
+++ b/smartt-perf/util/usage.c
@@ -0,0 +1,80 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "util.h"
+
+static void report(const char *prefix, const char *err, va_list params)
+{
+ char msg[1024];
+ vsnprintf(msg, sizeof(msg), err, params);
+ fprintf(stderr, " %s%s\n", prefix, msg);
+}
+
+static NORETURN void usage_builtin(const char *err)
+{
+ fprintf(stderr, "\n Usage: %s\n", err);
+ exit(129);
+}
+
+static NORETURN void die_builtin(const char *err, va_list params)
+{
+ report(" Fatal: ", err, params);
+ exit(128);
+}
+
+static void error_builtin(const char *err, va_list params)
+{
+ report(" Error: ", err, params);
+}
+
+static void warn_builtin(const char *warn, va_list params)
+{
+ report(" Warning: ", warn, params);
+}
+
+/* If we are in a dlopen()ed .so write to a global variable would segfault
+ * (ugh), so keep things static. */
+static void (*usage_routine)(const char *err) NORETURN = usage_builtin;
+static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin;
+static void (*error_routine)(const char *err, va_list params) = error_builtin;
+static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
+
+void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN)
+{
+ die_routine = routine;
+}
+
+void usage(const char *err)
+{
+ usage_routine(err);
+}
+
+void die(const char *err, ...)
+{
+ va_list params;
+
+ va_start(params, err);
+ die_routine(err, params);
+ va_end(params);
+}
+
+int error(const char *err, ...)
+{
+ va_list params;
+
+ va_start(params, err);
+ error_routine(err, params);
+ va_end(params);
+ return -1;
+}
+
+void warning(const char *warn, ...)
+{
+ va_list params;
+
+ va_start(params, warn);
+ warn_routine(warn, params);
+ va_end(params);
+}
diff --git a/smartt-perf/util/util.c b/smartt-perf/util/util.c
new file mode 100644
index 0000000..5b3ea49
--- /dev/null
+++ b/smartt-perf/util/util.c
@@ -0,0 +1,133 @@
+#include "util.h"
+#include <sys/mman.h>
+
+int mkdir_p(char *path, mode_t mode)
+{
+ struct stat st;
+ int err;
+ char *d = path;
+
+ if (*d != '/')
+ return -1;
+
+ if (stat(path, &st) == 0)
+ return 0;
+
+ while (*++d == '/');
+
+ while ((d = strchr(d, '/'))) {
+ *d = '\0';
+ err = stat(path, &st) && mkdir(path, mode);
+ *d++ = '/';
+ if (err)
+ return -1;
+ while (*d == '/')
+ ++d;
+ }
+ return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
+}
+
+static int slow_copyfile(const char *from, const char *to)
+{
+ int err = 0;
+ char *line = NULL;
+ size_t n;
+ FILE *from_fp = fopen(from, "r"), *to_fp;
+
+ if (from_fp == NULL)
+ goto out;
+
+ to_fp = fopen(to, "w");
+ if (to_fp == NULL)
+ goto out_fclose_from;
+
+ while (getline(&line, &n, from_fp) > 0)
+ if (fputs(line, to_fp) == EOF)
+ goto out_fclose_to;
+ err = 0;
+out_fclose_to:
+ fclose(to_fp);
+ free(line);
+out_fclose_from:
+ fclose(from_fp);
+out:
+ return err;
+}
+
+int copyfile(const char *from, const char *to)
+{
+ int fromfd, tofd;
+ struct stat st;
+ void *addr;
+ int err = -1;
+
+ if (stat(from, &st))
+ goto out;
+
+ if (st.st_size == 0) /* /proc? do it slowly... */
+ return slow_copyfile(from, to);
+
+ fromfd = open(from, O_RDONLY);
+ if (fromfd < 0)
+ goto out;
+
+ tofd = creat(to, 0755);
+ if (tofd < 0)
+ goto out_close_from;
+
+ addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0);
+ if (addr == MAP_FAILED)
+ goto out_close_to;
+
+ if (write(tofd, addr, st.st_size) == st.st_size)
+ err = 0;
+
+ munmap(addr, st.st_size);
+out_close_to:
+ close(tofd);
+ if (err)
+ unlink(to);
+out_close_from:
+ close(fromfd);
+out:
+ return err;
+}
+
+unsigned long convert_unit(unsigned long value, char *unit)
+{
+ *unit = ' ';
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'K';
+ }
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'M';
+ }
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'G';
+ }
+
+ return value;
+}
+
+int readn(int fd, void *buf, size_t n)
+{
+ void *buf_start = buf;
+
+ while (n) {
+ int ret = read(fd, buf, n);
+
+ if (ret <= 0)
+ return ret;
+
+ n -= ret;
+ buf += ret;
+ }
+
+ return buf - buf_start;
+}
diff --git a/smartt-perf/util/util.h b/smartt-perf/util/util.h
new file mode 100644
index 0000000..0f64d47
--- /dev/null
+++ b/smartt-perf/util/util.h
@@ -0,0 +1,273 @@
+#ifndef GIT_COMPAT_UTIL_H
+#define GIT_COMPAT_UTIL_H
+
+#define _FILE_OFFSET_BITS 64
+
+#ifndef FLEX_ARRAY
+/*
+ * See if our compiler is known to support flexible array members.
+ */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARRAY /* empty */
+#elif defined(__GNUC__)
+# if (__GNUC__ >= 3)
+# define FLEX_ARRAY /* empty */
+# else
+# define FLEX_ARRAY 0 /* older GNU extension */
+# endif
+#endif
+
+/*
+ * Otherwise, default to safer but a bit wasteful traditional style
+ */
+#ifndef FLEX_ARRAY
+# define FLEX_ARRAY 1
+#endif
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+#ifdef __GNUC__
+#define TYPEOF(x) (__typeof__(x))
+#else
+#define TYPEOF(x)
+#endif
+
+#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
+#define HAS_MULTI_BITS(i) ((i) & ((i) - 1)) /* checks if an integer has more than 1 bit set */
+
+/* Approximation of the length of the decimal representation of this type. */
+#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
+
+#define _ALL_SOURCE 1
+#define _GNU_SOURCE 1
+#define _BSD_SOURCE 1
+#define HAS_BOOL
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#ifndef NO_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <inttypes.h>
+#include "linux/magic.h"
+#include "types.h"
+#include <sys/ttydefaults.h>
+
+#ifndef NO_ICONV
+#include <iconv.h>
+#endif
+
+extern const char *graph_line;
+extern const char *graph_dotted_line;
+extern char buildid_dir[];
+
+/* On most systems <limits.h> would have given us this, but
+ * not on some systems (e.g. GNU/Hurd).
+ */
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#ifndef PRIuMAX
+#define PRIuMAX "llu"
+#endif
+
+#ifndef PRIu32
+#define PRIu32 "u"
+#endif
+
+#ifndef PRIx32
+#define PRIx32 "x"
+#endif
+
+#ifndef PATH_SEP
+#define PATH_SEP ':'
+#endif
+
+#ifndef STRIP_EXTENSION
+#define STRIP_EXTENSION ""
+#endif
+
+#ifndef has_dos_drive_prefix
+#define has_dos_drive_prefix(path) 0
+#endif
+
+#ifndef is_dir_sep
+#define is_dir_sep(c) ((c) == '/')
+#endif
+
+#ifdef __GNUC__
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#ifndef __attribute__
+#define __attribute__(x)
+#endif
+#endif
+
+/* General helper functions */
+extern void usage(const char *err) NORETURN;
+extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
+extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+
+#include "linux/stringify.h"
+
+#define DIE_IF(cnd) \
+ do { if (cnd) \
+ die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \
+ __stringify(cnd) "\n"); \
+ } while (0)
+
+
+extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
+
+extern int prefixcmp(const char *str, const char *prefix);
+extern void set_buildid_dir(void);
+extern void disable_buildid_cache(void);
+
+static inline const char *skip_prefix(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ(2, 1)
+#define HAVE_STRCHRNUL
+#endif
+#endif
+
+#ifndef HAVE_STRCHRNUL
+#define strchrnul gitstrchrnul
+static inline char *gitstrchrnul(const char *s, int c)
+{
+ while (*s && *s != c)
+ s++;
+ return (char *)s;
+}
+#endif
+
+/*
+ * Wrappers:
+ */
+extern char *xstrdup(const char *str);
+extern void *xrealloc(void *ptr, size_t size) __attribute__((weak));
+
+
+static inline void *zalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+static inline int has_extension(const char *filename, const char *ext)
+{
+ size_t len = strlen(filename);
+ size_t extlen = strlen(ext);
+
+ return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
+}
+
+/* Sane ctype - no locale, and works with signed chars */
+#undef isascii
+#undef isspace
+#undef isdigit
+#undef isxdigit
+#undef isalpha
+#undef isprint
+#undef isalnum
+#undef tolower
+#undef toupper
+
+extern unsigned char sane_ctype[256];
+#define GIT_SPACE 0x01
+#define GIT_DIGIT 0x02
+#define GIT_ALPHA 0x04
+#define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
+#define GIT_PRINT_EXTRA 0x20
+#define GIT_PRINT 0x3E
+#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define isascii(x) (((x) & ~0x7f) == 0)
+#define isspace(x) sane_istest(x,GIT_SPACE)
+#define isdigit(x) sane_istest(x,GIT_DIGIT)
+#define isxdigit(x) \
+ (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G')
+#define isalpha(x) sane_istest(x,GIT_ALPHA)
+#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define isprint(x) sane_istest(x,GIT_PRINT)
+#define tolower(x) sane_case((unsigned char)(x), 0x20)
+#define toupper(x) sane_case((unsigned char)(x), 0)
+
+static inline int sane_case(int x, int high)
+{
+ if (sane_istest(x, GIT_ALPHA))
+ x = (x & ~0x20) | high;
+ return x;
+}
+
+#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
+# define FORCE_DIR_SET_GID S_ISGID
+#else
+# define FORCE_DIR_SET_GID 0
+#endif
+
+#ifdef NO_NSEC
+#undef USE_NSEC
+#define ST_CTIME_NSEC(st) 0
+#define ST_MTIME_NSEC(st) 0
+#else
+#ifdef USE_ST_TIMESPEC
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
+#else
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
+#endif
+#endif
+
+int mkdir_p(char *path, mode_t mode);
+int copyfile(const char *from, const char *to);
+
+s64 perf_atoll(const char *str);
+char **argv_split(const char *str, int *argcp);
+void argv_free(char **argv);
+bool strglobmatch(const char *str, const char *pat);
+bool strlazymatch(const char *str, const char *pat);
+unsigned long convert_unit(unsigned long value, char *unit);
+int readn(int fd, void *buf, size_t size);
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+#endif
diff --git a/smartt-perf/util/values.c b/smartt-perf/util/values.c
new file mode 100644
index 0000000..bdd3347
--- /dev/null
+++ b/smartt-perf/util/values.c
@@ -0,0 +1,231 @@
+#include <stdlib.h>
+
+#include "util.h"
+#include "values.h"
+
+void perf_read_values_init(struct perf_read_values *values)
+{
+ values->threads_max = 16;
+ values->pid = malloc(values->threads_max * sizeof(*values->pid));
+ values->tid = malloc(values->threads_max * sizeof(*values->tid));
+ values->value = malloc(values->threads_max * sizeof(*values->value));
+ if (!values->pid || !values->tid || !values->value)
+ die("failed to allocate read_values threads arrays");
+ values->threads = 0;
+
+ values->counters_max = 16;
+ values->counterrawid = malloc(values->counters_max
+ * sizeof(*values->counterrawid));
+ values->countername = malloc(values->counters_max
+ * sizeof(*values->countername));
+ if (!values->counterrawid || !values->countername)
+ die("failed to allocate read_values counters arrays");
+ values->counters = 0;
+}
+
+void perf_read_values_destroy(struct perf_read_values *values)
+{
+ int i;
+
+ if (!values->threads_max || !values->counters_max)
+ return;
+
+ for (i = 0; i < values->threads; i++)
+ free(values->value[i]);
+ free(values->pid);
+ free(values->tid);
+ free(values->counterrawid);
+ for (i = 0; i < values->counters; i++)
+ free(values->countername[i]);
+ free(values->countername);
+}
+
+static void perf_read_values__enlarge_threads(struct perf_read_values *values)
+{
+ values->threads_max *= 2;
+ values->pid = realloc(values->pid,
+ values->threads_max * sizeof(*values->pid));
+ values->tid = realloc(values->tid,
+ values->threads_max * sizeof(*values->tid));
+ values->value = realloc(values->value,
+ values->threads_max * sizeof(*values->value));
+ if (!values->pid || !values->tid || !values->value)
+ die("failed to enlarge read_values threads arrays");
+}
+
+static int perf_read_values__findnew_thread(struct perf_read_values *values,
+ u32 pid, u32 tid)
+{
+ int i;
+
+ for (i = 0; i < values->threads; i++)
+ if (values->pid[i] == pid && values->tid[i] == tid)
+ return i;
+
+ if (values->threads == values->threads_max)
+ perf_read_values__enlarge_threads(values);
+
+ i = values->threads++;
+ values->pid[i] = pid;
+ values->tid[i] = tid;
+ values->value[i] = malloc(values->counters_max * sizeof(**values->value));
+ if (!values->value[i])
+ die("failed to allocate read_values counters array");
+
+ return i;
+}
+
+static void perf_read_values__enlarge_counters(struct perf_read_values *values)
+{
+ int i;
+
+ values->counters_max *= 2;
+ values->counterrawid = realloc(values->counterrawid,
+ values->counters_max * sizeof(*values->counterrawid));
+ values->countername = realloc(values->countername,
+ values->counters_max * sizeof(*values->countername));
+ if (!values->counterrawid || !values->countername)
+ die("failed to enlarge read_values counters arrays");
+
+ for (i = 0; i < values->threads; i++) {
+ values->value[i] = realloc(values->value[i],
+ values->counters_max * sizeof(**values->value));
+ if (!values->value[i])
+ die("failed to enlarge read_values counters arrays");
+ }
+}
+
+static int perf_read_values__findnew_counter(struct perf_read_values *values,
+ u64 rawid, const char *name)
+{
+ int i;
+
+ for (i = 0; i < values->counters; i++)
+ if (values->counterrawid[i] == rawid)
+ return i;
+
+ if (values->counters == values->counters_max)
+ perf_read_values__enlarge_counters(values);
+
+ i = values->counters++;
+ values->counterrawid[i] = rawid;
+ values->countername[i] = strdup(name);
+
+ return i;
+}
+
+void perf_read_values_add_value(struct perf_read_values *values,
+ u32 pid, u32 tid,
+ u64 rawid, const char *name, u64 value)
+{
+ int tindex, cindex;
+
+ tindex = perf_read_values__findnew_thread(values, pid, tid);
+ cindex = perf_read_values__findnew_counter(values, rawid, name);
+
+ values->value[tindex][cindex] = value;
+}
+
+static void perf_read_values__display_pretty(FILE *fp,
+ struct perf_read_values *values)
+{
+ int i, j;
+ int pidwidth, tidwidth;
+ int *counterwidth;
+
+ counterwidth = malloc(values->counters * sizeof(*counterwidth));
+ if (!counterwidth)
+ die("failed to allocate counterwidth array");
+ tidwidth = 3;
+ pidwidth = 3;
+ for (j = 0; j < values->counters; j++)
+ counterwidth[j] = strlen(values->countername[j]);
+ for (i = 0; i < values->threads; i++) {
+ int width;
+
+ width = snprintf(NULL, 0, "%d", values->pid[i]);
+ if (width > pidwidth)
+ pidwidth = width;
+ width = snprintf(NULL, 0, "%d", values->tid[i]);
+ if (width > tidwidth)
+ tidwidth = width;
+ for (j = 0; j < values->counters; j++) {
+ width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
+ if (width > counterwidth[j])
+ counterwidth[j] = width;
+ }
+ }
+
+ fprintf(fp, "# %*s %*s", pidwidth, "PID", tidwidth, "TID");
+ for (j = 0; j < values->counters; j++)
+ fprintf(fp, " %*s", counterwidth[j], values->countername[j]);
+ fprintf(fp, "\n");
+
+ for (i = 0; i < values->threads; i++) {
+ fprintf(fp, " %*d %*d", pidwidth, values->pid[i],
+ tidwidth, values->tid[i]);
+ for (j = 0; j < values->counters; j++)
+ fprintf(fp, " %*" PRIu64,
+ counterwidth[j], values->value[i][j]);
+ fprintf(fp, "\n");
+ }
+ free(counterwidth);
+}
+
+static void perf_read_values__display_raw(FILE *fp,
+ struct perf_read_values *values)
+{
+ int width, pidwidth, tidwidth, namewidth, rawwidth, countwidth;
+ int i, j;
+
+ tidwidth = 3; /* TID */
+ pidwidth = 3; /* PID */
+ namewidth = 4; /* "Name" */
+ rawwidth = 3; /* "Raw" */
+ countwidth = 5; /* "Count" */
+
+ for (i = 0; i < values->threads; i++) {
+ width = snprintf(NULL, 0, "%d", values->pid[i]);
+ if (width > pidwidth)
+ pidwidth = width;
+ width = snprintf(NULL, 0, "%d", values->tid[i]);
+ if (width > tidwidth)
+ tidwidth = width;
+ }
+ for (j = 0; j < values->counters; j++) {
+ width = strlen(values->countername[j]);
+ if (width > namewidth)
+ namewidth = width;
+ width = snprintf(NULL, 0, "%" PRIx64, values->counterrawid[j]);
+ if (width > rawwidth)
+ rawwidth = width;
+ }
+ for (i = 0; i < values->threads; i++) {
+ for (j = 0; j < values->counters; j++) {
+ width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
+ if (width > countwidth)
+ countwidth = width;
+ }
+ }
+
+ fprintf(fp, "# %*s %*s %*s %*s %*s\n",
+ pidwidth, "PID", tidwidth, "TID",
+ namewidth, "Name", rawwidth, "Raw",
+ countwidth, "Count");
+ for (i = 0; i < values->threads; i++)
+ for (j = 0; j < values->counters; j++)
+ fprintf(fp, " %*d %*d %*s %*" PRIx64 " %*" PRIu64,
+ pidwidth, values->pid[i],
+ tidwidth, values->tid[i],
+ namewidth, values->countername[j],
+ rawwidth, values->counterrawid[j],
+ countwidth, values->value[i][j]);
+}
+
+void perf_read_values_display(FILE *fp, struct perf_read_values *values, int raw)
+{
+ if (raw)
+ perf_read_values__display_raw(fp, values);
+ else
+ perf_read_values__display_pretty(fp, values);
+}
diff --git a/smartt-perf/util/values.h b/smartt-perf/util/values.h
new file mode 100644
index 0000000..2fa967e
--- /dev/null
+++ b/smartt-perf/util/values.h
@@ -0,0 +1,27 @@
+#ifndef __PERF_VALUES_H
+#define __PERF_VALUES_H
+
+#include "types.h"
+
+struct perf_read_values {
+ int threads;
+ int threads_max;
+ u32 *pid, *tid;
+ int counters;
+ int counters_max;
+ u64 *counterrawid;
+ char **countername;
+ u64 **value;
+};
+
+void perf_read_values_init(struct perf_read_values *values);
+void perf_read_values_destroy(struct perf_read_values *values);
+
+void perf_read_values_add_value(struct perf_read_values *values,
+ u32 pid, u32 tid,
+ u64 rawid, const char *name, u64 value);
+
+void perf_read_values_display(FILE *fp, struct perf_read_values *values,
+ int raw);
+
+#endif /* __PERF_VALUES_H */
diff --git a/smartt-perf/util/wrapper.c b/smartt-perf/util/wrapper.c
new file mode 100644
index 0000000..73e900e
--- /dev/null
+++ b/smartt-perf/util/wrapper.c
@@ -0,0 +1,40 @@
+/*
+ * Various trivial helper wrappers around standard functions
+ */
+#include "cache.h"
+
+/*
+ * There's no pack memory to release - but stay close to the Git
+ * version so wrap this away:
+ */
+static inline void release_pack_memory(size_t size __used, int flag __used)
+{
+}
+
+char *xstrdup(const char *str)
+{
+ char *ret = strdup(str);
+ if (!ret) {
+ release_pack_memory(strlen(str) + 1, -1);
+ ret = strdup(str);
+ if (!ret)
+ die("Out of memory, strdup failed");
+ }
+ return ret;
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+ void *ret = realloc(ptr, size);
+ if (!ret && !size)
+ ret = realloc(ptr, 1);
+ if (!ret) {
+ release_pack_memory(size, -1);
+ ret = realloc(ptr, size);
+ if (!ret && !size)
+ ret = realloc(ptr, 1);
+ if (!ret)
+ die("Out of memory, realloc failed");
+ }
+ return ret;
+}
diff --git a/smartt-perf/util/xyarray.c b/smartt-perf/util/xyarray.c
new file mode 100644
index 0000000..22afbf6
--- /dev/null
+++ b/smartt-perf/util/xyarray.c
@@ -0,0 +1,20 @@
+#include "xyarray.h"
+#include "util.h"
+
+struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
+{
+ size_t row_size = ylen * entry_size;
+ struct xyarray *xy = zalloc(sizeof(*xy) + xlen * row_size);
+
+ if (xy != NULL) {
+ xy->entry_size = entry_size;
+ xy->row_size = row_size;
+ }
+
+ return xy;
+}
+
+void xyarray__delete(struct xyarray *xy)
+{
+ free(xy);
+}
diff --git a/smartt-perf/util/xyarray.h b/smartt-perf/util/xyarray.h
new file mode 100644
index 0000000..c488a07
--- /dev/null
+++ b/smartt-perf/util/xyarray.h
@@ -0,0 +1,20 @@
+#ifndef _PERF_XYARRAY_H_
+#define _PERF_XYARRAY_H_ 1
+
+#include <sys/types.h>
+
+struct xyarray {
+ size_t row_size;
+ size_t entry_size;
+ char contents[];
+};
+
+struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size);
+void xyarray__delete(struct xyarray *xy);
+
+static inline void *xyarray__entry(struct xyarray *xy, int x, int y)
+{
+ return &xy->contents[x * xy->row_size + y * xy->entry_size];
+}
+
+#endif /* _PERF_XYARRAY_H_ */
diff --git a/smartt-player/Makefile b/smartt-player/Makefile
new file mode 100644
index 0000000..da1b062
--- /dev/null
+++ b/smartt-player/Makefile
@@ -0,0 +1,26 @@
+CC := gcc
+
+GST_LIBS := $(shell pkg-config --libs gstreamer-0.10 gstreamer-interfaces-0.10)
+GST_CFLAGS := $(shell pkg-config --cflags gstreamer-0.10 gstreamer-interfaces-0.10)
+GTK_LIBS := $(shell pkg-config --libs gtk+-2.0)
+GTK_CFLAGS := $(shell pkg-config --cflags gtk+-2.0)
+
+smartt-player: main.o media_player.o
+smartt-player: CFLAGS := $(CFLAGS) $(GTK_CFLAGS) $(GST_CFLAGS)
+smartt-player: LIBS := $(LIBS) $(GTK_LIBS) $(GST_LIBS)
+binaries += smartt-player
+
+all: $(binaries)
+
+$(binaries):
+ $(CC) $(LDFLAGS) $(LIBS) -o $@ $^
+
+%.o:: %.c
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+ rm -rf $(binaries)
+ find . -name "*.o" | xargs rm -rf
+
+install:
+ /usr/bin/install -D smartt-player ../bin/smartt-player
diff --git a/smartt-player/README b/smartt-player/README
new file mode 100644
index 0000000..07af199
--- /dev/null
+++ b/smartt-player/README
@@ -0,0 +1,12 @@
+Description: Gstreamer based Media Player displays avg-fps, curr fps, drop rate,
+renderred and dropped frame data at specified interval which is configurable.
+It also display width, height and Bitrate of file. Audio codec and video
+codec used for playback is also displayed. This complete metrics data is also
+sent to smart server for tracing.
+
+Usage: media_player [fps-update-interval] {video_file_path}
+
+ fps-update-interval : time-interval in Millisec to get metrics data
+ video_file_path : absolute path of video file e.g. /home/user/video.mp4
+
+Example: media_player 500 /home/user/video.mp4
diff --git a/smartt-player/main.c b/smartt-player/main.c
new file mode 100644
index 0000000..26d686a
--- /dev/null
+++ b/smartt-player/main.c
@@ -0,0 +1,89 @@
+/*
+* main.c
+*
+* Media Player - Gsteramer based media player to display metrics data
+*
+* This is a media player that displays avg-fps, curr fps, drop rate, renderred and
+* dropped frame data at specified interval which is configurable. It also display
+* widht, height and Bitrate of file. Audio codec and video codec used for playback
+* is also displayed. This complete metrics data is also sent to smart server for
+* tracing.
+*
+* Sample input:
+*
+* media_player {complete path of video file} such as:
+* media_player /home/user/video.mp4
+*
+* Sample output:
+*
+* Bitrate of file is 1206992
+* video_codec is MPEG-4 video
+* audio_codec is MPEG-1 layer 3
+* width x height is 1280 x 720
+*
+* Pid=3819 Rend=41 Dropped=1 Width=320 Height=240 Curfps=26.52 dps=0.00 avgfps=41.00
+* Pid=3819 Rend=53 Dropped=1 Width=320 Height=240 Curfps=23.46 dps=0.00 avgfps=26.50
+* Pid=3819 Rend=66 Dropped=1 Width=320 Height=240 Curfps=26.52 dps=0.00 avgfps=33.00
+*
+* Copyright (C) 2011, <-->@linaro.org>
+
+* Developed by:
+*
+* Vishal Raj <vishal.raj@linaro.org>
+* <vishal.raj@stericsson.com>
+*
+* Released under <Linaro 2010>
+*/
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include "media_player.h"
+
+static char *uri_to_play;
+static gint fps_update_interval=500;
+
+int
+
+main (int argc, char *argv[])
+{
+ gtk_init (&argc, &argv);
+
+ player_open();
+ player_init (&argc, &argv);
+
+ if (argc > 2)
+ {
+ fps_update_interval = atoi(argv[1]);
+ if (strchr (argv[2], ':'))
+ uri_to_play = g_strdup (argv[2]);
+ else
+ uri_to_play = g_strdup_printf ("file://%s", argv[2]);
+ }
+ else if (argc > 1)
+ {
+ if (strchr (argv[1], ':'))
+ uri_to_play = g_strdup (argv[1]);
+ else
+ uri_to_play = g_strdup_printf ("file://%s", argv[1]);
+ }
+ else
+ {
+ fprintf(stderr,"\nUsage: media_player [fps-update-interval] {video_file_path}\n");
+ fprintf(stderr,"\n\tfps-update-interval : time-interval in Millisec to get metrics data \n");
+ fprintf(stderr,"\tvideo_file_path : absolute path of video file e.g. /home/user/video.mp4\n");
+ fprintf(stderr,"\n\nExample: media_player 500 /home/user/video.mp4\n\n");
+ exit(0);
+ }
+
+ if (uri_to_play)
+ player_play (uri_to_play,fps_update_interval);
+
+ gtk_main ();
+
+ g_free (uri_to_play);
+
+ player_deinit ();
+
+ return 0;
+}
diff --git a/smartt-player/media_player.c b/smartt-player/media_player.c
new file mode 100644
index 0000000..d651e8e
--- /dev/null
+++ b/smartt-player/media_player.c
@@ -0,0 +1,246 @@
+/*
+* media_player.c
+*
+* Media Player - Gsteramer based media player to display metrics data
+*
+* This is a media player that displays avg-fps, curr fps, drop rate, rendered and
+* dropped frame data at specified interval which is configurable. It also display
+* width, height and Bitrate of file. Audio codec and video codec used for playback
+* is also displayed. This complete metrics data is also sent to smart server for
+* tracing.
+*
+* Sample output:
+*
+* Bitrate of file is 1206992
+* video_codec is MPEG-4 video
+* audio_codec is MPEG-1 layer 3
+* width x height is 1280 x 720
+*
+* Pid=3819 Rend=41 Dropped=1 Width=320 Height=240 Curfps=26.52 dps=0.00 avgfps=41.00
+* Pid=3819 Rend=53 Dropped=1 Width=320 Height=240 Curfps=23.46 dps=0.00 avgfps=26.50
+* Pid=3819 Rend=66 Dropped=1 Width=320 Height=240 Curfps=26.52 dps=0.00 avgfps=33.00
+*
+* Copyright (C) 2010
+
+* Author(s):
+*
+* Vishal Raj <vishal.raj@linaro.org>
+* <vishal.raj@stericsson.com>
+*
+* Released under GPL v2
+*/
+
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <string.h>
+#include "media_player.h"
+
+static GstElement *pipeline;
+static GstElement *bin;
+static GstElement *fpsdispsink;
+static gpointer data;
+static GstPad *pad;
+static GstStructure *gstr;
+static GstCaps *gcaps;
+static GstTagList *tag_list;
+static char *video_codec,*audio_codec;
+typedef struct {
+ pid_t pid;
+ guint rate;
+ guint width ;
+ guint height;
+ gdouble curfps;
+ gdouble dps;
+ gdouble avgfps;
+ unsigned long int dropped;
+ unsigned long int rendered;
+ char vcodec[64];
+ char acodec[64];
+}mm_data;
+
+static mm_data mmdata;
+static mm_data *mmdataptr;
+
+static gboolean
+
+fps_measurement_cb(GstElement *fpsdispsink,gdouble curfps,gdouble dps, gdouble avgfps)
+{
+ mmdataptr->curfps = curfps;
+ mmdataptr->dps = dps;
+ mmdataptr->avgfps = avgfps;
+
+ g_object_get (G_OBJECT (fpsdispsink), "frames-rendered", &mmdataptr->rendered, NULL);
+ g_object_get (G_OBJECT (fpsdispsink), "frames-dropped", &mmdataptr->dropped, NULL);
+
+ if(!mmdataptr->width)
+ {
+ pad = gst_element_get_pad(fpsdispsink,"sink");
+ if((gcaps = gst_pad_get_negotiated_caps (pad)) && (gstr = gst_caps_get_structure (gcaps, 0)))
+ {
+ gst_structure_get_int (gstr, "width", &mmdataptr->width);
+ gst_structure_get_int (gstr, "height", &mmdataptr->height);
+ fprintf(stderr," width x height is %d x %d\n ",mmdataptr->width,mmdataptr->height);
+ }
+ gst_caps_unref(gcaps);
+ }
+
+ fprintf(stderr,"Pid=%u Rend=%lu Dropped=%lu Width=%d Height=%d Curfps=%4.2f dps=%4.2f avgfps=%4.2f\n",
+ mmdata.pid,
+ (unsigned long int)mmdata.rendered,
+ (unsigned long int)mmdata.dropped,
+ mmdata.width,
+ mmdata.height,
+ mmdata.curfps,
+ mmdata.dps,
+ mmdata.avgfps);
+ player_send();
+}
+
+static gboolean
+bus_cb (GstBus *bus,
+ GstMessage *msg,
+ gpointer data)
+{
+ switch (GST_MESSAGE_TYPE (msg))
+ {
+ case GST_MESSAGE_EOS:
+ {
+ g_debug ("end-of-stream");
+ player_stop ();
+ exit(0);
+ //break;
+ }
+ case GST_MESSAGE_TAG :
+ {
+
+ gst_message_parse_tag (msg,&tag_list);
+ if ( gst_tag_list_get_uint(tag_list,GST_TAG_BITRATE,&mmdataptr->rate))
+ fprintf(stderr," Bitrate of file is %d \n",mmdataptr->rate);
+ if ( gst_tag_list_get_string(tag_list, GST_TAG_VIDEO_CODEC,&video_codec))
+ {
+ strcpy(mmdataptr->vcodec,video_codec);
+ fprintf(stderr," video_codec is %s \n",mmdataptr->vcodec);
+ }
+ if ( gst_tag_list_get_string(tag_list, GST_TAG_AUDIO_CODEC,&audio_codec))
+ {
+ strcpy(mmdataptr->acodec,audio_codec);
+ fprintf(stderr," audio_codec is %s \n",mmdataptr->acodec);
+ }
+ break;
+ }
+ case GST_MESSAGE_ERROR:
+ {
+ gchar *debug;
+ GError *err;
+
+ gst_message_parse_error (msg, &err, &debug);
+ g_free (debug);
+
+ g_warning ("Error: %s", err->message);
+ g_error_free (err);
+ exit(0);
+ // break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+
+void player_open(void)
+{
+ struct hostent *hp;
+
+ sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ server.sin_family = AF_INET;
+ server.sin_port = (4200);
+
+ hp = gethostbyname("127.0.0.1");
+ bcopy (hp->h_addr,&(server.sin_addr),hp->h_length);
+}
+
+void
+player_init (int *argc,
+ char **argv[])
+{
+ mmdataptr = &mmdata;
+ mmdataptr->pid = getpid();
+ gst_init (argc, argv);
+ player_send();
+}
+
+void
+player_play (const gchar *filename, const gint fps_update_interval)
+{
+ player_stop ();
+
+ pipeline = gst_pipeline_new ("gst-player");
+
+ bin = gst_element_factory_make ("playbin2", "bin");
+
+ fpsdispsink = gst_element_factory_make ("fpsdisplaysink", "fpsdispsink");
+
+ g_object_set (G_OBJECT (fpsdispsink), "signal-fps-measurements", TRUE,"text-overlay",TRUE,"fps-update-interval",fps_update_interval,"sync",TRUE, NULL);
+ g_signal_connect (fpsdispsink, "fps-measurements", G_CALLBACK (fps_measurement_cb), (gpointer) data);
+
+ g_object_set (G_OBJECT (bin), "video-sink", fpsdispsink, NULL);
+
+ {
+ GstBus *bus;
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_watch (bus, bus_cb, NULL);
+ gst_object_unref (bus);
+ }
+
+ {
+ gchar *uri;
+
+ if (gst_uri_is_valid (filename))
+ {
+ uri = g_strdup (filename);
+ }
+ else if (g_path_is_absolute (filename))
+ {
+ uri = g_filename_to_uri (filename, NULL, NULL);
+ }
+ else
+ {
+ gchar *tmp;
+ tmp = g_build_filename (g_get_current_dir (), filename, NULL);
+ uri = g_filename_to_uri (tmp, NULL, NULL);
+ g_free (tmp);
+ }
+
+ g_debug ("%s", uri);
+ g_object_set (G_OBJECT (bin), "uri", uri, NULL);
+ g_free (uri);
+ }
+ gst_bin_add (GST_BIN (pipeline), bin);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+}
+
+void
+player_send (void)
+{
+ sendto(sock_desc, (void *)&mmdata, sizeof(mmdata),0,(struct sockaddr *) &server, sizeof(server));
+}
+
+void
+player_stop (void)
+{
+ if (pipeline)
+ {
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+ pipeline = NULL;
+ }
+}
+
+
+void
+player_deinit (void)
+{
+}
diff --git a/smartt-player/media_player.h b/smartt-player/media_player.h
new file mode 100644
index 0000000..229752b
--- /dev/null
+++ b/smartt-player/media_player.h
@@ -0,0 +1,49 @@
+
+#ifndef MEDIA_PLAYER_H
+#define MEDIA_PLAYER_H
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <inttypes.h>
+
+
+int sock_desc;
+struct sockaddr_in server;
+
+void player_open(void);
+void player_init (int *argc, char **argv[]);
+void player_deinit (void);
+void player_play (const gchar *filename,const gint fps_update_interval);
+void player_send (void);
+void player_stop (void);
+
+
+#endif /* MEDIA_PLAYER_H */
diff --git a/smartt-server/Makefile b/smartt-server/Makefile
new file mode 100644
index 0000000..6ea552c
--- /dev/null
+++ b/smartt-server/Makefile
@@ -0,0 +1,10 @@
+
+CC = gcc
+
+CFLAGS = -Wall
+
+smartt-client: smartt-server.c
+ $(CC) smartt-server.c -g -o smartt-server $(CFLAGS)
+
+clean:
+ rm -f *.o smartt-server
diff --git a/smartt-server/README b/smartt-server/README
new file mode 100644
index 0000000..4411433
--- /dev/null
+++ b/smartt-server/README
@@ -0,0 +1,21 @@
+DESCRIPTION: SMARTT-SERVER extracts metrics cpu-cycles, instructions, branches, bus-cycles
+ and cache-misses from the perf utility. The tool also receives data from the
+ instrumented media-player and gets the data such as current-fps, average-fps,
+ renderred and dropped frames. The metrics-data is packeted and send to the
+ smartt-client and smartt-tracing for display and tracing.
+
+USAGE: smartt-server
+PROMPT [SMARTT-CLIENT] #
+
+COMMMAND(s) s or S (start)
+ r or R (run)
+
+
+[SMART-SERVER] # s
+
+[I] SMART Server Started : Success
+[I] Waiting Request from SMART Client : Waiting
+[I] Connected to SMART Client @ 10.199.16.141:5000 : Success
+[I] Waiting for user configuration from SMART Client : Waiting
+
+
diff --git a/smartt-server/perf b/smartt-server/perf
new file mode 120000
index 0000000..baabd57
--- /dev/null
+++ b/smartt-server/perf
@@ -0,0 +1 @@
+../smartt-perf/perf \ No newline at end of file
diff --git a/smartt-server/smartt-network-services.c b/smartt-server/smartt-network-services.c
new file mode 100644
index 0000000..809899c
--- /dev/null
+++ b/smartt-server/smartt-network-services.c
@@ -0,0 +1,146 @@
+/*
+ * smartt-network-services.c
+ *
+ * Network Configuration
+*/
+
+#include "smartt-network-services.h"
+#include "smartt-server-utils.h"
+
+unsigned int addr_len;
+
+int SMARTTserver_OpenSmarttClient_INPort()
+{
+
+ bool status;
+ char param_type[64];
+ unsigned int local_port;
+
+
+ FILE *f1 = fopen("/etc/smartt/smartt-server.conf", "r");
+ if (f1== NULL)
+ {
+ fprintf(stderr, "[E] Cannot open smart-server.conf in /etc/smartt/ \t\t: Error\n");
+ exit(1);
+ }
+
+ fscanf(f1,"%s %d\n",param_type, &local_port);
+
+ smartt_client_rx_sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ if (smartt_client_rx_sock_desc == -1)
+ {
+ fprintf(stderr,"Error opening the connection\n");
+ exit(1);
+ }
+
+ smartt_client_rx_server_addr.sin_family = AF_INET;
+ smartt_client_rx_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ smartt_client_rx_server_addr.sin_port = (local_port);
+
+ status = bind(smartt_client_rx_sock_desc,(struct sockaddr *)&smartt_client_rx_server_addr, sizeof(smartt_client_rx_server_addr));
+ if (status == -1)
+ {
+ fprintf(stderr, "Failed to bind the client\n");
+ exit(1);
+ }
+
+ addr_len = sizeof(struct sockaddr);
+ return(1);
+}
+
+int SMARTTserver_OpenSmarttClient_OUTPort(char* remote_host, unsigned int remote_port)
+{
+ struct hostent *hp;
+
+ smartt_client_tx_sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ if (smartt_client_tx_sock_desc == -1)
+ {
+ fprintf(stderr,"[E] Error opening Tx Server\t\t: Error\n");
+ exit(1);
+ }
+ smartt_client_tx_server_addr.sin_family = AF_INET;
+ smartt_client_tx_server_addr.sin_port = remote_port;
+ hp = gethostbyname(remote_host);
+ bcopy (hp->h_addr,&(smartt_client_tx_server_addr.sin_addr),hp->h_length);
+ return(1);
+}
+
+int SMARTTserver_OpenSmarttPerf_INPort()
+{
+ bool status;
+
+ smartt_perf_rx_sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ if (smartt_perf_rx_sock_desc == -1)
+ {
+ fprintf(stderr,"Error opening the connection\n");
+ exit(1);
+ }
+
+ smartt_perf_rx_server_addr.sin_family = AF_INET;
+ smartt_perf_rx_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ smartt_perf_rx_server_addr.sin_port = (SMARTT_PERF_LOCAL_PORT);
+
+ status = bind(smartt_perf_rx_sock_desc,(struct sockaddr *)&smartt_perf_rx_server_addr, sizeof(smartt_perf_rx_server_addr));
+ if (status == -1)
+ {
+ fprintf(stderr, "Failed to bind the client\n");
+ exit(1);
+ }
+
+ addr_len = sizeof(struct sockaddr);
+ return(1);
+}
+
+int SMARTTserver_OpenSmarttTop_INPort()
+{
+ bool status;
+
+ smartt_top_rx_sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ if (smartt_top_rx_sock_desc == -1)
+ {
+ fprintf(stderr,"Error opening the connection\n");
+ exit(1);
+ }
+
+ smartt_top_rx_server_addr.sin_family = AF_INET;
+ smartt_top_rx_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ smartt_top_rx_server_addr.sin_port = (SMARTT_TOP_LOCAL_PORT);
+
+ status = bind(smartt_top_rx_sock_desc,(struct sockaddr *)&smartt_top_rx_server_addr, sizeof(smartt_top_rx_server_addr));
+ if (status == -1)
+ {
+ fprintf(stderr, "Failed to bind the client\n");
+ exit(1);
+ }
+
+ addr_len = sizeof(struct sockaddr);
+ return(1);
+}
+
+int SMARTTserver_OpenSmarttPlayer_INPort()
+{
+ bool status;
+
+ smartt_player_rx_sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+ if (smartt_player_rx_sock_desc == -1)
+ {
+ fprintf(stderr,"Error opening the connection\n");
+ exit(1);
+ }
+
+ smartt_player_rx_server_addr.sin_family = AF_INET;
+ smartt_player_rx_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ smartt_player_rx_server_addr.sin_port = (SMARTT_PLAYER_LOCAL_PORT);
+
+ status = bind(smartt_player_rx_sock_desc,(struct sockaddr *)&smartt_player_rx_server_addr, sizeof(smartt_player_rx_server_addr));
+ if (status == -1)
+ {
+ fprintf(stderr, "Failed to bind the client\n");
+ exit(1);
+ }
+
+ addr_len = sizeof(struct sockaddr);
+ return(1);
+}
+
+
diff --git a/smartt-server/smartt-network-services.h b/smartt-server/smartt-network-services.h
new file mode 100644
index 0000000..bdda887
--- /dev/null
+++ b/smartt-server/smartt-network-services.h
@@ -0,0 +1,24 @@
+#ifndef __SMARTT_NETWORK_SERVICES_H
+#define __SMARTT_NETWORK_SERVICES_H
+
+
+unsigned int smartt_client_rx_sock_desc;
+unsigned int smartt_client_tx_sock_desc;
+unsigned int smartt_perf_rx_sock_desc;
+unsigned int smartt_top_rx_sock_desc;
+unsigned int smartt_player_rx_sock_desc;
+
+struct sockaddr_in smartt_client_rx_server_addr;
+struct sockaddr_in smartt_client_tx_server_addr;
+struct sockaddr_in smartt_perf_rx_server_addr;
+struct sockaddr_in smartt_top_rx_server_addr;
+struct sockaddr_in smartt_player_rx_server_addr;
+
+#define SMARTT_PERF_LOCAL_PORT 4000
+#define SMARTT_TOP_LOCAL_PORT 4100
+#define SMARTT_PLAYER_LOCAL_PORT 4200
+
+int SMARTTserver_OpenRxChannel();
+int SMARTTserver_OpenTxChannel(char* remote_host, unsigned int remote_port);
+
+#endif /*__SMARTT_NETWORK_SERVICES_H*/
diff --git a/smartt-server/smartt-server-utils.c b/smartt-server/smartt-server-utils.c
new file mode 100644
index 0000000..85a1f0d
--- /dev/null
+++ b/smartt-server/smartt-server-utils.c
@@ -0,0 +1,74 @@
+/*
+ * smartt-util.c
+ *
+ * Utility functions
+*/
+
+void smartt_util_exec(char *line)
+{
+ char *argv[64]; /* the command line argument */
+ int i = 0;
+
+ if (strcmp(line,"clear")==0)
+ *(argv + i++) = "clear";
+ *(argv + i) = '\0';
+
+ smartt_util_execute(argv);
+}
+
+/* ----------------------------------------------------------------- */
+/* FUNCTION parse: */
+/* This function takes an input line and parse it into tokens. */
+/* It first replaces all white spaces with zeros until it hits a */
+/* non-white space character which indicates the beginning of an */
+/* argument. It saves the address to argv[], and then skips all */
+/* non-white spaces which constitute the argument. */
+/* ----------------------------------------------------------------- */
+
+void smartt_util_parse(char *line, char **argv)
+{
+ while (*line != '\0')
+ { /* if not the end of line ....... */
+ while (*line == ' ' || *line == '\t' || *line == '\n')
+ *line++ = '\0'; /* replace white spaces with 0 */
+ *argv++ = line; /* save the argument position */
+ while (*line != '\0' && *line != ' ' &&
+ *line != '\t' && *line != '\n')
+ line++; /* skip the argument until ... */
+ }
+ *argv = '\0'; /* mark the end of argument list */
+}
+
+/* ----------------------------------------------------------------- */
+/* FUNCTION execute: */
+/* This function receives a commend line argument list with the */
+/* first one being a file name followed by its arguments. Then, */
+/* this function forks a child process to execute the command using */
+/* system call execvp(). */
+/* ----------------------------------------------------------------- */
+
+void smartt_util_execute(char **argv)
+{
+ pid_t pid;
+ int status;
+
+ if ((pid = fork()) < 0)
+ { /* fork a child process */
+ printf("*** ERROR: forking child process failed\n");
+ exit(1);
+ } else if (pid == 0)
+ { /* for the child process: */
+ if (execvp(*argv, argv) < 0)
+ { /* execute the command */
+ printf("*** ERROR: exec failed\n");
+ exit(1);
+ }
+ } else
+ { /* for the parent: */
+ while (wait(&status) != pid) /* wait for completion */
+ ;
+ }
+}
+
+
+
diff --git a/smartt-server/smartt-server-utils.h b/smartt-server/smartt-server-utils.h
new file mode 100644
index 0000000..30832a9
--- /dev/null
+++ b/smartt-server/smartt-server-utils.h
@@ -0,0 +1,43 @@
+#ifndef __SMARTT_UTILS_H
+#define __SMARTT_UTILS_H
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <inttypes.h>
+
+
+void smartt_util_exec(char *line);
+void smartt_util_parse(char *line, char **argv);
+void smartt_util_execute(char **argv);
+
+
+#endif /*__SMARTT_UTILS_H*/
diff --git a/smartt-server/smartt-server.c b/smartt-server/smartt-server.c
new file mode 100644
index 0000000..4d7e97d
--- /dev/null
+++ b/smartt-server/smartt-server.c
@@ -0,0 +1,249 @@
+/*
+* smartt-server.c
+*
+* System Metrics - Annotation Recording and Tracing Tool (SMARTT)
+*
+* This is a client application that displays cpu-cycles, bus-cycles, instructions,
+* cache-misses, branches along with the player data such as FPS, Drop Rate etc
+* The watemark levels for all the metrics can be set and are exclusively marked as
+* watermark highligted regions.
+*
+* Sample output:
+*
+*[ PID(s) ][ TIME-STAMP ][ CPU-CYCLES (w) ][ CACHE-M'SS (w) ][ BRANCHES (w) ]
+*
+* 3149 1298458764 5186403 3743842 (+) 1369802 (+)
+* 3149 1298458765 19817424 (+) 22701574 345118 (+)
+* 3149 1298458766 4727479 1447752 (+) 1168918
+*
+* Copyright (C) Linaro 2010
+
+* Author(s):
+*
+* Sudip Jain <sudip.jain@linaro.org>
+* <sudip.jain@stericsson.com>
+*
+* Released under GPL v2
+*/
+
+#include "smartt-network-services.c"
+#include "smartt-server-utils.c"
+
+#include "smartt-server.h"
+#include "smartt-network-services.h"
+#include "smartt-server-utils.h"
+
+
+struct s_remote_client_handler remote_client_handler;
+struct s_system_data system_data;
+struct s_sys_info sys_info;
+struct s_mem_info mem_info;
+struct s_player_info player_info;
+
+char *perf_args[64], *top_args[64];
+char stat_counter_str[16], user_pid_str[16];
+char pid_str[16], delay_str[16], iter_str[16];
+unsigned int g_user_mode;
+
+int main(void)
+{
+ SMARTTserver_Init();
+ while (1)
+ { /* repeat until done .... */
+ printf("\n[SMART-SERVER] # Listening... "); /* display a prompt */
+ fflush(stdout);
+ recvfrom(smartt_client_rx_sock_desc, &remote_client_handler, sizeof(remote_client_handler), 0,(struct sockaddr *)&smartt_client_rx_server_addr, &addr_len);
+ if (strcmp(remote_client_handler.msg_id,"REQ:CONNECT") == 0)
+ SMARTTserver_Start();
+ else if (strcmp(remote_client_handler.msg_id,"REQ:RUN") == 0)
+ SMARTTserver_Exec();
+ else
+ fprintf(stderr,"[E] Invalid Request\t\t\t: Failed\n");
+ }
+}
+
+void SMARTTserver_Init()
+{
+ smartt_util_exec("clear");
+ SMARTTserver_OpenSmarttClient_INPort();
+ SMARTTserver_OpenSmarttPerf_INPort();
+ SMARTTserver_OpenSmarttTop_INPort();
+ SMARTTserver_OpenSmarttPlayer_INPort();
+
+}
+
+int SMARTTserver_Start()
+{
+ int status, n;
+
+ printf("\n");
+ printf("[I] SMART Server Started\t\t\t\t: Success\n");
+ printf("[I] Waiting Request from SMART Client\t\t\t: Waiting\n");
+ fflush(stdout);
+
+ if ((status = SMARTTserver_OpenSmarttClient_OUTPort(remote_client_handler.channel_config.host_ip, remote_client_handler.channel_config.port_no ))== 1)
+ {
+ sendto(smartt_client_tx_sock_desc, "ACK:ACCEPT", 64,0,(struct sockaddr *)&smartt_client_tx_server_addr, sizeof(smartt_client_tx_server_addr));
+ fprintf(stderr,"[I] Connected to SMART Client @ %s:%d\t: Success\n",remote_client_handler.channel_config.host_ip,
+ remote_client_handler.channel_config.port_no);
+ } else
+ {
+ fprintf(stderr,"[I] Connected to SMART Client\t\t\t\t: Failed\n");
+ return(0);
+ }
+
+ fprintf(stderr,"[I] Waiting for user configuration from SMART Client\t: Waiting\n");
+ n = recvfrom(smartt_client_rx_sock_desc, &remote_client_handler, sizeof(remote_client_handler), 0,(struct sockaddr *)&smartt_client_rx_server_addr, &addr_len);
+ if (strcmp(remote_client_handler.msg_id,"REQ:USER-CONFIG") == 0)
+ {
+ fprintf(stderr,"[I] User Configuration Received\t\t\t\t: Success\n");
+ sendto(smartt_client_tx_sock_desc, "ACK:USER-CONFIG",64,0,(struct sockaddr *) &smartt_client_tx_server_addr, sizeof(smartt_client_tx_server_addr));
+ } else
+ {
+ fprintf(stderr,"[I] User Configuration Received\t\t\t\t: Failed\n");
+ return(0);
+ }
+
+ if (remote_client_handler.pid == 0)
+ {
+ g_user_mode = 1;
+ fprintf(stderr,"[I] Waiting for SMARTT Player to start \t\t\t: Waiting\n");
+ n = recvfrom(smartt_player_rx_sock_desc, &player_info, sizeof(player_info), 0, (struct sockaddr *)&smartt_player_rx_server_addr, &addr_len);
+ remote_client_handler.pid = player_info.pid;
+ fprintf(stderr,"[I] Received SMART Player Process Id [%5u] \t\t: Success\n",player_info.pid);
+ }
+
+ n = recvfrom(smartt_client_rx_sock_desc, &remote_client_handler, sizeof(remote_client_handler), 0,(struct sockaddr *)&smartt_client_rx_server_addr, &addr_len);
+ if (strcmp(remote_client_handler.msg_id,"REQ:CAPTURE") == 0)
+ {
+ fprintf(stderr,"[I] Capturing System Metrics for PID [%5u] \t\t: Running\n",remote_client_handler.pid);
+ SMARTTserver_Exec();
+
+ fprintf(stderr,"[I] Capturing System Metrics for PID [%5u] \t\t: Completed\n",remote_client_handler.pid);
+ } else
+ {
+ fprintf(stderr,"[I] Capturing System Metrics for PID [%5u] \t\t: Failed\n",remote_client_handler.pid);
+ return(0);
+ }
+ return(1);
+}
+
+void SMARTTserver_FillPerfArgs()
+{
+ int i = 0;
+
+ *(perf_args + i++) = "./perf";
+ *(perf_args + i++) = "stat";
+ *(perf_args + i++) = "-p";
+
+
+ sprintf(user_pid_str,"%d",remote_client_handler.pid);
+ *(perf_args + i++) = user_pid_str;
+
+ if (remote_client_handler.event_config.event_enable[0] == 1)
+ {
+ *(perf_args + i++) = "-e";
+ *(perf_args + i++) = "cpu-cycles";
+ }
+ if (remote_client_handler.event_config.event_enable[1] == 1)
+ {
+ *(perf_args + i++) = "-e";
+ *(perf_args + i++) = "instructions";
+ }
+ if (remote_client_handler.event_config.event_enable[2] == 1)
+ {
+ *(perf_args + i++) = "-e";
+ *(perf_args + i++) = "bus-cycles";
+ }
+ if (remote_client_handler.event_config.event_enable[3] == 1)
+ {
+ *(perf_args + i++) = "-e";
+ *(perf_args + i++) = "cache-misses";
+ }
+ if (remote_client_handler.event_config.event_enable[4] == 1)
+ {
+ *(perf_args + i++) = "-e";
+ *(perf_args + i++) = "branches";
+ }
+ *(perf_args + i++) = "sleep";
+ sprintf(stat_counter_str,"%4.2lf",remote_client_handler.event_config.counter);
+ *(perf_args + i++) = stat_counter_str;
+ *(perf_args + i) = '\0';
+}
+
+void SMARTTserver_FillTopArgs()
+{
+ int i = 0;
+
+ *(top_args + i++) = "./top";
+
+ *(top_args + i++) = "-p";
+// sprintf(pid_str,"%u",9478);
+ sprintf(pid_str,"%d",remote_client_handler.pid);
+ *(top_args + i++) = pid_str;
+
+ *(top_args + i++) = "-d";
+ sprintf(delay_str,"%4.2lf",remote_client_handler.event_config.counter);
+// sprintf(delay_str,"%4.2lf",1.0);
+ *(top_args + i++) = delay_str;
+
+ *(top_args + i++) = "-n";
+ sprintf(iter_str,"%u",1);
+ *(top_args + i++) = iter_str;
+
+ *(top_args + i) = '\0';
+}
+
+void SMARTTserver_Exec()
+{
+ int i;
+
+ SMARTTserver_FillPerfArgs();
+ SMARTTserver_FillTopArgs();
+ for (i=1; i<=remote_client_handler.event_config.samples; i++)
+ {
+ smartt_util_execute(perf_args);
+ smartt_util_execute(top_args);
+
+ SMARTTserver_Capture();
+ SMARTTserver_Process();
+ }
+}
+
+void SMARTTserver_Capture()
+{
+ unsigned int n;
+
+ n = recvfrom(smartt_perf_rx_sock_desc, &sys_info, sizeof(sys_info), 0, (struct sockaddr *)&smartt_perf_rx_server_addr, &addr_len);
+ n = recvfrom(smartt_top_rx_sock_desc, &mem_info, sizeof(mem_info), 0, (struct sockaddr *)&smartt_top_rx_server_addr, &addr_len);
+ if (g_user_mode)
+ n = recvfrom(smartt_player_rx_sock_desc, &player_info, sizeof(player_info), 0, (struct sockaddr *)&smartt_player_rx_server_addr, &addr_len);
+}
+
+void SMARTTserver_Process()
+{
+ system_data.sys_info = sys_info;
+ system_data.mem_info = mem_info;
+ system_data.player_info = player_info;
+
+ sendto(smartt_client_tx_sock_desc, (void *)&system_data, sizeof(system_data),0,(struct sockaddr *) &smartt_client_tx_server_addr, sizeof(smartt_client_tx_server_addr));
+ printf("[R] Sys info captured and send to server at epoch time [%lu] \n", system_data.sys_info.time_stamp);
+ fflush(stdout);
+}
+
+void SMARTTserver_Exit()
+{
+ printf("[I] Exitting SMARTT Server\t\t\t\t: Success\n");
+ exit(0);
+}
+
+void SMARTTserver_Test()
+{
+ int i=0;
+ SMARTTserver_FillTopArgs();
+ smartt_util_execute(top_args);
+ while (getchar()!='\0')
+ printf("%s",*(top_args + i++));
+}
+
+
diff --git a/smartt-server/smartt-server.conf b/smartt-server/smartt-server.conf
new file mode 100644
index 0000000..57705a0
--- /dev/null
+++ b/smartt-server/smartt-server.conf
@@ -0,0 +1 @@
+LOCAL-PORT 5100
diff --git a/smartt-server/smartt-server.config b/smartt-server/smartt-server.config
new file mode 100644
index 0000000..3110796
--- /dev/null
+++ b/smartt-server/smartt-server.config
@@ -0,0 +1 @@
+LOCAL-PORT 4000
diff --git a/smartt-server/smartt-server.h b/smartt-server/smartt-server.h
new file mode 100644
index 0000000..84b777f
--- /dev/null
+++ b/smartt-server/smartt-server.h
@@ -0,0 +1,89 @@
+
+#ifndef __SMARTT_SERVER_H
+#define __SMARTT_SERVER_H
+#define NO_OF_EVENTS 6
+
+struct s_client_event_manager
+{
+ const char *event_name;
+ int event_enable;
+ double data_counter;
+ unsigned int sample_count;
+};
+
+struct s_event_config
+{
+ int event_enable[NO_OF_EVENTS];
+ double counter;
+ unsigned int samples;
+};
+
+struct s_channel_config
+{
+ char host_ip[16];
+ unsigned int port_no;
+};
+
+struct s_remote_client_handler
+{
+ char msg_id[64];
+ unsigned int pid;
+ struct s_event_config event_config;
+ struct s_channel_config channel_config;
+};
+
+struct s_sys_info
+{
+ unsigned int pid;
+ unsigned long int cpu_cycles;
+ unsigned long int instructions;
+ unsigned long int bus_cycles;
+ unsigned long int cache_misses;
+ unsigned long int branches;
+ unsigned long int time_stamp;
+};
+
+struct s_mem_info {
+ unsigned long int main_total;
+ unsigned long int main_used;
+ unsigned long int main_free;
+ unsigned long int main_buffers;
+ unsigned long int main_cached;
+ unsigned long int swap_total;
+ unsigned long int swap_used;
+ unsigned long int swap_free;
+};
+
+struct s_player_info
+{
+ unsigned int pid;
+ unsigned int rate;
+ unsigned int width ;
+ unsigned int height;
+ double curfps;
+ double dps;
+ double avgfps;
+ unsigned long int dropped;
+ unsigned long int renderred;
+ char vcodec[64];
+ char acodec[64];
+};
+
+struct s_system_data
+{
+ struct s_sys_info sys_info;
+ struct s_mem_info mem_info;
+ struct s_player_info player_info;
+};
+
+void SMARTTserver_Init();
+int SMARTTserver_Start();
+void SMARTTserver_Exec();
+void SMARTTserver_Process();
+void SMARTTserver_Capture();
+void SMARTTserver_FillPerfArgs();
+void SMARTTserver_Test();
+void SMARTTserver_Exit();
+int SMARTTserver_GetUserPid();
+
+#endif /* __SMARTT_SERVER_H */
diff --git a/smartt-server/top b/smartt-server/top
new file mode 120000
index 0000000..93e29ae
--- /dev/null
+++ b/smartt-server/top
@@ -0,0 +1 @@
+../smartt-top/top \ No newline at end of file
diff --git a/smartt-top/AUTHORS b/smartt-top/AUTHORS
new file mode 100644
index 0000000..d861b4b
--- /dev/null
+++ b/smartt-top/AUTHORS
@@ -0,0 +1,51 @@
+free:
+Brian Edmonds
+
+oldps:
+Branko Lankester <lankeste@fwi.uva.nl>
+Michael K. Johnson <johnsonm@redhat.com>
+Michael Shields <mjshield@nyx.cs.du.edu>
+Charles Blake <cblake@bbn.com>
+David Mossberger-Tang
+
+ps:
+Albert Cahalan <albert@users.sf.net>
+
+skill/kill/snice:
+Albert Cahalan <albert@users.sf.net>
+
+tload:
+Branko Lankester
+David Engel <david@ods.com>
+Michael K. Johnson <johnsonm@redhat.com>
+
+top:
+Jim Warner <warnerjc@worldnet.att.net>
+
+oldtop:
+Branko Lankester <lankeste@fwi.uva.nl>
+Roger Binns
+Robert Nation <nation@rocket.sanders.lockheed.com>
+Michael K. Johnson <johnsonm@redhat.com>
+Michael Shields <mjshield@nyx.cs.du.edu>
+Tim Janik <timj@gtk.org>
+Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+George Bonser <george@captech.com>
+
+uptime:
+Larry Greenfield <greenfie@gauss.rutgers.edu>
+Michael K. Johnson <johnsonm@sunsite.unc.edu>
+
+vmstat:
+Henry Ware <al172@yfn.ysu.edu>.
+
+w:
+Larry Greenfield <greenfie@gauss.rutgers.edu>
+Michael K. Johnson <johnsonm@redhat.com>
+Charles Blake
+
+watch:
+Tony Rems <rembo@unisoft.com>
+Mike Coleman <mkc@acm.org>
+Jarrod Lowe <procps@rrod.net>
+
diff --git a/smartt-top/BUGS b/smartt-top/BUGS
new file mode 100644
index 0000000..ec45bde
--- /dev/null
+++ b/smartt-top/BUGS
@@ -0,0 +1,74 @@
+BUG REPORTS
+
+Please read this file before sending in a bug report or patch.
+
+Also, PLEASE read the documentation first. 90% of the mail I get
+complaining about procps is due to the sender not having read the
+documentation!
+
+
+Where to send
+=============
+Send comments, bug reports, patches, etc., to albert@users.sf.net
+
+
+What to send
+============
+It is much more useful to me if a program really crashes to recompile it
+with make "CFLAGS=-ggdb -O", run it with "gdb prog" and "run" and send
+me a stack trace ('bt' command). That said, any bug report is still
+better than none.
+
+strace and ltrace output are very helpful:
+
+ strace -o output-file ps --blah
+ bzip2 output-file
+
+I also like "ps --info" output, even if there isn't a ps problem.
+
+Kernel-Dependent Patches
+========================
+If you send me patches which are specific to *running* with a particular
+kernel version of /proc, please condition them with the runtime determined
+variable 'linux_version_code' from libproc/version.c. It is the same
+number as the macro LINUX_VERSION_CODE for which the kernel /proc fs
+code was compiled.
+
+A macro is provide in libproc/version.h to construct the code from its
+components, e.g.
+ if (linux_version_code < LINUX_VERSION(2,5,41))
+ /* blah blah blah */
+A startup call to set_linux_version may also be necessary.
+
+Of course, if a bug is due to a change in kernel file formats, it would
+be best to first try to generalize the parsing, since the code is then
+more resilient against future change.
+
+Also unified diffs (diff -u) are my preference, context diffs (diff -c )
+are kind of usable, and standard diffs (diff) are more useless than a
+generic text description of what you did. Just use
+ diff -Naurd oldfile newfile
+or
+ diff -Naurd old-procps-dir new-procps-dir
+to create your diffs and you will make me happy. Also make sure to
+include a description of what the diff is for or I'm likely to ignore
+it because of general lack of time...
+
+It might be nice to get rid of miscellaneous compiler warnings, but
+don't bend over backwards to do it.
+
+
+Code Structure
+==============
+
+A goal is to encapsulate *all* parsing dependent on /proc
+file formats into the libproc library. If the API is general enough
+it can hopefully stabilize and then /proc changes might only require
+updating libproc.so. Beyond that having the set of utilities be simple
+command lines parsers and output formatters and encapsulating all kernel
+divergence in libproc is the way to go.
+
+Hence if you are submitting a new program or are fixing an old one, keep
+in mind that adding files to libproc which encapsulate such things is
+more desirable than patching the actual driver program. (well, except
+to move it toward the API of the library).
diff --git a/smartt-top/COPYING b/smartt-top/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/smartt-top/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/smartt-top/COPYING.LIB b/smartt-top/COPYING.LIB
new file mode 100644
index 0000000..92b8903
--- /dev/null
+++ b/smartt-top/COPYING.LIB
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/smartt-top/CodingStyle b/smartt-top/CodingStyle
new file mode 100644
index 0000000..962207d
--- /dev/null
+++ b/smartt-top/CodingStyle
@@ -0,0 +1,101 @@
+If you start a new file, you get to choose the style.
+If you change an existing file, follow the existing style.
+
+Hard tabs are OK, as long as you consider the tab stops to
+be every 8 characters. You can also use 2, 3, or 4 spaces.
+Tabs are kind of yucky, since cut-and-paste mangles them
+sometimes and they make "diff -Naurd old new" output less
+readable.
+
+Spaces within a line don't matter much, and won't be
+considered part of the style. Just make it readable:
+
+if(x){ // OK
+if( x ){ // OK
+if (x) { // OK
+if(x==y && a==b){ // OK
+if(x == y && a == b){ // poor
+if(x==y&&a==b){ // poor
+
+This is evil:
+
+szWinStallman
+FoulCodingStyle (int iInsanity)
+ {
+ if (iInsanity)
+ {
+ GoHackEmacs () ;
+ }
+ else
+ {
+ SeekHelpForYourLisp () ;
+ }
+ }
+
+
+Curly braces belong at the end of a line. If you must, go ahead
+and make function bodies an exception to that rule. (as Linus does)
+
+Big fprintf() calls and similar go like this:
+
+fprintf(fd, "%d %d %d %d %d %d\n",
+ sdfsdf_sdfsdf + sdfs_iii, // not an example of good names!
+ iijjij,
+ kjfkkj_sdfssss_sfff,
+ sdflkjfdskj + sdf - sfds,
+ jksss,
+ sfssss + wwwwfwfw
+);
+
+Keep these distinct: NULL, '\0', 0, 0.0
+
+Command-line parsers need to be bomb-proof. It is not acceptable
+to crash due to a messed up command-line. For an option "-x" that
+takes an argument, accept both "-x arg" and "-xarg". Remember to
+support "--" and "--version".
+
+Be extremely careful when handling data from other users.
+For example, it is a security hole if /proc/123/cmdline can
+overflow an array. It is often a security hole if you allow
+non-ASCII characters to be printed. Assuming the console is
+not in UTF-8 mode, all of these are bad: "\b\e\f\n\r\t\v\x9b".
+(the "\x9b" is valid in UTF-8 mode, but equivalent to "\e["
+when not in UTF-8 mode -- which gives control of terminal
+settings) It's best if you consider user-supplied data to
+be unsafe, since this makes for less work in case the code
+ends up needing to run setuid. Termcap data is user-supplied.
+Except for the above security issues, don't bother to check
+for something you can't handle... like printf() failing.
+It is expected that /dev exists and so on.
+
+Remember that a read() may return early, with partial data
+or with -1 and errno set to EINTR. You then must try again.
+
+char: may be signed or unsigned by default
+int: always 32-bit
+long long: always 64-bit
+pointer: either 32-bit or 64-bit
+long: same size as a pointer
+KLONG: same size as a pointer or long IN THE KERNEL
+
+Functions used in just one file must be marked static.
+Use the "const" and "restrict" keywords wherever you can.
+
+Put main() at the bottom of a file so you don't need all
+those ugly forward declarations.
+
+Avoid the strcat() function. It is slow. For some odd
+reason, snprintf() is faster than sprintf().
+
+Reuse memory allocations when you can. When using realloc(),
+do your increments by more than one. 25% is a nice amount.
+
+Avoid compile-time choices. They make documentation difficult,
+and they are not friendly to binary distribution.
+
+Write programs that can handle a million processes without
+getting hopelessly slow. Allow for /proc/123/cmdline to
+be at least 128 kB.
+
+The LGPL license is strongly preferred. This allows use of
+the code in the library.
diff --git a/smartt-top/Makefile b/smartt-top/Makefile
new file mode 100644
index 0000000..fcb8410
--- /dev/null
+++ b/smartt-top/Makefile
@@ -0,0 +1,262 @@
+# procps Makefile
+# Albert Cahalan, 2002-2004
+#
+# Recursive make is considered harmful:
+# http://google.com/search?q=%22recursive+make+considered+harmful%22
+#
+# For now this Makefile uses explicit dependencies. The project
+# hasn't grown big enough to need something complicated, and the
+# dependency tracking files are an ugly annoyance.
+#
+# This file includes */module.mk files which add on to variables:
+# FOO += bar/baz
+#
+#
+# Set (or uncomment) SKIP if you wish to avoid something.
+# For example, you may prefer the /bin/kill from util-linux or bsdutils.
+
+
+VERSION := 3
+SUBVERSION := 2
+MINORVERSION := 8
+TARVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION)
+
+############ vars
+
+# so you can disable them or choose alternates
+ldconfig := ldconfig
+ln_f := ln -f
+ln_sf := ln -sf
+install := install -D --owner 0 --group 0
+
+# Lame x86-64 /lib64 and /usr/lib64 abomination:
+lib64 := lib$(shell [ -d /lib64 ] && echo 64)
+
+usr/bin := $(DESTDIR)/usr/bin/
+bin := $(DESTDIR)/bin/
+sbin := $(DESTDIR)/sbin/
+usr/proc/bin := $(DESTDIR)/usr/bin/
+man1 := $(DESTDIR)/usr/share/man/man1/
+man5 := $(DESTDIR)/usr/share/man/man5/
+man8 := $(DESTDIR)/usr/share/man/man8/
+lib := $(DESTDIR)/$(lib64)/
+usr/lib := $(DESTDIR)/usr/$(lib64)/
+usr/include := $(DESTDIR)/usr/include/
+
+#SKIP := $(bin)kill $(man1)kill.1
+
+BINFILES := $(usr/bin)uptime $(usr/bin)tload $(usr/bin)free $(usr/bin)w \
+ $(usr/bin)top $(usr/bin)vmstat $(usr/bin)watch $(usr/bin)skill \
+ $(usr/bin)snice $(bin)kill $(sbin)sysctl $(usr/bin)pmap \
+ $(usr/proc/bin)pgrep $(usr/proc/bin)pkill $(usr/bin)slabtop \
+ $(usr/proc/bin)pwdx
+
+MANFILES := $(man1)uptime.1 $(man1)tload.1 $(man1)free.1 $(man1)w.1 \
+ $(man1)top.1 $(man1)watch.1 $(man1)skill.1 $(man1)kill.1 \
+ $(man1)snice.1 $(man1)pgrep.1 $(man1)pkill.1 $(man1)pmap.1 \
+ $(man5)sysctl.conf.5 $(man8)vmstat.8 $(man8)sysctl.8 \
+ $(man1)slabtop.1 $(man1)pwdx.1
+
+TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \
+ Makefile procps.lsm procps.spec v t README.top CodingStyle \
+ sysctl.conf minimal.c $(notdir $(MANFILES)) dummy.c \
+ uptime.c tload.c free.c w.c top.c vmstat.c watch.c skill.c \
+ sysctl.c pgrep.c top.h pmap.c slabtop.c pwdx.c
+
+# Stuff (tests, temporary hacks, etc.) left out of the standard tarball
+# plus the top-level Makefile to make it work stand-alone.
+_TARFILES := Makefile
+
+CURSES := -lncurses
+CURSESW := -lncursesw
+
+# This seems about right for the dynamic library stuff.
+# Something like this is probably needed to make the SE Linux
+# library loading not conflict with embedded systems stuff.
+#
+#ifeq ($(SHARED),1)
+#ldl := -ldl
+#LIBTYPE := -DSHAREDLIB
+#else
+#LIBTYPE := -DSTATICLIB
+#endif
+
+# Preprocessor flags.
+PKG_CPPFLAGS := -D_GNU_SOURCE -I proc
+CPPFLAGS := -I/usr/include/ncurses
+ALL_CPPFLAGS := $(PKG_CPPFLAGS) $(CPPFLAGS)
+
+# Left out -Wconversion due to noise in glibc headers.
+# Left out -Wunreachable-code and -Wdisabled-optimization
+# because gcc spews many useless warnings with them.
+#
+# Since none of the PKG_CFLAGS things are truly required
+# to compile procps, they might best be moved to CFLAGS.
+# On the other hand, they aren't normal -O -g things either.
+#
+# Note that -O2 includes -fomit-frame-pointer only if the arch
+# doesn't lose some debugging ability.
+#
+PKG_CFLAGS := -fno-common -ffast-math \
+ -W -Wall -Wshadow -Wcast-align -Wredundant-decls \
+ -Wbad-function-cast -Wcast-qual -Wwrite-strings -Waggregate-return \
+ -Wstrict-prototypes -Wmissing-prototypes
+# Note that some stuff below is conditional on CFLAGS containing
+# an option that starts with "-g". (-g, -g2, -g3, -ggdb, etc.)
+CFLAGS := -O2 -s
+ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS)
+
+PKG_LDFLAGS := -Wl,-warn-common
+LDFLAGS :=
+ALL_LDFLAGS := $(PKG_LDFLAGS) $(LDFLAGS)
+
+############ Add some extra flags if gcc allows
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),tar)
+ifneq ($(MAKECMDGOALS),extratar)
+ifneq ($(MAKECMDGOALS),beta)
+
+# Unlike the kernel one, this check_gcc goes all the way to
+# producing an executable. There might be a -m64 that works
+# until you go looking for a 64-bit curses library.
+check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o will_this_file_really_exist.tmp $(CURSES) $(CURSESW) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ; rm -f will_this_file_really_exist.tmp)
+
+# Be 64-bit if at all possible. In a cross-compiling situation, one may
+# do "make m64=-m32 lib64=lib" to produce 32-bit executables. DO NOT
+# attempt to use a 32-bit executable on a 64-bit kernel. Packagers MUST
+# produce separate executables for ppc and ppc64, s390 and s390x,
+# i386 and x86-64, mips and mips64, sparc and sparc64, and so on.
+# Failure to do so will cause data corruption.
+m64 := $(call check_gcc,-m64,$(call check_gcc,-mabi=64,))
+ALL_CFLAGS += $(m64)
+
+ALL_CFLAGS += $(call check_gcc,-Wdeclaration-after-statement,)
+ALL_CFLAGS += $(call check_gcc,-Wpadded,)
+ALL_CFLAGS += $(call check_gcc,-Wstrict-aliasing,)
+
+# Adding -fno-gcse might be good for those files which
+# use computed goto.
+#ALL_CFLAGS += $(call check_gcc,-fno-gcse,)
+
+# if not debugging, enable things that could confuse gdb
+ifeq (,$(findstring -g,$(filter -g%,$(CFLAGS))))
+ALL_CFLAGS += $(call check_gcc,-fweb,)
+ALL_CFLAGS += $(call check_gcc,-frename-registers,)
+ALL_CFLAGS += $(call check_gcc,-fomit-frame-pointer,)
+endif
+
+# in case -O3 is enabled, avoid bloat
+ALL_CFLAGS += $(call check_gcc,-fno-inline-functions,)
+
+endif
+endif
+endif
+endif
+
+############ misc.
+
+# free.c pmap.c sysctl.c uptime.c vmstat.c watch.c pgrep.c skill.c tload.c top.c w.c
+# utmp.c oldtop.c tmp-junk.c minimal.c
+
+.SUFFIXES:
+.SUFFIXES: .a .o .c .s .h
+
+.PHONY: all clean do_all install tar extratar beta
+
+ALL := $(notdir $(BINFILES))
+
+CLEAN := $(notdir $(BINFILES))
+
+DIRS :=
+
+INSTALL := $(BINFILES) $(MANFILES)
+
+# want this rule first, use := on ALL, and ALL not filled in yet
+all: do_all
+
+-include */module.mk
+
+do_all: $(ALL)
+
+junk := DEADJOE *~ *.o core gmon.out
+
+# Remove $(junk) from all $(DIRS)
+CLEAN += $(junk) $(foreach dir,$(DIRS),$(addprefix $(dir), $(junk)))
+
+##########
+# not maintained because it isn't really needed:
+#
+#SRC :=
+#OBJ := $(patsubst %.c,%.o, $(filter %.c,$(SRC)))
+#
+#ifneq ($(MAKECMDGOALS),clean)
+#-include $(OBJ:.o=.d)
+#endif
+#
+#%.d: %.c
+# depend.sh $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@
+############
+
+# don't want to type "make procps-$(TARVERSION).tar.gz"
+tar: $(TARFILES)
+ mkdir procps-$(TARVERSION)
+ (tar cf - $(TARFILES)) | (cd procps-$(TARVERSION) && tar xf -)
+ tar cf procps-$(TARVERSION).tar procps-$(TARVERSION)
+ gzip -9 procps-$(TARVERSION).tar
+
+extratar: $(_TARFILES)
+ mkdir procps-$(TARVERSION)
+ (tar cf - $(_TARFILES)) | (cd procps-$(TARVERSION) && tar xf -)
+ tar cf extra-$(TARVERSION).tar procps-$(TARVERSION)
+ gzip -9 extra-$(TARVERSION).tar
+
+beta: $(TARFILES) $(_TARFILES)
+ mkdir beta-$(TARVERSION)
+ (tar cf - $(TARFILES) $(_TARFILES)) | (cd beta-$(TARVERSION) && tar xf -)
+ tar cf beta-$(TARVERSION).tar beta-$(TARVERSION)
+ gzip -9 beta-$(TARVERSION).tar
+
+clean:
+ rm -f $(CLEAN)
+
+###### install
+
+$(BINFILES) : all
+ $(install) --mode a=rx $(notdir $@) $@
+
+$(MANFILES) : all
+ $(install) --mode a=r $(notdir $@) $@
+
+install: $(filter-out $(SKIP) $(addprefix $(DESTDIR),$(SKIP)),$(INSTALL))
+ cd $(usr/bin) && $(ln_f) skill snice
+ cd $(usr/proc/bin) && $(ln_f) pgrep pkill
+
+############ prog.c --> prog.o
+
+top.o : top.h
+
+%.o : %.c
+ $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c -g -o $@ $<
+
+w.o: w.c
+ $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(W_SHOWFROM) -c -g $<
+
+############ prog.o --> prog
+
+pmap w uptime tload free sysctl vmstat utmp pgrep skill pwdx: % : %.o $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@
+
+slabtop top: % : %.o $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES)
+
+watch: % : %.o
+ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSESW)
+
+############ progX --> progY
+
+snice kill: skill
+ $(ln_f) skill $@
+
+pkill: pgrep
+ $(ln_f) pgrep pkill
diff --git a/smartt-top/NEWS b/smartt-top/NEWS
new file mode 100644
index 0000000..60acc88
--- /dev/null
+++ b/smartt-top/NEWS
@@ -0,0 +1,368 @@
+procps-3.2.7 --> procps-3.2.8
+
+ps: allow "+" in sort specifications, as in man page rh208217
+ps: recognize SCHED_ISO and SCHED_IDLE
+ps: document SCHED_BATCH and add a "see also" for stime
+ps: man page less ambiguous
+top: normal exit code should be 0 #341272 #354255 rh199174
+top: misc fixes
+pgrep: usage error should exit with 2 #413383
+vmstat: use EXIT_FAILURE -- thanks Yoshio Nakamura #425492
+sysctl: fix crash -- thanks Steinar Gunderson #423704
+watch: tolerate umlauts #207103
+pmap: range limits with -A low,high
+update /dev/tty* info to May 2009 devices.txt
+don't read off end of string const rh469495 rh498182
+
+procps-3.2.6 --> procps-3.2.7
+
+top: document H option -- thanks Tony Ernst
+top: terabytes -- thanks Tony Ernst
+top: CPU usage column width fixes -- thanks Tony Ernst
+top: *roff change #326517
+ps: SCHED_BATCH is B
+ps: fix s format (signals) output with thread display
+watch: avoid integer overflow for the time delay
+pwdx: buffer overflow fixed -- thanks Ulf Harnhammar
+procps.spec needed a slash -- thanks Jesse Brandeburg
+w: stale utmp entries snuck in via uninitialized var -- thanks Robert A Basch
+pgrep/pkill: fix some realloc-related crashes #353894
+pgrep/pkill: g is criteria (PLD Linux, patch found in locked filing cabinet)
+sysctl: use - for stdin (PLD Linux: beware of the leopard)
+top: show CPU time stolen from a virtual machine
+
+procps-3.2.5 --> procps-3.2.6
+
+vmstat: /proc/stat buffer big enough for 1024 CPUs
+dietlibc needs termios.h for struct winsize -- thanks Thomas Ogrisegg
+top: can do per-task display -- thanks John Blackwood rh114012
+more MIPS crud -- thanks Jim Gifford and Ryan Oliver
+begin prep for setuid
+top: fix %CPU max on 2..9 CPU SMP -- thanks Ga*tan LEURENT rh110555
+ps: fix crash related to realloc -- thanks David Houlder
+ps: man page more detailed #334682
+spelling fixes #300333 #334684 #334685
+top: crash on resize fixed -- thanks Michal Maruska #320289
+vmstat: -p handles /dev/ and does not overflow #319523 #330969
+
+procps-3.2.4 --> procps-3.2.5
+
+display problem on 64-bit systems fixed #287947
+top: variable-width PID and PPID
+top: variable-width %CPU rh110555
+sysctl: better error messages
+ps: security labels can contain any printable ASCII
+top: help and version message on stdout, with exit(0) #283541
+ps: SIGTSTP and SIGTTOU shouldn't print bug email address #246123
+slabtop: compile with glibc 2.2.17 (and older, likely)
+slabtop: fix overflow on huge NUMA boxes #264640
+slabtop: accept any slabinfo 2.x format g77301 #287691 rh145369 rh145906
+ps: alignment after WCHAN fixed ub5385
+pmap: when no -x or -d option, show full path rh142751
+
+procps-3.2.3 --> procps-3.2.4
+
+support 64-bit MIPS with n32 binary
+sparc32 optimized for sparc32 again
+pwdx: new command -- thanks Nicholas Miell
+ps: UTF-8 username + command -- thanks Karel Zak rh134763,rh112518,rh134780
+ps: more room for some columns
+ps: tolerate SubDomain security module CONTEXT/LABEL data #277401
+watch: passes COLUMNS and LINES in environment
+top: in batch mode, tolerate unknown $TERM -- thanks Daniel Walsh
+pkill: quiet about processes that die before kill()
+
+procps-3.2.2 --> procps-3.2.3
+
+avoid truncating long usernames
+avoid warning about -lncurses when not linking (more)
+new names for shared libraries (packagers: watch out!)
+"make install" no longer rebuilds everything
+wchan now '*' for multi-threaded processes
+ps: new man page -- thanks Nicolas Francois
+ps: STAT shows l for multi-threaded processes
+vmstat: some overflow problems fixed -- thanks Holger Kiehl
+sysctl: man page fix
+
+procps-3.2.1 --> procps-3.2.2
+
+new packager (downstream maintainer) guidelines in README
+move striping from install command to CFLAGS
+new gcc options in use: -fweb, -frename-registers, etc.
+avoid warning about -lncurses when not linking -- thanks FLWM
+tolerate IA-64 headers without PAGE_SIZE
+ps: k option, same as --sort
+ps: personality-specific -x support (HP-UX and SVR4-MP)
+pgrep: man page SEE ALSO updated #226817
+sysctl: -q and -N options
+sysctl: better error handling of failed writes
+top: tolerate sparse CPU numbering
+top: try to handle terminals lacking rmam and smam #235003
+top: xterm dislikes clear-to-eol at eol (char lost)
+vmstat: fixed -d
+watch: allow sub-second intervals -- thanks Thomas Stewart
+
+procps-3.2.0 --> procps-3.2.1
+
+build even w/ curses in an odd location -- thanks to Segher Boessenkool
+ps: STAT flags for fg process group and session leader
+ps: STAT flags for swapped out process dropped (was broken)
+ps: new -M and Z options for security data (SE Linux, etc.)
+slabtop: detect broken /proc/slabinfo -- thanks to Fabian Frederick
+slabtop: ensure that error messages show up on the screen -- FF again
+
+procps-3.1.15 --> procps-3.2.0
+
+build on IA-64 again #227933
+pmap: output like Solaris 9, not Solaris 7
+ps: also handle SELinux on the 2.4.xx kernels
+top: during a ^Z, the terminal was messed up #228822
+future-proof the tty handling (thanks to Zhou Wei)
+slabtop (Chris Rivera and Robert Love) #226778
+pmap: detect the primary stack
+pmap: -d format
+free: report high and low memory
+
+procps-3.1.14 --> procps-3.1.15
+
+install to /lib64 if it exists
+hide kernel PID bug (Linux 2.4.13-pre1 to 2.4.MAX) #217278 #219730 #217525 #224470
+ps: faster threaded display
+top: auto-margin problem #217559
+ps: support NSA SELinux, all builds, Linux 2.6+ #193648
+sysctl: tweak man page for ESR's broken parser
+
+procps-3.1.13 --> procps-3.1.14
+
+top: displays on more genuine serial terminals
+handle 32-bit dev_t of Linux 2.6
+ps: finally, m and -m satisfy the original design
+ps: distinct per-thread and whole-process pending signals
+
+procps-3.1.12 --> procps-3.1.13
+
+ps: can display NPTL threads w/ kernel patch
+no seLinux for now (new kernel interface)
+
+procps-3.1.11 --> procps-3.1.12
+
+ps: explicit width ("ps -o pid,wchan:42,args")
+ps: $PS_FORMAT works properly #201575
+top: new Linux 2.6.0-test4 CPU stats shown
+top: multiple -p options work again
+top: fixed 4 GB wrap-around
+ps: has a set of tests to ensure correctness
+man page: /var/run/utmp, not /etc/utmp #206583
+required flags moved out of CFLAGS #205429
+RPM generation handles /lib64
+WCHAN skips leading '.'
+vmstat: numerous new features
+
+procps-3.1.10 --> procps-3.1.11
+
+compile with gcc 2.95 again (C99 issue)
+
+procps-3.1.9 --> procps-3.1.10
+
+handle GPLONLY_ symbols #143549 #188374
+kill: better man page
+skill: better man page
+ps: PID-like columns change width as needed
+top: COMMAND instead of Command
+vmstat: -m displays slabinfo
+vmstat: -d displays disk stats
+
+procps-3.1.8 --> procps-3.1.9
+
+memory sizes fixed for 64-bit w/ gcc 3.x #194376 #191933
+ps: detect broken OS install w/o /proc mounted #172735
+top: fix suspend/resume behavior
+top: ditch warning until a GOOD interface is found #188271
+kill: more info in the man page #182414
+ps: document the -o, o, -O, and O options #169301
+vmstat: choose units you like: 1000, 1024, 1000000...
+
+procps-3.1.7 --> procps-3.1.8
+
+top: fix keyboard handling (help screen, etc.)
+
+procps-3.1.6 --> procps-3.1.7
+
+Makefile: made SKIP feature easier to use
+watch: --help now explains -t, --no-title #182246
+ps: warning directs users to the FAQ
+top: batch mode can refresh by fractional seconds
+top: faster start-up
+top: do not refresh like crazy
+ps: better crash message
+
+procps-3.1.5 --> procps-3.1.6
+
+handle the 2.5.61 kernel
+top: memory leak fixed
+ps: new --ppid option selects by PPID
+watch: new --no-title option #179862
+handle SPARC Linux badness
+rare crash fixed
+compile with gcc 2.91.xx again
+more informative "ps --info"
+README update
+ps: compare more with "ps -C verylongname" #178127
+
+procps-3.1.4 --> procps-3.1.5
+
+ancient (2.x.xx era) data corruption fixed
+serious hidden-process problem (3.1.3+) fixed
+w: escape sequence vulnerability fixed
+
+procps-3.1.3 --> procps-3.1.4
+
+top: was trashing every "3" in a command name
+top: when killing a process, the PID was cut at a "3"
+top: more reliable %CPU
+update copyright dates (GPL & LGPL require this)
+RPM generation works now
+
+procps-3.1.2 --> procps-3.1.3
+
+uses /proc/*/wchan files when available
+top: user selection
+sysctl: add -e for Red Hat 8.0 boot scripts
+sysctl: the obvious --help, -V, and --version
+sysctl: some command line error checking
+w: stdout, not stderr -- thanks to Sander van Malssen
+
+procps-3.1.1 --> procps-3.1.2
+
+better RPM generation
+use C99 features
+some seLinux fixes
+now count Inact_laundry as needed #172163
+ps: fewer globals
+ps: hardware-enforced buffer protection
+ps: 1 kB smaller
+top: B command added (for bold on/off)
+top: handle old (and future) config files
+top: man page tweak
+top: old sort keys #167249
+top: out-of-bounds RT as "RT"
+top: several times faster
+top: t command fixed
+vmstat: -f
+vmstat: -s
+w: much faster
+watch: don't drop empty lines #171005
+watch: re-indented
+
+procps-3.1.0 --> procps-3.1.1
+
+vmstat faster on 2.5.xx kernels
+vmstat header fixed
+vmstat -a re-fixed
+
+procps-3.0.5 --> procps-3.1.0
+
+vmstat displays IO-wait time instead of bogus "w"
+can build w/o shared library (set SHARED=0)
+when IO-wait hidden, count as idle, not as sys
+pmap command added (like Sun has)
+do not crash GNU make 3.79
+top slightly faster
+
+procps-3.0.4 --> procps-3.0.5
+
+top tolerates super-wide displays
+better (?) RPM generation
+XConsole and top.desktop removed
+old build system removed
+code cleanup
+pgrep and pkill get "-o" (oldest matching process)
+had vmstat "bi" and "bo" output interchanged on 2.5.xx
+fix man page tbl directives
+top man page cleaned up
+
+procps-3.0.3 --> procps-3.0.4
+
+make top go faster
+Linux 2.2.xx ELF note warning removed
+only show IO-wait on recent kernels
+fix top's SMP stats
+fix top for "dumb" and "vt510" terminals
+in top, limit the priority values to -99 ... 99
+
+procps-3.0.2 --> procps-3.0.3
+
+more "make install" fixes
+lib CFLAGS working again
+top.1 codes fixed
+bad (int*) cast in top removed
+top runs faster
+libproc memory corruption fixed
+rant moved out of top.1 man page
+ability to SKIP installing things
+fixed ps --sort crash
+
+procps-3.0.1 --> procps-3.0.2
+
+top defaults to the old layout
+top defaults to sorting by %CPU
+fix top for non-SMP 2.2.xx and 2.0.xx
+new "make install" fixed
+vmstat -a fixed
+vmstat compiles with latest gcc-3.x
+vmstat does 64-bit time
+
+procps-3.0.0 --> procps-3.0.1
+
+sysctl handles net/ipv4/conf/eth1.0123/tag (VLAN interface)
+sysctl handles net.ipv4.conf.eth1/0123.tag (VLAN interface)
+"ps" is now about 2x faster than in procps-2.x.x
+"ps -F" now documented
+w works in KOI8-R locale
+vmstat documentation update
+"skill -n blah blah blah" lets you test options
+simple "make && make install" now
+
+procps-2.x.x --> procps-3.0.0
+
+designed to support Linux 2.0 through 2.5.41 and beyond
+new top, with optional: color, windowing, SMP stats
+runs faster
+more "it crashes" bugs fixed
+top shows IO-wait time
+vmstat can show active/inactive memory stats
+real-time info supported in ps
+correct "ps -o size" and "ps --sort size"
+new maintainers
+reduced memory usage for ps
+allow large PIDs to be specified
+SELINUX support is just a recompile away
+the "F" column shrank, so "ps -l" has more command name room
+64-bit time reduces the overflow problem
+support S/390, IA-64 emulator, and user-mode Linux
+oldps is gone
+configure script -- use "make -f Makefile.noam" as a backup
+"w" program better at determining what a user is doing
+more stable
+code at http://procps.sf.net/ now (SourceForge)
+
+Earlier changes, for those not using Debian already:
+
+more stable
+runs faster
+-F format option
+better error reporting in ps for unknown format specifiers
+BSD's sysctl options -b and -X
+top displays well on large-memory systems
+old BSD-style select-by-PID ("ps l$$")
+15-character user names
+ps 'f' ASCII art forest fixed
+add SIGSYS on i386
+top reports real RSS value
+large-memory systems work
+minimal ps program for embedded systems (minimal.c)
+BSD personality process selection fixed
+support locale (French) with ',' and '.' mixed up
+pgrep program
+includes the "kill" and "nice" programs
+don't chop non-tty ps output at 80 columns
diff --git a/smartt-top/README b/smartt-top/README
new file mode 100644
index 0000000..fd76134
--- /dev/null
+++ b/smartt-top/README
@@ -0,0 +1,72 @@
+COMPATIBILITY
+
+ This code is intended for use with Linux 2.2.xx, 2.4.xx,
+ 2.6.xx, and hopefully all future kernels. You should be
+ running a system with libc 6, but libc 5 might work too.
+
+INSTALLATION
+
+ make
+ make install
+
+ Only the second ("make install") is needed if you just
+ want to build and install procps in the normal way.
+
+ If you wish to test before installing, use the scripts
+ named t, v, and p to ensure that the correct libproc
+ (the new one) is used during your testing.
+
+ You may set SKIP to avoid building or installing things.
+ For example:
+
+ make SKIP='/bin/kill /usr/share/man/man1/kill.1' install
+
+ Use SHARED=0 to build procps without shared libraries.
+ This may be useful for installing in your home directory.
+
+ make SHARED=0 DESTDIR=$HOME install
+
+ Suppose you wanted to install stuff in strange places.
+ You might do something like this:
+
+ make usr/bin=/tmp/Q/i/ DESTDIR=/tmp/Q install="install -D" ldconfig=echo install
+
+ If cross-compiling, you might need to set lib64 to
+ either "lib" or "lib64". You might need to set m64 to
+ -m64, -m32, or nothing at all. Some examples:
+
+ make lib64=lib m64=-m32 # for a bi-arch gcc
+ make lib64=lib64 CC=x86_64-gcc
+ make lib64=lib CC=alpha-gcc
+
+PACKAGING
+
+ If you are a downstream maintainer (packager) for a Linux distribution,
+ please avoid causing troubles. This section applies to you.
+
+ Send patches in regularly. Many patches made by vendors have been buggy,
+ some quite severely so. Sending in a patch will at least get it reviewed,
+ if not included. There is a procps test suite that must be passed.
+ Forward all bug reports. If your bug database is public and busy enough
+ to bother with, please make this known. Follow Debian's lead in making
+ the bug database easy to comment on via email w/o need for an account.
+
+ Do not change the user interface. Many of the programs are intended to be
+ compatible with Solaris, FreeBSD, AIX, IRIX, Tru64, and the UNIX standard.
+ Your nice new command options WILL BE BROKEN as needed to ensure that
+ procps remains compatible with the rest of the world. Sysadmins hate to
+ deal with incompatible behavior. If you need a new option, ask for it.
+
+ For normal packages, ensure that you do not add debugging flags
+ to the CFLAGS variable. If debugging flags are present, the Makefile
+ will avoid adding several optimizations that would interfere with gdb.
+
+ There should be no need to modify the Makefile. You can set variables
+ on the "make" command line or use "make -e" to pass variables from
+ the environment.
+
+BUG REPORTS
+
+ Debian users should use the Debian bug tracking system.
+ Email to albert@users.sf.net or csmall@debian.org or
+ procps-feedback@lists.sf.net will also work.
diff --git a/smartt-top/README.top b/smartt-top/README.top
new file mode 100644
index 0000000..3f225d5
--- /dev/null
+++ b/smartt-top/README.top
@@ -0,0 +1,545 @@
+Credit for this belongs to:
+Jim / James C. Warner, <warnerjc@worldnet.att.net>
+
+----------------------------------
+
+Ok, ok, I yield -- most of what follows has been removed from the manual page
+and packaged separately as this README (hey, it was only TEMPORARY insanity).
+
+Of course, that means that now absolutely nobody will ever read it.
+
+This is probably a good thing...
+
+
+## Table of Contents ---------------------------------------------------##
+ # the only darn thing that wasn't in the man page
+ CUSTOMIZING the Sources
+ # the following carry their original topic numbers
+ DIFFERENCES / New Features
+ Interface Etiquette
+ Expanded Configurable Display Support
+ Enhanced Field/Column Management
+ Customization Flexibility
+ NOTES and Rantings
+ The top Binary
+ Comparing Performance
+ Cost of Stuff
+ The top Sources
+ EXAMPLES of Windows
+ The 'A' Mode Command Toggle
+ STACKIN' & WHACKIN' Windows
+ ALL TOGETHER Now, Window(s)
+
+
+## CUSTOMIZING the Sources ---------------------------------------------##
+
+Listed below are the conditionals available should you wish to recompile
+this top. The author's favorite is: PRETEND4CPUS.
+
+That's the #define allowing you to simulate an SMP environment, and
+(perhaps) impress your friends. It's currently set to display four
+separate CPUs, but could easily be changed.
+
+ Caution: do NOT use this provision in an effort to impress someone
+ who truly possesses such a machine! The fact that all 4
+ CPUs show the same dynamic results will likely have the
+ opposite effect.
+
+
+//#define ATEOJ_REPORT /* report a bunch of stuff, at end-of-job */
+//#define CASEUP_HEXES /* show any hex values in upper case */
+//#define CASEUP_SCALE /* show scaled time/num suffix upper case */
+//#define CASEUP_SUMMK /* show memory summary kilobytes with 'K' */
+//#define POSIX_CMDLIN /* use '[ ]' for kernel threads, not '( )' */
+//#define PRETEND2_5_X /* pretend we're linux 2.5.x (for IO-wait) */
+//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */
+//#define PRETENDNOCAP /* use a terminal without essential caps */
+//#define SORT_SUPRESS /* *attempt* to reduce qsort overhead */
+//#define STDOUT_IOLBF /* disable our own stdout _IOFBF override */
+//#define USE_LIB_STA3 /* use lib status (3 ch) vs. proc_t (1 ch) */
+//#define WARN_NOT_SMP /* restrict '1' & 'I' commands to true smp */
+
+
+## 6. DIFFERENCES / New Features ---------------------------------------##
+ The following summarizes differences between this top and your
+ former top. It was originally based on procps-2.0.7. However,
+ except for the separate/summary CPU toggle, all of these differ-
+ ences also apply through procps-2.0.10.
+
+ 6a. Interface Etiquette
+ -*- Input and output are far more carefully implemented in
+ this top. You won't be subjected to 4 - 5 'Unknown command'
+ messages should you press the wrong key.
+
+ -*- You need suffer a confirmation message only when the results
+ of a command are not obvious by their effects on the display.
+
+ -*- The Help screen will no longer overflow, even when running
+ with a 24 row xterm (vt100).
+
+ -*- The fields selection/ordering screens do not carelessly
+ destroy important information through unintended line wraps.
+
+ -*- Should you narrow a xterm window to less than 80 columns
+ while this top is running, you will not be left with an
+ utterly worthless, embarrassing display.
+
+ 6b. Expanded Configurable Display Support
+ -*- In an SMP environment, you can choose between a summary dis-
+ play or you may show each cpu separately. No longer must
+ this choice be irrevocably made at startup.
+
+ -*- There are new fields and with this top, any field is
+ selectable for sorting. Plus, your sorted column can be
+ instantly reversed with just a single keystroke.
+
+ -*- You may optionally apply 2 distinct types of highlighting to
+ running tasks and/or sorted columns. With this top, you'll
+ be able to instantly spot running tasks and always know the
+ current sort field.
+
+ -*- While you could continue to use the more familiar (and
+ boring) monochrome display, you might want to try this top's
+ new color display. You can even create your own unique col-
+ ors used in summaries, messages, headings and tasks, each of
+ which can be made persistent until you choose to change them.
+
+ -*- Up to four separate windows can be displayed simultaneously,
+ giving you four separate ways to sort and view the tasks cur-
+ rently cluttering up your system. You could have one view by
+ pids, another by cpu usage, yet another showing memory con-
+ sumption. You get the idea...
+
+ -*- Each window comes with pre-configured (but user configurable)
+ fields and you can size each window individually.
+
+ -*- Virtually every one of this top's options (summaries, fields,
+ colors, sorted column, etc.) is separately configurable for
+ each of those four windows.
+
+ Heck, you can even change a window's name, if you don't care
+ for top's choices. Your changes will be reflected not only
+ when you're in what top calls alternate-display mode but also
+ on his special new 'Windows' help screen.
+
+ -*- And, [ ** Drum-Roll + Ta-Da ** ] with just one keystroke you
+ can quickly switch between full-screen and multiple window
+ modes! Or, with a different keystroke, toggle a single win-
+ dow Off for now, then On again later!!
+
+ 6c. Enhanced Field/Column Management
+ -*- Many Field/Column names have been changed to make them more
+ intuitive, more self-descriptive. And with this top you
+ won't be fooled with field choices that are "not yet imple-
+ mented".
+
+ -*- Task memory statistics are more meaningful and more accurate.
+
+ -*- You'll finally have complete display integrity regardless of
+ field selections, their order or screen width. And that
+ means the command column no longer need be kept as the right-
+ most field, lest your screen turn to <bleep> when all the
+ following columns get misaligned.
+
+ 6d. Customization Flexibility
+ -*- You have complete program naming freedom with no internal
+ ties to a specific personal configuration file. Symbolic
+ links could be used to establish different configuration
+ files reflecting the different personalities of your cus-
+ tomized "tops", under whatever aliases you've used.
+
+ Thus, you could have an alias for running top in 'Batch
+ mode', another for when you work from the Linux console and
+ maybe a third used with X-Windows. All of that, yet still
+ just a single binary image!
+
+ -*- All of your configuration choices can be preserved in a per-
+ sonal configuration file, including any changes made on a
+ per-window basis. Thus, once you personalize things they
+ remain personalized until you decide to change them again.
+ This top has been completely cured of:
+ i-cant-remember-so-please-do-that-all-over-again
+ ( and again, and again ... )
+
+ The bottom line is this: if you save your configuration
+ before quitting top, upon restart the display will appear
+ exactly as you left it. And that means you no longer have to
+ keep top running until-the-end-of-time (ok, a long time
+ anyway), lest your customizations go bye-bye.
+
+
+## 7. NOTES and Rantings -----------------------------------------------##
+ 7a. The top Binary
+ To whom it may (should) concern: this top, even with its vastly
+ expanded capabilities, is only slightly larger than the old top.
+ Were it not for extensive help text and additional sort callbacks,
+ it would be smaller.
+ Throw source carelessly at objectives, it will
+ produce equally careless machine instructions.
+ example: (num_pages - an_address)/1024 == duh?
+ kicker: document result as broken, due to elf!
+ ----------------------------------------------
+ I know you're out there, are you getting this?
+
+ Now, as for all those new capabilities like colors and windows and
+ highlighting, you'd expect this top to be the "mother of all pigs"
+ compared to old top -- right?
+
+ Yea, with this top expect following piglets:
+ . A smaller virtual image and resident footprint
+ . Slightly fewer major page faults
+ . A large reduction in minor page faults for SMP
+ . The same or better response time
+ . The same or even less CPU costs
+
+ Ideally any comparison of the old and new top should be against
+ the same libproc format (32-bit or 64-bit tics) and run in a true
+ or simulated SMP environment (producing separate CPU stats). This
+ latter requirement will coax old top into handling his own
+ '/proc/stat' access -- something this top always does, but with
+ less cost.
+
+ 7b. Comparing Performance
+ Even with equivalent libraries and '/proc/stat' access, it's dif-
+ ficult to accurately compare tops using their own displays.
+ Results for these cpu-intensive programs (who frequently exceed
+ their time-slice) generally show a wide disparity in %CPU. This
+ is due to differing call patterns, kernel preemptions and the tim-
+ ing of process snapshots. For slightly better results, start each
+ program with the following commands:
+ ./old-top -d 0.5
+ nice -n-10 ./new-top -d 0.4
+
+ While actually putting this top at a performance disadvantage, the
+ higher scheduling priority and staggered timing will periodically
+ yield a somewhat truer picture. You could even reverse those
+ roles and get similar results.
+
+ The most consistent performance results will be obtained 'off-
+ line', using your shell's time pipe or the time program itself.
+ And even in a single processor environment or without equivalent
+ libraries, total cpu costs (user time + system time) are similar.
+
+ However, this top's cpu costs ARE influenced by the capabilities
+ you choose to exploit, even if they don't SEEM to be reflected in
+ such timings. So let's examine some...
+
+ 7c. Cost of Stuff
+ Colors Cost -- Nada (almost).
+ Once the terminfo strings are built (at and during a user's
+ behest) they are SAVED with each window's stuff. And while
+ there will be extra tty escape sequences transmitted because of
+ colors, it makes no difference which 'char *' is actually used.
+
+ Highlighting Cost -- Nada (maybe), or blame it on Rio.
+ On second thought, let's blame it on the user.
+
+ For row highlighting, there is only the cost of those extra tty
+ escape sequences (same as for colors). For column highlight-
+ ing, there is a fairly significant cost associated with column
+ transition management combined with even more tty output.
+ These increased costs are incurred on every task display row.
+
+ Sooo... hey USER -- do NOT highlight COLUMNS. You shouldn't
+ need a constant visual reminder of your chosen sort field.
+ However, if you forget which field top is sorting it can serve
+ as a quick visual reminder.
+
+ Windows Cost -- Nada (if just 1 window).
+ If more than 1 window, almost certainly NOT Nada so blame it on
+ reality. Colors are not an issue, but those sort fields are.
+
+ If we could trust the user to always select the same 'c' state,
+ 'S' state and sort field (hey, why ya got multiple windows then
+ user, huh?) AND if we can trust someone to recompile top with a
+ #define enabled, then we could achieve 'Nada'.
+
+ Ok, not likely, so we're gonna' be doing multiple sorts. BUT,
+ it may not be as bad as it sounds. Those sorts involve point-
+ ers only. And, that's as good as it gets ! (right Mr. N?)
+
+ 7d. The top Sources
+ top.h
+ Unlike his predecessor, this top has a proper header file. It
+ contains ONLY declarations, NOT definitions. And there are
+ several conditionals present to help with further customiza-
+ tions and experimentation. All are Off by default.
+
+ top.c
+ Hopefully proves that source code needn't be a disorganized,
+ misaligned MESS. And, WHO says a source listing shouldn't
+ occasionally make you SMILE? Why, top.c even does a darn good
+ job of following the suggestions in a document hardly anybody
+ seems to observe.
+
+ the Linus Torvalds CodingStyle guidelines ...
+ -*- -*- -*- on indentation + etc. -*- -*- -*-
+ well almost all, except for those stinkin'...
+
+ I suppose even Linus Torvalds is entitled to err now and again.
+ How so you say? Tabs, me' bucko, stinkin' tabs! That, plus the
+ simplistic position regarding indentation espoused in that other-
+ wise excellent document.
+
+ -*- Rant On, and on -*-
+ Let's compare two approaches to the tab/indentation issue with a
+ small code sample using tabs then spaces. This snippet happens to
+ be the key to top's use of dynamic colors on many static screens,
+ while also ensuring screen width isn't exceeded so as to avoid
+ line wraps. We'll view just the first 40 columns, assuming one
+ wishes to occasionally provide comments to the right of actual
+ code (you do, don't you?).
+
+ Then YOU decide which approach makes the most SENSE!
+
+ Stinkin' Tabs versus Spaces: the Linus way
+ Hey, where'd my +----+----1----+----2----+----3----+----4+
+ many code lines | while (*sub_beg) { :
+ up-and-gone-to? | switch (*sub_end:
+ | case 0: :
+ Gosh, wonder if | \ Tabs Induced / :
+ Linus expects a | case 1: :
+ fellow to stick | + WASTE-Lands! + case 5: :
+ his comments on | :
+ the left side?! | + Not a Living + :
+ | :
+ Ever see source | + line-of-code + :
+ with not enough | :
+ whitespace; and | / To Be Found! \ :
+ this is better? | default::
+ | :
+ Oh lookie here, \ } :
+ there's just a hint of REAL code! ----> if (0 >= room) b:
+ / } /* end: while 'subtrin:
+ +----------------------------------------+
+
+ Spaces versus Stinkin' Tabs: the other way
+ +----+----1----+----2----+----3----+----4+
+ Wow, now this is | while (*sub_beg) { :
+ Visible hackin'! | switch (*sub_end) { :
+ | case 0: :
+ Hmmm, wonder how | *(sub_end + 1) = '\0'; :
+ many programmers | case 1: case 2: case 3: case:
+ read those lines | case 5: case 6: case 7: case:
+ from the LEFT to | cap = Curwin->captab[(int:
+ the RIGHT? This | *sub_end = '\0'; :
+ "innovation" may | PUTP("%s%.*s%s", cap, roo:
+ possibly benefit | room -= (sub_end - sub_be:
+ those particular | sub_beg = ++sub_end; :
+ kinds of people, | break; :
+ you agree? Duh! | default: :
+ | ++sub_end; :
+ AND, there might | } :
+ even be room for | if (0 >= room) break; :
+ unseen comments! | } /* end: while 'subtrings' */ :
+ +----------------------------------------+
+
+ Gosh, I just don't KNOW -- it's such a TOUGH choice...
+
+ Oh you Stinkin' Tabs: correspondence, Who-Cares; documentation,
+ Oh-Alright; even scripts, Well-If-You-Must. But you have NO place
+ within the code-space of MY C-source listing! So be gone
+ already!!
+
+ In Summation...
+ - If you want to use tabs to the right of the code, go-for-it.
+ But PLEASE, not ever in the C-source code-space, thank-you-
+ kindly. Just use three little ol' spaces (exactly 3, no-more,
+ no-less) where you WOULD have stuck a stinkin' tab.
+
+ We'll get far more READABLE files, much less WAISTED precious
+ horizontal space, more consistent CURSORS and on, and ON, AND
+ ON! Plus, without those awful *the-devil's-own-handiwork*, the
+ aforementioned document need NEVER speak of their EVILS again.
+
+ - Lastly, since SPACES (not stinkin' tabs) are SO beneficial,
+ maybe we should use just a few more of 'em. Some of those C-
+ thingies are VERY sensitive -- they don't like being TOUCHED
+ by any other syntax element! Which ones? Why these guys:
+
+ braces, reserved words and binary operators
+ ( it's the TRUTH, they told me themselves )
+
+ It's so EASY to keep 'em HAPPY! And lo-and-behold, the combi-
+ nation of <sp>thingy<sp> turns out to be a darn effective bug
+ repellent, too. So much so, one can actually code while
+ TOTALLY NUDE yet still avoid them ol' bug-bytes (sic-sic)!
+ step
+ down_from
+ me_punctilious
+ soap-box_once_again
+ [1 +5 +5 +5 = huh?]
+
+
+## 4c. EXAMPLES of Windows ---------------------------------------------##
+
+ -*- The 'A' Mode Command Toggle -*-
+ Here's what you'll see when you first invoke the alternate-display
+ mode interactive command.
+
+ This particular display was produce on a VT100 xterm, with only 24
+ rows. All four task displays are visible, but they could not be sized
+ the same. Available lines are parceled out in the fairest way possi-
+ ble so the last two task displays have an extra line each.
+
+ Notice the 'current' window name in the summary area -- it's been
+ emphasized because the associated task display is visible. Since
+ 1:Def has a task area, the full range of interactive commands would be
+ at your disposal. But remember, many of those commands will apply
+ only to window 1:Def.
+
+ +--------------------------------------+
+ 1:Def name is bold, |1:Def - 15:46:37 up 16:25, 9 users, :
+ thus all commands |Tasks: 76 total, 1 running, 75 sle:
+ will be available. |Cpu(s): 0.7% user, 1.3% system, :
+ |Mem: 126588k total, 123688k used,:
+ |Swap: 265032k total, 8232k used,:
+ |______________________________________:
+ Tough luck windows |1__PID_USER______PR__NI_%CPU____TIME+_:
+ #1 & 2 - you lost | 7343 jtwm 16 0 0.9 0:00.59:
+ one line each -- | 7339 jtwm 9 0 0.0 0:00.02:
+ guess you'll just |__7337_root_______9___0__0.0___0:01.30:
+ have to learn how |2__PID__PPID_Command____________TIME+_:
+ to live with it. | 997 952 kdeinit 17:59.59:
+ | 1115 952 kdeinit 2:16.47:
+ |__1803__1116_led_______________1:55.30:
+ |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_:
+ The #3 & #4 windows | 4634 12.3 15620 0 15m 860 14m :
+ better not gloat | 7337 11.3 14396 92 13m 36 13m :
+ over 1 extra line. | 923 10.6 30524 16m 13m 1120 12m :
+ That user could yet |___991__7.2__9492__316_9176___12_9164_:
+ sock 'em with the |4_UID_USER_____GROUP____TTY________PID:
+ 'n' command and | 43 xfs xfs ? 806:
+ take those lines, | 0 ykde users pts/7 5561:
+ plus others, away! | 0 wgnome users pts/7 5560:
+ | 0 root root pts/7 5325:
+ +--------------------------------------+
+
+ So, what say we start applying some of those "full range of interac-
+ tive commands"?
+
+ Onward + Downward...
+
+ -*- STACKIN' & WHACKIN' Windows -*-
+ Whoa, hold on mate. Someone has already whacked these windows. See,
+ there are no task areas for windows 1:Def and 4:Usr. Well, we can at
+ least retrace their steps...
+
+ Here's what was done, after issuing the 'A' command and entering
+ alternate-display mode.
+ 1) When #1 was the 'current' window, '-' was pressed,
+ toggling Off the associated task display
+ ( if 'l t m' had been applied to its summary, too )
+ ( then there'll be only a msg line when 'current' )
+ 2) Then the 'w' key was struck to cycle backward,
+ making 4:Usr the 'current' window
+ (could have used 'a a a', if one likes to type)
+ 3) Then step #1 was repeated, and bye-bye window #4
+ 4) Finally, window #2 was made the 'current' window
+ ( Q. how many keystrokes were used? )
+ ( A. minimum of 2: 'a a' or 'w w'. )
+
+ +--------------------------------------+
+ No 'l','t','m','1' |2:Top - 15:48:35 up 16:27, 9 users, :
+ commands have been |Tasks: 75 total, 1 running, 74 sle:
+ issued here, |Cpu(s): 2.0% user, 0.7% system, :
+ but... |Mem: 126588k total, 123712k used,:
+ |Swap: 265032k total, 8232k used,:
+ |______________________________________:
+ #2's been changed; |2__PID__PPID_Command____________TIME+_:
+ user applied a 'c' | 997 952 kdeinit: konsol 18:00.70:
+ command (when it | 1115 952 kdeinit: konsol 2:16.47:
+ was current) - now | 1803 1116 led tiptop.HELP 1:55.30:
+ shows cmd lines vs. | 923 922 X :0 1:09.60:
+ program names; | 973 1 klaptopdaemon 0:59.63:
+ still seems to be | 981 952 /usr/bin/artsd 0:48.63:
+ sorted on TIME+ | 987 1 kdeinit: kdeskt 0:24.34:
+ though |___991_____1_kdeinit:_kicker___0:04.59:
+ |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_:
+ This #3 guy appears | 4634 12.3 15620 0 15m 860 14m :
+ to still be running | 7337 11.3 14396 92 13m 36 13m :
+ with the supplied | 923 10.6 30544 16m 13m 1120 12m :
+ defaults, but no | 991 7.2 9492 316 9176 12 9164 :
+ telling what damage | 7329 7.0 9036 140 8896 36 8860 :
+ might have been | 1115 6.9 8956 160 8796 36 8760 :
+ done to it's | 987 6.4 8668 524 8144 20 8124 :
+ summary info stuff | 1131 6.4 8268 144 8124 36 8088 :
+ +--------------------------------------+
+
+ And that's what brought us to this current state. No, wait. Oh
+ lordy, will you look at that -- someone has changed the name of win-
+ dow #2 from 'Job' to 'Top'!
+
+ How'd they do that? Well, they just issued the 'g' interactive com-
+ mand, of course. That command is available whenever alternate-display
+ mode is active and always impacts just the 'current' window. Gosh,
+ you can even issue the 'g' command when 'l' has toggled Off the very
+ summary area line containing the window name!
+
+ Almost Done...
+
+ -*- ALL TOGETHER Now, Window(s) -*-
+ Here, the window 1:Def task display has been toggled Off but it
+ remains the 'current' window. Since there is no task area, many com-
+ mands will be restricted. However, the commands ('l', 't', 'm', '1')
+ affecting the summary area, as well as some other global commands
+ ('k', 'Z', etc.), would still be active.
+
+ Notice that the Mem and Swap lines are not shown. This means that the
+ loser (oops, user) has, in fact, issued the 'm' command! Now, if you
+ were to cycle the 'current' window with the 'a' or 'w' commands, the
+ task display would remain the same (except possibly growing/shrinking
+ slightly) but the summary area would change periodically.
+
+ The comments to the left of the image provide additional insights into
+ how things came to be. Note especially the comments for window 4:Usr
+ -- the one with some empty rows...
+
+ 1:Def no highlight, +--------------------------------------+
+ thus disabled cmds: |1:Def - 15:50:32 up 16:29, 9 users, :
+ b,i,n,u,x,y, etc. |Tasks: 75 total, 2 running, 73 sle:
+ & m = lost Mem/Swap |Cpu(s): 10.6% user, 0.0% system, :
+ |______________________________________:
+ 2:Job was very busy: |2__PID__PPID_Command____________TIME+_:
+ 'n' cmd, w/ 7 tasks | 80 1 ( khubd ) 0:00.00:
+ 'c' cmd, cmd line | 6 0 ( kreclaimd ) 0:00.00:
+ 'O' cmd, sort cmd | 9 1 ( mdrecoveryd ) 0:00.00:
+ 'R' cmd, sort bkwd | 11358 1 /bin/bash/ /usr 0:00.00:
+ 'x' cmd, hi column | 1297 1 /sbin/mingetty 0:00.00:
+ (when 2 WAS current) | 683 1 xinetd -stayali 0:00.00:
+ |___836_____1_login_--_root_____0:00.00:
+ 3:Mem has altered |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_:
+ some std defaults: | 4634 12.3 15620 0 15m 860 14m :
+ 'y' turned Off | 7337 11.3 14396 92 13m 36 13m :
+ 'x' turned On | 923 10.6 30544 16m 13m 1120 12m :
+ (when 3 WAS current) | 991 7.2 9492 316 9176 12 9164 :
+ |__7329__7.0__9036__140_8896___36_8860_:
+ Huh? 4:Usr has some |4_UID_USER_____GROUP____TTY________PID:
+ blank rows! ? ? ? ? | 0 jtwm root pts/2 5561:
+ Aha, the 'i' command | 0 root root ? 5560:
+ applied (when 4 WAS | :
+ current); could be | :
+ reversed with '=', | :
+ when 4 IS current! +--------------------------------------+
+
+ Ok now, how about that 'current' window 1:Def and its unseen tasks?
+ At any time, you can quickly retrieve lost tasks in a number of ways:
+ 1) Press '-', toggling just the 'current' window
+ 2) Press '_', toggling all visible/invisible windows
+ ( 1:Def is the only window currently not shown )
+ ( afterward, it'll be the only window showing! )
+ * 3) Press '+', forcing all task displays to become visible
+ 4) Press 'A' to return to full-screen mode,
+ with only 1:Def tasks shown and without a window name
+
+ Now that should be enough ways of getting a task area visible again to
+ satisfy almost any user, don't ya think?
+
+ Note: Use #3 above when you've messed up your screen beyond
+ redemption. The four task displays will reappear, nice and even.
+ They will also have retained any customizations you had previously
+ applied, except for the 'i' (idle tasks) and 'n' (max tasks) com-
+ mands.
+
+ That's It ! Piece of Cake !! Enjoy them there windows !!!
+
diff --git a/smartt-top/TODO b/smartt-top/TODO
new file mode 100644
index 0000000..7831ea0
--- /dev/null
+++ b/smartt-top/TODO
@@ -0,0 +1,151 @@
+-------------------------- general ------------------------
+
+Consider using glibc obstacks for memory allocation.
+
+Implement /usr/proc/bin tools like Solaris has.
+The prstat command is interesting, like top in batch mode.
+SCO has a pstat command.
+
+Don't these really belong in the procps package?
+ killall pstree fuser lsof who
+(they are maintained elsewhere, which causes version problems)
+
+OpenBSD has a pfind command.
+
+Cache results of dev_to_tty.
+
+---------------------- kernel -------------------------
+
+Add an "adopted child" flag to mark processes that are not
+natural children of init. This can make --forest work better.
+
+Supply better data for top's CPU state display. Currently top has
+to subtract old numbers from new numbers and divide that result by
+the number of processors. The kernel won't even supply the number
+of processors in a portable way.
+
+Supply data for the ADDR and JOBC fields.
+
+Support & supply data for SL and RE.
+
+Add a /proc/*/tty symlink to eliminate guessing when /proc/*/fd is
+not accessable.
+
+Add /proc/*/.bindata files to avoid string parsing. It should be an array
+of 64-bit values on all machines. New entries go on the end and obsolete
+ones get filled in with something logical -- entries must never be deleted!
+
+Add all the stuff Solaris has. This would also replace ptrace.
+
+---------------------- watch --------------------------
+
+Tolerate UTF-8.
+
+Tolerate color, bold, underline, etc. #129334
+
+Tolerate stderr. #420377 #155227 #225549
+
+Tolerate VT100 line-drawing characters. Maybe translate them.
+
+---------------------- w --------------------------
+
+The LOGIN@ column sometimes has a space in it. This makes correct
+scripting difficult.
+
+Time formats are demented.
+
+---------------------- vmstat --------------------------
+
+Extract /proc/diskstats parsing from vmstat into libproc somewhere.
+
+--------------------- libproc ----------------------
+
+Stop storing fields with duplicate info (often different
+units: kB and pages, seconds and jiffies) in the proc_t struct.
+
+Use own readdir code (assembly language) because glibc sucks ass.
+
+---------------------- top -------------------------
+
+Share more stuff with ps.
+
+don't truncate long usernames
+
+have a --config option
+
+---------------- ps for now, maybe move to libproc ------------------
+
+With forest output and a tty named /dev/this_is_my_tty, the position
+of the command name gets messed up. (we print too many spaces) (fixed?)
+
+Fix missing stuff for these formats: FB_j FB_l FB_v HP_f HP_l HP_fl JFMT OL_m
+(jobc,cpu,sl,re,cpu,prmgrp,m_swap,m_share,vm_lib,m_dt)
+Note that "cpu" has two meanings.
+
+Add Beowulf support. This is ugly, since the current patches use a
+daemon to collect info and add a HOST field after the PID field.
+
+Query optimizer, put cheap/required process selection first.
+
+Avoid reading both /proc/*/status and /proc/*/stat.
+
+Support printing the client hostname (the FROM that w(1) uses) in place
+of a pty.
+
+Disambiguate narrow tty info. (/dev/tty7 == /dev/pts/7 now)
+1------8 1--4
+ttyS2 S2
+ttyI31 I31
+pts/7 7 Short form could be /999.
+pts/9999 9999 Short form could just be trunctuated to /999.
+tty7 7 Short form could be vc-7.
+tty63 63 Short form could be vc63.
+
+Internationalization, as specified by XPG3, Volume 1, Commands and Utilities.
+(and suggested by Unix98) LC_TIME affects date format.
+
+----------------------- ps -----------------------
+
+Add an option to select all processes that a user can kill.
+(related to RUID, EUID, tty, etc. -- but maybe ignore root power)
+
+Add a nice display option for killing things.
+ruser,euser,ppid,pid,pmem,stime,args
+
+For RT stuff:
+pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm
+
+For job control:
+stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
+
+Make the column alignment algorithm support this:
+ FOO BAR
+ 8 44444
+ 453 45
+ 45 2989
+ 63666 0
+ 34 333
+(useful for the UNIX tty and time values, since the time might look
+like 100-10:40:32 for old processes and the tty might have extra room)
+
+Improve long sort/format specifiers documentation and fill in the missing
+code as much as the kernel can support. Make sure that memory amounts are in
+pages when they should be and in kB when they should be, not backwards.
+
+output encoding: UTF8 --nul --null
+
+Make BSD formats use non-standard BSD time format, at least when it
+doesn't violate the "no whitespace" rule.
+
+Better unmangling of '?' as a tty. The shell destroys '?' when there
+is a filename that matches. If the argument seems like garbage,
+check for a file that might have screwed up the '?'.
+
+If the 'O' option is given something already implied by 'O',
+assume the user wanted a sorting option.
+
+Conflict:
+Digital THREAD is user,pcpu,pri,scnt,wchan,usertime,systime
+AIX THREAD is uname,pid,ppid,tid,S,C,PRI,scount,WCHAN,F,tty,bnd,comm
+AIX looks like this:
+ USER PID PPID TID S C PRI SC WCHAN FLAG TTY BND CMD
diff --git a/smartt-top/debian/NEWS b/smartt-top/debian/NEWS
new file mode 100644
index 0000000..262c361
--- /dev/null
+++ b/smartt-top/debian/NEWS
@@ -0,0 +1,8 @@
+procps (1:3.2.7-10) unstable; urgency=low
+
+ * In this version I revert a long-standing (since potato I think) patch that
+ incorrectly changed the time output for w for its Idle, PCPU and JCPU
+ times. The patch should have only changed the times if the -o flag was
+ used, instead of all the time. This is now fixed.
+
+ -- Craig Small <csmall@debian.org> Fri, 09 Jan 2009 16:41:02 +1100
diff --git a/smartt-top/debian/README.Debian b/smartt-top/debian/README.Debian
new file mode 100644
index 0000000..c3f75d2
--- /dev/null
+++ b/smartt-top/debian/README.Debian
@@ -0,0 +1,35 @@
+README for Debian package of procps
+===================================
+
+ipv6 sysctl keys
+----------------
+Modern Debian kernel packages have the IPv6 module compiled in by default.
+This means that the /proc/sys/net/ipv6 directory exists when the procps
+startup script runs.
+
+However if you make your own kernel then you may make ipv6 a module and
+get a race condition between the netbase and procps startup scripts. This
+is because netbase, by default, causes ipv6 module to be loaded but
+they don't (and cannot) depend on each-other.
+
+The solution is to either:
+ - Not put ipv6 keys into /etc/sysctl.d/* or /etc/sysctl.conf
+ - Compile the ipv6 module into the kernel
+ - Load the module early by putting ipv6 into /etc/modules
+ - Make a init script dependency by adding netbase to the Required-Start
+ line in /etc/init.d/procps
+
+pgrep
+-----
+pgrep is a new program, using the Unix standard name for something that
+greps for processes. If you are looking for Perl compatible regular
+expression grep, it is called pcregrep.
+
+forks
+-----
+Procps upstream is forked. This one comes from procps.sf.net I'm really
+not interested when the others have new versions so please don't bug me about
+it. However if you see something neat in the others and would like it in
+the Debian one, report a *wishlist* level bug about it.
+
+ Craig Small <csmall@debian.org>
diff --git a/smartt-top/debian/README.sysctl b/smartt-top/debian/README.sysctl
new file mode 100644
index 0000000..13970ff
--- /dev/null
+++ b/smartt-top/debian/README.sysctl
@@ -0,0 +1,13 @@
+Kernel system variables configuration files
+
+Files found under the /etc/sysctl.d directory that end with .conf are
+parsed within sysctl(8) at boot time. If you want to set kernel variables
+you can either edit /etc/sysctl.conf or make a new file.
+
+The filename isn't important, but don't make it a package name as it may clash
+with something the package builder needs later. It must end with .conf though.
+
+My personal preference would be for local system settings to go into
+/etc/sysctl.d/local.conf but as long as you follow the rules for the names
+of the file, anything will work. See sysctl.conf(8) man page for details
+of the format.
diff --git a/smartt-top/debian/changelog b/smartt-top/debian/changelog
new file mode 100644
index 0000000..b9f0b15
--- /dev/null
+++ b/smartt-top/debian/changelog
@@ -0,0 +1,1534 @@
+procps (1:3.2.8-9ubuntu3) maverick; urgency=low
+
+ * debian/sysctl.d/10-ptrace.conf: adjusted to include new yama path.
+
+ -- Kees Cook <kees@ubuntu.com> Tue, 06 Jul 2010 15:23:12 -0700
+
+procps (1:3.2.8-9ubuntu2) maverick; urgency=low
+
+ * debian/procps.upstart: duplicate the original init script behavior of
+ not failing on unknown keys (required for cross-kernel upgrades, etc).
+
+ -- Kees Cook <kees@ubuntu.com> Wed, 09 Jun 2010 10:18:14 -0700
+
+procps (1:3.2.8-9ubuntu1) maverick; urgency=low
+
+ * Merge with Debian unstable; remaining changes:
+ - debian/rules (Ubuntu-specific):
+ - use upstart for init script.
+ - install sysctl files from new sysctl.d directory.
+ - debian/sysctl.d (Ubuntu-specific):
+ - 10-console-messages.conf: stop low-level kernel messages on console.
+ - 10-network-security.conf: enable rp_filter and SYN-flood protection.
+ - 10-keyboard.conf.powerpc: mouse button emulation on PowerPC.
+ - 10-zeropage.conf: safe mmap_min_addr value for graceful fall-back.
+ - README: describe how this directory is supposed to work.
+ - debian/upstart, debian/postinst (Ubuntu-specific):
+ - upstart configuration to replace old style sysv init script.
+ * Fixed upstream: debian/patches/60_linux_version_init.dpatch
+ * Added debian/sysctl.d/10-ptrace.conf: describe new PTRACE setting.
+
+ -- Kees Cook <kees@ubuntu.com> Tue, 08 Jun 2010 11:45:44 -0700
+
+procps (1:3.2.8-9) unstable; urgency=low
+
+ * Fix for patch on cgroup crash Closes: #579824
+
+ -- Craig Small <csmall@debian.org> Sun, 02 May 2010 21:06:06 +1000
+
+procps (1:3.2.8-8) unstable; urgency=low
+
+ * Work-around for kfreebsd HZ problem Bug #460331
+ * Added comments about ipv6 keys to README Closes: #507788
+ ipv6 is compiled in by default so this impacts custom kernels only
+ * Added note the ipv4/tcp* keys effect ipv6 too Closes: #571281
+ * watch interprets ANSI color Closes: #129334
+ * Fixed off by one for top -u username Closes: #571790
+ * Document maj_flt and min_flt in ps.1 Closes: #434221
+
+ -- Craig Small <csmall@debian.org> Mon, 01 Mar 2010 12:37:06 +1100
+
+procps (1:3.2.8-7) unstable; urgency=low
+
+ * Adjusts skill_multiarg.patch so multiple args work Closes: #569030
+
+ -- Craig Small <csmall@debian.org> Wed, 10 Feb 2010 21:36:05 +1100
+
+procps (1:3.2.8-6) unstable; urgency=low
+
+ * Removed non-linux binaries from package Closes: #568255
+ * Cleaned up some extra files not needed in hurd and kfreebsd
+ * top handles numeric arguments better Closes: #358724
+ * Explain what SWAP means in top.1 Closes: #217841
+ * w makes better guesses at process Closes: #187808
+
+ -- Craig Small <csmall@debian.org> Fri, 05 Feb 2010 23:03:38 +1100
+
+procps (1:3.2.8-5) unstable; urgency=low
+
+ * Need both ncurses*-dev depends Closes: #568038
+
+ -- Craig Small <csmall@debian.org> Tue, 02 Feb 2010 14:21:46 +1100
+
+procps (1:3.2.8-4) unstable; urgency=low
+
+ * Build-depends on libncurses5w-dev Closes: #567783
+ * Updated to standards 3.8.4
+ * Changed priority to important See: #564504
+ * Added Vcs-* fields to control
+
+ -- Craig Small <csmall@debian.org> Mon, 01 Feb 2010 13:53:43 +1100
+
+procps (1:3.2.8-3) unstable; urgency=low
+
+ * Changed source format to 3.0 quilt
+ * rules file to new debhelper tiny format
+ * some pid points set to null if status file cannot be read Closes: #550009
+ * vmstat doesn't scale things that shouldn't Closes: #558134
+ * Limit counter wrap with vmstat Closes: #558361
+ * Adjusted rules file so wont build amd64 files on i386 Closes:
+ #558089
+ * Removed kernel.maps_protect from example file Closes: #554808
+ * skill doesnt treat null param as 0 Closes: #551173
+ * Reduced init.d verbosity Closes: #567704
+ * Documented /etc/sysctl.d directory Closes: #551933
+ * sysctl.conf warns about RAs ignored with ipv6 forwarding Closes:
+ #545007
+ * free -o better documented Closes: #526604
+ * slabtop -o doesnt use ncurses slabtop_once patch Closes: #503089
+ * libproc.so symlink in -dev package Closes: #306778
+ * skill/snice option parsing fixed Closes: #331419
+ * Forced libproc constructor order Closes: #460331
+ * pmap -x prints RSS and dirty columns Closes: #347476, #505571
+
+ -- Craig Small <csmall@debian.org> Sun, 31 Jan 2010 18:31:08 +1100
+
+procps (1:3.2.8-2) unstable; urgency=low
+
+ * ps displays supplementary groups Closes: #506303
+ * Updated to standards version 3.8.3
+ * Removed setproctitle reference in ps.1 Closes: #529045
+ * Fixed LGPL path in copyright Closes: 528814
+ * Adjusted printk in sysctl suggestion Closes: #526855
+ * Fixed package description Closes: #535954
+ * Renamed second SZ to SIZE in ps Closes: #541061
+ * Removed net options in sysctl.conf that are there by default Closes:
+ #540186
+ * Updated free.1 Closes: #534198
+ * Fixed typo in NEWS.Debian Closes: #513381
+ * Removed comment about window scaling in sysctl example Closes:
+ #512764
+ * ps.1 options sorted Closes: #518620
+ * Added PROCPS_USERLEN environment variable to set username length in
+ w Closes: #396423
+ * Added PROCPS_FROMLEN environment variable to set from column width
+ in w Closes: #341439
+ * Adjusted column width for ps.1
+
+ -- Craig Small <csmall@debian.org> Mon, 05 Oct 2009 21:14:10 +1100
+
+procps (1:3.2.8-1.2) unstable; urgency=low
+
+ * Non-maintainer upload to fix release goal.
+ * Avoid using /usr/bin/which in init.d script, to make sure it work
+ before /usr/ is mounted (Closes: #548802).
+
+ -- Petter Reinholdtsen <pere@debian.org> Thu, 01 Oct 2009 11:30:03 +0200
+
+procps (1:3.2.8-1.1) unstable; urgency=low
+
+ * Non-maintainer upload to fix release goal.
+ * Correct init.d script dependencies and depend on initscripts
+ to make sure the required script dependencies are present
+ (Closes: #546888).
+ * Add depend on ${misc:Depends} some times used by debhelper.
+
+ -- Petter Reinholdtsen <pere@debian.org> Wed, 16 Sep 2009 17:02:42 +0200
+
+procps (1:3.2.8-1ubuntu4) lucid; urgency=low
+
+ * debian/sysctl.d/10-zeropage.conf*: Reestablish a procps-controlled
+ mmap_min_addr value so that when package-installed sysctl settings
+ are removed, the secure default is restored.
+ * debian/rules: enhance per-architecture sysctl install logic to allow
+ installing "non-default" conf files instead of always building up
+ a single arch-specific config.
+ * debian/sysctl.d/README: clarify how to use procps when changing files.
+ * debian/sysctl.conf:
+ - remove outdated example of maps_protect (LP: #269715).
+ - remove invalid warning about TCP Window Scaling.
+
+ -- Kees Cook <kees@ubuntu.com> Wed, 16 Dec 2009 11:06:56 -0800
+
+procps (1:3.2.8-1ubuntu3) karmic; urgency=low
+
+ FFE LP: #427356.
+
+ * Replace init script with Upstart job.
+ - Do not start after upgrade.
+ * debian/control:
+ - Add missing ${misc:Depends}
+ - Bump build-dependency on debhelper for Upstart-aware dh_installinit
+ * Use contents of /etc/sysctl.conf after contents of files in /etc/sysctl.d
+ LP: #292470.
+
+ -- Scott James Remnant <scott@ubuntu.com> Tue, 15 Sep 2009 03:26:56 +0100
+
+procps (1:3.2.8-1ubuntu2) karmic; urgency=low
+
+ * New patch 60_linux_version_init, fixes linux version detection. This
+ relied on a side-effect of the elf loader and previously hoped that
+ constructors would be called in a certain order. That behavior was
+ undefined and hence unreliable. For newer kernels, an elf note is now
+ checked to extract HZ tick count. (LP: #364656).
+
+ -- David Sugar <david.sugar@canonical.com> Thu, 06 Aug 2009 10:02:54 +0000
+
+procps (1:3.2.8-1ubuntu1) karmic; urgency=low
+
+ * Merge from debian unstable, remaining changes:
+ - debian/rules (Ubuntu-specific):
+ - set init script to priority 17.
+ - install sysctl files from new sysctl.d directory.
+ - append debian/sysctl.d/*.conf.$DEB_HOST_ARCH to 10-arch-specific.conf
+ - debian/sysctl.d (Ubuntu-specific):
+ - 10-console-messages.conf: stop low-level kernel messages on console.
+ - 10-network-security.conf: enable rp_filter and SYN-flood protection.
+ - 10-keyboard.conf.powerpc: mouse button emulation on PowerPC.
+ - Drop conf-file-cleanups in debian/{preinst,postinst,postrm}.
+ - Drop pre-Hardy rcS.d procps.sh cleanups in debian/postinst.
+
+ -- Kees Cook <kees@ubuntu.com> Wed, 03 Jun 2009 07:28:56 -0700
+
+procps (1:3.2.8-1) unstable; urgency=low
+
+ * New upstream release
+ * Actually apply sort time patch Closes: #508435
+ * Corrected 35_path_max patch Closes: #519331
+ * fixed cumulative parameter in watch.1 Closes: #527193
+ * Applied vmstat -p fix patch Closes: #526360
+
+ -- Craig Small <csmall@debian.org> Thu, 28 May 2009 17:34:22 +1000
+
+procps (1:3.2.7-11ubuntu2) jaunty; urgency=low
+
+ * debian/{preinst,postinst,postrm}: drop sysctl.d/10-process-security.conf
+ now that the defaults are carried in the kernel configurations
+ (LP: #344955).
+
+ -- Kees Cook <kees@ubuntu.com> Wed, 18 Mar 2009 14:52:48 -0700
+
+procps (1:3.2.7-11ubuntu1) jaunty; urgency=low
+
+ * Merge from debian unstable, remaining changes:
+ - debian/{postinst,rules}: init script to priority 17, remove on upgrade.
+ - debian/rules (Ubuntu-specific):
+ - install sysctl files from new sysctl.d directory.
+ - append debian/sysctl.d/*.conf.$DEB_HOST_ARCH to 10-arch-specific.conf
+ - debian/sysctl.d (Ubuntu-specific):
+ - 10-console-messages.conf: stop low-level kernel messages on console.
+ - 10-network-security.conf: enable "rp_filter" by default.
+ - 10-process-security.conf: block lower 64k allocations to protect
+ kernel from NULL deref attacks.
+ - 10-keyboard.conf.powerpc: mouse button emulation on PowerPC.
+ * procps-3.2.7/debian/{preinst,postinst,postrm}: drop
+ sysctl.d/10-tcp-timestamps-workaround.conf again now that we have a
+ fixed kernel, and make sure it gets removed on upgrade to this version
+ (LP: #264019, duplicated from 1:3.2.7-9ubuntu2.1).
+ * debian/sysctl.d/10-network-security.conf: enable SYN-flood protection
+ by default (LP: #57091).
+
+ -- Kees Cook <kees@ubuntu.com> Fri, 06 Feb 2009 08:22:44 -0800
+
+procps (1:3.2.7-11) unstable; urgency=low
+
+ * Reversed rules line so init.d is in everything except kfreebsd
+ Closes: #511513
+
+ -- Craig Small <csmall@debian.org> Mon, 12 Jan 2009 08:48:53 +1100
+
+procps (1:3.2.7-10) unstable; urgency=low
+
+ * PATH_MAX problem fixed Closes: #496274
+ * NOHZ patch appilied Closes: #495882
+ * Added some extra comments about sysctl Closes: #495884
+ * init script respects VERBOSE Closes: #495885
+ * More ps sorting Closes: #508435
+ * ps displays cgroups Closes: #469669
+ * Added cross build support Closes: #451812
+ * Added Hilko's fix for sysctl Closes: #511082
+ * Applied Mortys watch exec patch, thankyou!!
+ Closes: #225549, #155227, #420377, #481679,#410967
+ * top UID length now increased to 5 Closes: #426782
+ * init script not installed for kFreeBSD Closes: #502729
+ * Fixed kernel.maps_protect entry in sysctl.conf Closes: #494655
+ * D flag documented in man page Closes: #495987
+ * w uses locale for centi-seconds Closes: #252575
+ * Corrected w-bassman patch which made ALL times change and documented
+ what the times mean in w.1 Closes: #414906
+ * Changed dh_clean -k to dh_prep
+ * Updated to debhelper 7
+ * Replaced may in kill.1 Closes: #375739
+ * Added precision wait time to watch Closes: #183486
+ * top exits if it cannot read /proc Closes: #378695
+ * Adjusted top.1 as CODE DATA fields were incorrect Closes: 267873
+ * Added --errexit flag on watch to exit on command error Closes:
+ #183346
+
+ -- Craig Small <csmall@debian.org> Sun, 11 Jan 2009 12:29:42 +1100
+
+procps (1:3.2.7-9.1) unstable; urgency=low
+
+ * NMU
+ * Removes spuirous debug printf() that breaks other packages
+ (Closes: #511082)
+
+ -- Hilko Bengen <bengen@debian.org> Wed, 07 Jan 2009 13:47:37 +0100
+
+procps (1:3.2.7-9ubuntu3) jaunty; urgency=low
+
+ * debian/sysctl.d/10-process-security.conf:
+ - Remove kernel.maps_protect option, it has been dropped in 2.6.28
+ kernel tree: http://lkml.org/lkml/2008/10/9/399 (LP: #304117).
+
+ -- Luca Falavigna <dktrkranz@ubuntu.com> Mon, 01 Dec 2008 20:17:15 +0100
+
+procps (1:3.2.7-9ubuntu2) intrepid; urgency=low
+
+ * Add debian/sysctl.d/10-tcp-timestamps-workaround.conf to disable TCP
+ timestamping, since its implementation in the 2.6.27 kernel in Ubuntu
+ 8.10 causes problems with certain routers (LP: #264019).
+
+ -- Colin Watson <cjwatson@ubuntu.com> Mon, 27 Oct 2008 10:47:21 +0000
+
+procps (1:3.2.7-9ubuntu1) intrepid; urgency=low
+
+ * Merge from debian unstable, remaining changes:
+ - debian/{postinst,rules}: init script to priority 17, remove on upgrade.
+ - debian/patches/60_top_nohz: fix idle report when running NOHZ (debian
+ bug #495882).
+ - debian/init: respect $VERBOSE setting (debian bug #495885).
+ - debian/sysctl.conf: recommend against tcp_syncookies since it blocks
+ tcp window scaling (debian bug #495884).
+ - debian/control: Maintainer field update.
+ * debian/sysctl.d: move all Ubuntu-specific sysctl settings:
+ - 10-console-messages.conf: stop low-level kernel messages on console.
+ - 10-network-security.conf: enable "rp_filter" by default.
+ - 10-process-security.conf:
+ - block lower 64k allocations to protect kernel from NULL deref attacks.
+ - enable /proc/$pid/maps protection.
+ - 10-keyboard.conf.powerpc: Mouse button emulation.
+ - 30-inotify-limits.conf: moved to "tracker" package for increasing
+ inotify watches to 524,388 for tracker.
+ * debian/rules:
+ - install sysctl files from new sysctl.d directory.
+ - append debian/sysctl.d/*.conf.$DEB_HOST_ARCH to 10-arch-specific.conf
+ * debian/sysctl.conf: add comment drawing attention to sysctl.d directory
+ (debian bug #495884).
+
+ -- Kees Cook <kees@ubuntu.com> Sun, 17 Aug 2008 06:25:40 -0700
+
+procps (1:3.2.7-9) unstable; urgency=low
+
+ * Reverse order of sysctl file reads
+ * Added sys.kernel.maps_protect option to syctl.conf Closes: #446466
+ * Add up 7 cpu numbers Closes: 460331
+ * Top closes when it gets EOF Closes: #458986, #416976
+ * Fixed -a option processing Closes: #408608
+ * Added German man page for w Closes: #390225
+ * Updated to standards version 3.8.0
+
+ -- Craig Small <csmall@debian.org> Sun, 10 Aug 2008 11:49:12 +1000
+
+procps (1:3.2.7-8ubuntu2) intrepid; urgency=low
+
+ * Remove rcS.d/S17procps.sh on upgrade, eliminating Ubuntu cruft.
+
+ -- Adam Conrad <adconrad@ubuntu.com> Mon, 30 Jun 2008 15:46:16 -0600
+
+procps (1:3.2.7-8ubuntu1) intrepid; urgency=low
+
+ * Merge from debian unstable, remaining changes (LP: #242976):
+ - debian/{postinst,rules}: Place init script at priority 17.
+ - debian/patches/60_top_nohz: fix idle report when running NOHZ.
+ - debian/sysctl.conf: enable /proc/$pid/maps protection.
+ - debian/rules: allow for arch-specific sysctl.conf settings.
+ (append debian/sysctl.conf.$DEB_HOST_ARCH, if it exists, to sysctl.conf)
+ - debian/sysctl.conf.powerpc: Mouse button emulation.
+ - debian/init: respect $VERBOSE setting.
+ - debian/control: Maintainer field update.
+ - debian/sysctl.conf: stop low-level kernel messages to console
+ (this was formerly not documented in the changelog)
+ - debian/sysctl.conf: increase inotify watches to 524,388 for tracker.
+ - debian/sysctl.conf: enable "rp_filter" by default.
+ - debian/sysctl.conf: comment added about tcp_syncookies setting disabling
+ TCP Window Scaling
+ - debian/sysctl.conf: enable lower 64k protection to stop NULL deref
+ attacks.
+
+ -- Lars Wirzenius <lars@ubuntu.com> Wed, 25 Jun 2008 22:36:33 +0300
+
+procps (1:3.2.7-8) unstable; urgency=low
+
+ * Quote $file in init script and remove weird *.conf directory
+ Closes: #474710, #474725
+
+ -- Craig Small <csmall@debian.org> Tue, 08 Apr 2008 08:34:54 +1000
+
+procps (1:3.2.7-7) unstable; urgency=low
+
+ * Actually apply top patch Closes: #351065
+ * Irix mode + thread CPU fix Closes: #459890
+ * kill/skill prints error message Closes: #468363
+ * rule file changes for nostrip and passing CFLAGS Closes: #468656
+ * Duplicate ignore broadcast in sysctl.conf removed Closes: #474406
+ * Added IPv6 lines for redirects and source route to sysctl.conf
+ Closes: #474431
+ * Fixed IPv6 forwarding line in sysctl.conf Closes: #469557, #464150
+ * pri field explained in ps.1 Closes: #465761
+ * sysct.conf now has TCP/IP instead of TCP.IP again Closes: #463652,
+ #463824
+ * Added /etc/sysctl.d/ directory for other packages Closes: #370351
+ * Added bug presubj for kill Closes: #409118
+ * no-heading option documented in ps.1 Closes: #431081
+
+ -- Craig Small <csmall@debian.org> Mon, 07 Apr 2008 10:01:05 +1000
+
+procps (1:3.2.7-6) unstable; urgency=low
+
+ * Remove procps.sh version check Closes: #459680
+ * Added Paolo's patch to fix highlighting cutoffs Closes: #351065
+ * Cleaned up sysctl.conf Closes: #447076, #446466, #446989, #446990, #465449
+ * Removed missing option from sysctl.conf Closes: #460665
+ * Changed to standard version 3.7.3
+
+ -- Craig Small <csmall@debian.org> Fri, 01 Feb 2008 23:01:28 +1100
+
+procps (1:3.2.7-5ubuntu2) hardy; urgency=low
+
+ * debian/sysctl.conf:
+ - enable "rp_filter" by default (LP: #201952).
+ - clean up duplicated entries, adjust documentation about syn cookies.
+
+ -- Kees Cook <kees@ubuntu.com> Thu, 13 Mar 2008 14:13:26 -0700
+
+procps (1:3.2.7-5ubuntu1) hardy; urgency=low
+
+ * Merge from debian unstable, remaining changes:
+ - debian/{postinst,rules}: Place init script at priority 17.
+ - debian/patches/60_top_nohz: fix idle report when running NOHZ.
+ - debian/sysctl.conf: enable /proc/$pid/maps protection.
+ - debian/rules: allow for arch-specific sysctl.conf settings.
+ - debian/sysctl.conf.powerpc: Mouse button emulation.
+ - debian/init: respect $VERBOSE setting.
+ - debian/control: Maintainer field update.
+ - debian/sysctl.conf: increase inotify watches to 524,388 for tracker.
+ * debian/sysctl.conf: enable lower 64k protection to stop NULL deref attacks.
+
+ -- Kees Cook <kees@ubuntu.com> Wed, 24 Oct 2007 10:41:23 -0700
+
+procps (1:3.2.7-5) unstable; urgency=low
+
+ * Added headers into vmstat Closes: #436805
+ * totpages and pslab removed from vmstat.8 Closes: #436806
+ * Conditionally move procps.sh to procps init script Closes: #437063
+ * SEE ALSO section in pgrep.1 uses .BR Closes: #437678
+ * ipv4 forwarding example fixed Closes: #418804
+ * top minimum time adjusted Closes: #298347
+ * pgrep.1 patch doesnt add the -c option again Closes: #385621, #431884
+ * Fixed minor typo in free.1 Closes: #435540
+ * top.1 mentions st steal time Closes: #402478
+ * Added some security-enhancing options to sysctl.conf Closes: #324593
+ * Updated date on patched man pages as this is their new issue date
+
+ -- Craig Small <csmall@debian.org> Thu, 04 Oct 2007 22:44:25 +1000
+
+procps (1:3.2.7-4.1) unstable; urgency=low
+
+ * armel porter NMU.
+ * DEB_HOST_GNU_SYSTEM != linux-gnu on armel. Check DEB_HOST_ARCH_OS instead.
+ Closes: #437613
+
+ -- Joey Hess <joeyh@debian.org> Mon, 10 Sep 2007 15:55:08 -0400
+
+procps (1:3.2.7-4) unstable; urgency=low
+
+ * top exits with correct code Closes: #341272, #354255
+ * sysctl malloc extended by 1 Closes: #423704
+ * sysctl.conf has both rp_filter lines Closes: #389395
+ * Updated top.c resize patch Closes: #256376
+ * Changed /proc message Closes: #292721
+ * Correct return code for pgrep Closes: #413383
+ * Correct return code for vmstat Closes: #425492
+ * init script lost it .sh we dont need it Closes: #343620
+ * Adjusted the shlibs so they are more relaxed.
+
+ -- Craig Small <csmall@debian.org> Wed, 08 Aug 2007 20:27:55 +1000
+
+procps (1:3.2.7-3ubuntu7) hardy; urgency=low
+
+ * No particular need for the maximum inotify instances to be that high,
+ leave them at the default -- otherwise you'll exhaust file descriptors
+ first.
+
+ -- Scott James Remnant <scott@ubuntu.com> Sat, 10 Nov 2007 10:47:44 -0500
+
+procps (1:3.2.7-3ubuntu6) hardy; urgency=low
+
+ * Increase maximum inotify instances to 1,024 and watches to 524,388 -
+ since tracker is in our default install, we need a lot more.
+
+ -- Scott James Remnant <scott@ubuntu.com> Sun, 28 Oct 2007 10:34:10 -0400
+
+procps (1:3.2.7-3ubuntu5) gutsy; urgency=low
+
+ * Test for DEB_HOST_ARCH_OS=linux instead of DEB_HOST_GNU_SYSTEM=linux-gnu
+ as the lpia DEB_HOST_GNU_SYSTEM is linux-gnulp instead, which breaks.
+
+ -- Adam Conrad <adconrad@ubuntu.com> Tue, 31 Jul 2007 17:03:28 +1000
+
+procps (1:3.2.7-3ubuntu4) gutsy; urgency=low
+
+ * Fix idle counter display with NOHZ kernels.
+ With massive SMP machines and NOHZ, it is possible that the CPU is really
+ idle.. so idle that top will show 0.0% in idle. This one liner will make
+ sure to report that the CPU is 100.0% in idle and produce a less confusing
+ output.
+
+ -- Fabio M. Di Nitto <fabbione@ubuntu.com> Fri, 20 Jul 2007 12:53:29 +0200
+
+procps (1:3.2.7-3ubuntu3) gutsy; urgency=low
+
+ * debian/sysctl.conf: enable /proc/$pid/maps protection, new in 2.6.22.
+
+ -- Kees Cook <kees@ubuntu.com> Thu, 10 May 2007 00:39:48 -0700
+
+procps (1:3.2.7-3ubuntu2) feisty; urgency=low
+
+ * Rebuild for changes in the amd64 toolchain.
+ * Set Ubuntu maintainer address.
+
+ -- Matthias Klose <doko@ubuntu.com> Mon, 5 Mar 2007 01:24:55 +0000
+
+procps (1:3.2.7-3ubuntu1) feisty; urgency=low
+
+ * Merge from Debian unstable. Remaining Ubuntu changes:
+ - debian/rules: Add debian/sysctl.conf.$(DEB_HOST_ARCH) to default
+ sysctl.conf file.
+ - debian/sysctl.conf.powerpc: Mouse button emulation.
+ - debian/postinst, debian/rules: Place init script at priority 17.
+ - debian/sysctl.conf: Set kernel.printk to default values 4 4 1 7 to avoid
+ cluttering consoles on powerpc.
+ - debian/procps.sh: Respect $VERBOSE.
+
+ -- Martin Pitt <martin.pitt@ubuntu.com> Fri, 3 Nov 2006 18:28:41 +0100
+
+procps (1:3.2.7-3) unstable; urgency=low
+
+ * Changed init script dependencies Closes: #386637, #386341
+ * init script uses lsb logging Closes: #384920
+
+ -- Craig Small <csmall@debian.org> Wed, 13 Sep 2006 11:27:56 +1000
+
+procps (1:3.2.7-2ubuntu3) edgy; urgency=low
+
+ * debian/sysctl.conf: Explicitly set the kernel.printk sysctl to '4 4 1 7'
+ to stop kernel log spewage to consoles on powerpc. On i386/amd64 this is
+ the default printk setting anyway, thus does not change any default, but
+ with the powerpc kernels the default is '0 0 0 0'.
+
+ -- Martin Pitt <martin.pitt@ubuntu.com> Fri, 8 Sep 2006 15:44:45 +0200
+
+procps (1:3.2.7-2ubuntu2) edgy; urgency=low
+
+ * debian/rules: Re-add debian/sysctl.conf.$(DEB_HOST_ARCH) addition to the
+ default sysctl.conf file (apparently this got dropped in the last merge).
+ This repairs mouse F11/F12 mouse button emulation on powerpc.
+
+ -- Martin Pitt <martin.pitt@ubuntu.com> Wed, 16 Aug 2006 15:19:51 +0200
+
+procps (1:3.2.7-2ubuntu1) edgy; urgency=low
+
+ [ Ubuntu Merge-o-Matic ]
+ * Merge from debian unstable.
+
+ -- Scott James Remnant <scott@ubuntu.com> Fri, 30 Jun 2006 01:33:16 +0100
+
+procps (1:3.2.7-2) unstable; urgency=medium
+
+ * pgrep c option patch works correctly Closes: #375791
+ * Made urgency medium as pgrep -c <non-matching> segfaults pgrep
+
+ -- Craig Small <csmall@debian.org> Wed, 28 Jun 2006 22:28:02 +1000
+
+procps (1:3.2.7-1ubuntu1) edgy; urgency=low
+
+ [ Ongoing Merge Process ]
+ * Merge from debian unstable.
+
+ [ Scott James Remnant ]
+ * Drop postinst migration now that dapper is released.
+
+ -- Scott James Remnant <scott@ubuntu.com> Wed, 28 Jun 2006 23:31:06 +0100
+
+procps (1:3.2.7-1) unstable; urgency=low
+
+ * New upstream release
+ * Fixed nroff typo in man page Closes: #326517
+ * pgrep/pkill: fix some realloc-related crashes Closes: #353894
+ * Using debhelper compat level 5
+ * Bumped standards versiob to 3.7.2
+ * watch difference check uses 8 bit char Closes: #207103
+ * Updated watch file location Closes: #354242
+ * Cleaned up init.d script Closes: #294765, #338970
+ * init.d script has those lsb dependency thingies Closes: #335320
+ * Removed .rej file out of gnu_kbsd patch Closes: #362875
+ * Removed duplicate -N option out of sysctl.8 Closes: #364894
+ * Explain load average in uptime.1 Closes: #365019
+ * Removed empty directories Closes: #359146
+ * IPv4/6 sysctl.conf examples fixed Closes: #341087
+ * sysctl.8 has optional filename for -p Closes: #297144
+ * top.1 Now has Bold enable=off as default Closes: #293981
+ * free.1 documents the -g (gigabytes) flag CloseS: #286900
+ * Cleanup man pages Closes: #282168
+ * added rollbacks to maint scripts Closes: #374456
+ * Updated gnu kbsd patch Closes: #349982
+ * Added list of commands to description Closes: #354686
+ * Changed copyright, most of it is under LGPL now
+ * Move to admin section to stop dinstaller complaining at me
+ * Actually apply cpu state top.1 patch Closes: #351998
+ * sysctl.conf doesn't mention /etc/network/options Closes: #339788
+ * sysctl.conf uses default instead of all for network stuff as networking
+ rc is done later Closes: #337019
+
+ -- Craig Small <csmall@debian.org> Mon, 26 Jun 2006 08:16:19 +1000
+
+procps (1:3.2.6-2.2) unstable; urgency=low
+
+ * NMU
+ * Document the copyright of the emacs bindings bit of pgrep in
+ debian/copyright (Closes: #362876)
+
+ -- Don Armstrong <don@debian.org> Sat, 22 Apr 2006 07:45:25 -0700
+
+procps (1:3.2.6-2.1) unstable; urgency=low
+
+ * NMU
+ * Use a temporary file for the output of the check_gcc test to avoid
+ removing /dev/null on failure. Thanks to Niv Altivanik for the patch.
+ (Closes: #336710)
+
+ -- Don Armstrong <don@debian.org> Sun, 19 Feb 2006 15:48:35 -0800
+
+procps (1:3.2.6-2ubuntu4) dapper; urgency=low
+
+ * Move init script to S17 so it gets run after all modules have probably
+ been loaded, otherwise many sysctls will probably not be set. Users
+ will have to worry about disabling things a different way (which this
+ has never been useful for anyway).
+
+ -- Scott James Remnant <scott@ubuntu.com> Mon, 23 Jan 2006 15:34:03 +0000
+
+procps (1:3.2.6-2ubuntu3) dapper; urgency=low
+
+ * Undo rename of init script, I was overzealously reading my notes and
+ it's not necessary. Keeps the Debian diff smaller.
+
+ -- Scott James Remnant <scott@ubuntu.com> Wed, 4 Jan 2006 10:41:49 +0000
+
+procps (1:3.2.6-2ubuntu2) dapper; urgency=low
+
+ * Move init script from S30 to S05 so it can apply options before
+ modules are loaded; especially important for disabling networking
+ features, etc.
+ * Rename init script to avoid confusion, and avoid problems using exit
+ in *.sh scripts.
+
+ -- Scott James Remnant <scott@ubuntu.com> Thu, 15 Dec 2005 01:44:51 +0000
+
+procps (1:3.2.6-2ubuntu1) dapper; urgency=low
+
+ * resync with debian, merged by hand
+
+ -- Michael Vogt <michael.vogt@ubuntu.com> Fri, 25 Nov 2005 17:50:43 +0100
+
+procps (1:3.2.6-2) unstable; urgency=low
+
+ * properly patched for kfreebsd Closes: #290719
+
+ -- Craig Small <csmall@debian.org> Wed, 2 Nov 2005 13:58:22 +1100
+
+procps (1:3.2.6-1) unstable; urgency=low
+
+ * New upstream source
+ * ps: man page more detailed Closes: #334682
+ * spelling fixes Closes: #300333, #334684, #334685
+ * top: crash on resize fixed -- thanks Michal Maruska Closes: #320289
+ * vmstat: -p handles /dev/ and does not overflow Closes: #319523, #330969
+ * CPU states in top man page Closes: #312157, #228899
+ * w.bassman finally patched into w Closes: #45937
+ * w uses COLUMNS if ioctl fails, eg with pipe Closes: #252799
+ * GNU/kFreeBSD Support Closes: #290719
+ * Variables that are set are specified on init Closes: #330464
+ * sysctl.conf has netbase examples Closes: #331192
+ * sysctl.conf example has way to stop console logging Closes: #292834
+
+ -- Craig Small <csmall@debian.org> Mon, 31 Oct 2005 18:49:07 +1100
+
+procps (1:3.2.5-1ubuntu1) breezy; urgency=low
+
+ * Resynchronise with Debian.
+
+ -- Adam Conrad <adconrad@0c3.net> Thu, 21 Apr 2005 08:50:56 +0000
+
+procps (1:3.2.5-1) unstable; urgency=low
+
+ * New upstream release
+ display problem on 64-bit systems fixed Closes: #287947
+ top: help and version message on stdout, with exit(0) Closes: #283541
+ ps: SIGTSTP and SIGTTOU shouldn't print bug email address Closes: #246123
+ slabtop: fix overflow on huge NUMA boxes Closes: #264640
+ slabtop: accept any slabinfo 2.x format Closes: #287691
+
+ -- Craig Small <csmall@debian.org> Thu, 27 Jan 2005 11:07:46 +1100
+
+procps (1:3.2.4-1ubuntu1) hoary; urgency=low
+
+ * Resynchronise with Debian.
+
+ -- Colin Watson <cjwatson@canonical.com> Wed, 8 Dec 2004 10:45:17 +0100
+
+procps (1:3.2.4-1) unstable; urgency=low
+
+ * New upstream release
+ ps: tolerate SubDomain security module CONTEXT/LABEL data Closes: #277401
+ * init.d doesnt print done when not verbose Closes: #264431
+ * init.d script runs sysctl with -q for quiet Closes: #277111
+ * sysctl.conf has warning about network overiding varables Closes: #275291
+
+ -- Craig Small <csmall@debian.org> Wed, 17 Nov 2004 12:56:21 +1100
+
+procps (1:3.2.3-1ubuntu1) hoary; urgency=low
+
+ * Resynchronise with Debian.
+
+ -- Scott James Remnant <scott@canonical.com> Wed, 27 Oct 2004 14:28:50 +0100
+
+procps (1:3.2.3-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Craig Small <csmall@debian.org> Wed, 18 Aug 2004 13:24:19 +1000
+
+procps (1:3.2.2-2) unstable; urgency=low
+
+ * Missed one more lib64 override. Works on os390 now
+
+ -- Craig Small <csmall@debian.org> Fri, 30 Jul 2004 21:48:47 +1000
+
+procps (1:3.2.2-1) unstable; urgency=low
+
+ * New upstream release
+ pgrep: man page SEE ALSO updated Closes: #226817
+ top: try to handle terminals lacking rmam and smam Closes: #235003
+ * Upstream now has quiet patch so removing dpatch for it.
+
+ -- Craig Small <csmall@debian.org> Wed, 28 Jul 2004 16:25:34 +1000
+
+procps (1:3.2.1-2ubuntu3) warty; urgency=low
+
+ * On Macs, emulate the middle and right mouse buttons with F11 and F12
+ respectively (closes: Warty #1187).
+
+ -- Colin Watson <cjwatson@canonical.com> Tue, 14 Sep 2004 20:11:25 +0100
+
+procps (1:3.2.1-2ubuntu2) warty; urgency=low
+
+ * Added versioned depend on lsb-base
+
+ -- Nathaniel McCallum <npmccallum@canonical.com> Fri, 3 Sep 2004 15:15:09 -0400
+
+procps (1:3.2.1-2ubuntu1) warty; urgency=low
+
+ * debian/procps.sh: pretty initscript
+
+ -- Nathaniel McCallum <npmccallum@canonical.com> Fri, 3 Sep 2004 11:59:29 -0400
+
+procps (1:3.2.1-2) unstable; urgency=low
+
+ * Fix Makefile patch so it works with newer patch packages
+ Closes: #242574
+ * Changed the way SHARED is used in Makefiles
+ * init script looks nicer when setting multiple variables Closes: #241721
+
+ -- Craig Small <csmall@debian.org> Tue, 13 Apr 2004 07:59:25 +1000
+
+procps (1:3.2.1-1) unstable; urgency=low
+
+ * New upstream release
+ * Changed init script output Closes: #238567
+ * menu item has text quoted
+
+ -- Craig Small <csmall@debian.org> Thu, 1 Apr 2004 22:53:37 +1000
+
+procps (1:3.2.0-1) unstable; urgency=low
+
+ * New upstream release
+ test scripts build on ia-64
+ Fix terminal when ^Z top Closes: #228822
+ slabtop added Closes: #226778
+ * postinst checks for symlink and not file to remove Closes: #234306
+
+
+ -- Craig Small <csmall@debian.org> Wed, 25 Feb 2004 16:35:03 +1100
+
+procps (1:3.1.15-3) unstable; urgency=low
+
+ * Removed entry in Makefile for /libc64 so sparc64 builds
+
+ -- Craig Small <csmall@debian.org> Tue, 17 Feb 2004 11:01:24 +1100
+
+procps (1:3.1.15-2) unstable; urgency=low
+
+ * Removed offending test directory Closes: #227933
+
+ -- Craig Small <csmall@debian.org> Tue, 20 Jan 2004 10:20:44 +1100
+
+procps (1:3.1.15-1) unstable; urgency=low
+
+ * Now uses dpatch to handle the various patches
+ * New upstream release
+ - Supports SELINUX flags for kernel v2.6 Closes: #193648
+ - Hide kernel PID bug Closes: #217278, #219730, #217525, #224470
+ - Fixed top auto-margins problem Closes: #217559
+ * Fix top manual page Closes: #225089
+ * Patch so SELINUX flags work in v2.4 kernels
+ * Extra +s in ps.1 removed Closes: #218220
+ * Added patch so sysctl has quiet -q flag Closes: #189100
+
+ -- Craig Small <csmall@debian.org> Fri, 26 Dec 2003 18:02:59 +1100
+
+procps (1:3.1.14-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Craig Small <csmall@debian.org> Wed, 22 Oct 2003 11:59:53 +1000
+
+procps (1:3.1.13-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Craig Small <csmall@debian.org> Wed, 24 Sep 2003 16:10:42 +1000
+
+procps (1:3.1.12-1) unstable; urgency=low
+
+ * New upstream release
+ ps: $PS_FORMAT works properly Closes: #201575
+ man page: /var/run/utmp, not /etc/utmp Closes: #206583
+ required flags moved out of CFLAGS Closes: #205429
+
+
+ -- Craig Small <csmall@debian.org> Sat, 13 Sep 2003 21:11:09 +1000
+
+procps (1:3.1.11-2) unstable; urgency=low
+
+ * Made shlibs have a specific version Closes: #199118
+ * libproc.a is back in libproc-dev, use it
+ * Added README to libproc-dev for people who use the library
+
+ -- Craig Small <csmall@debian.org> Wed, 20 Aug 2003 15:31:59 +1000
+
+procps (1:3.1.11-1) unstable; urgency=low
+
+ * 3.1.11 New upstream version
+ - handle GPLONLY_ symbols Closes: #143549, #188374
+ * freeproc now defined Closes: #189047
+ * warning in readproc.c fixed Closes: #197346
+ * Removed optarg and optind defines Closes: #197343
+ * Fixed saved_start_time warning Closes: #197342
+
+ -- Craig Small <csmall@debian.org> Thu, 24 Jul 2003 17:02:36 +1000
+
+procps (1:3.1.9-1) unstable; urgency=low
+
+ * New upstream source
+ * memory sizes fixed for 64-bit w/ gcc 3.x Closes: #194376, #191933
+ * Doesn't segfault if /proc not mounted Closes: #172735
+ * No warning in top about memory key Closes: #188271
+ * More info in kill man page Closes: #182414
+ * Document the different oO options Closes: #169301
+ * Updated stabndards version to 3.5.9
+ * Changed disk to backing storage Closes: #175925
+
+ -- Craig Small <csmall@debian.org> Mon, 2 Jun 2003 02:31:03 +1000
+
+procps (1:3.1.8-1) unstable; urgency=low
+
+ * New upstream release
+ watch man page fixed, Closes #182246
+ * Changed section for libproc-dev
+
+ -- Craig Small <csmall@debian.org> Tue, 1 Apr 2003 10:19:05 +1000
+
+procps (1:3.1.6-1) unstable; urgency=low
+
+ * New upstream release
+ - watch has --no-title option Closes: #179862
+ - ps -C can compare very long names Closes: #178127
+ * Fixed descriptions in manual pages Closes: #179046
+ * w wont crash if /proc unreadable Closes: #169398
+
+ -- Craig Small <csmall@debian.org> Sat, 22 Feb 2003 21:33:45 +1100
+
+procps (1:3.1.5-1) unstable; urgency=low
+
+ * New upstream version
+ - watch don't drop empty lines Closes: #171005
+ - top has old sort keys Closes: #167249
+ - now count Inact_laundry as needed Closes: #172163
+
+ -- Craig Small <csmall@debian.org> Mon, 6 Jan 2003 13:49:32 +1100
+
+procps (1:3.1.3-1) unstable; urgency=low
+
+ * New upstream source
+
+ -- Craig Small <csmall@debian.org> Fri, 13 Dec 2002 16:16:36 +1100
+
+procps (1:3.1.1-1) unstable; urgency=low
+
+ * New upstream source
+ - vmstats reports memort counts Closes: #169774
+
+ -- Craig Small <csmall@debian.org> Wed, 4 Dec 2002 15:57:13 +1100
+
+procps (1:3.1.0-1) unstable; urgency=low
+
+ * New upstream version
+ - vmstat displays IO-wait time instead of bogus "w"
+ - when IO-wait hidden, count as idle, not as sys
+ - pmap command added (like Sun has)
+ * Manual pages cleaned up Closes: #165970, #164481
+
+ -- Craig Small <csmall@debian.org> Mon, 11 Nov 2002 12:03:46 +1100
+
+procps (1:3.0.5-1) unstable; urgency=low
+
+ * New upstream version
+ - top tolerates super-wide displays Closes: #165497
+ - ELF note warning gone for some kernels Closes: #165900
+ - Fix ps and top man pages a bit, dropped bugs severity.
+
+ -- Craig Small <csmall@debian.org> Tue, 29 Oct 2002 22:04:36 +1100
+
+procps (1:3.0.4-1) unstable; urgency=low
+
+ * New upstream version
+ - ELF note warning removed Closes: #165093, #165343
+ - top works for most TERM= settings Closes: #164864, #164956
+ * w has FROM column again by default Closes: #165252
+ * debhelper dependency version fixed Closes: #165083
+ * ps.1 looks a bit better now Closes: #16448
+
+ -- Craig Small <csmall@debian.org> Mon, 21 Oct 2002 08:30:04 +1000
+
+procps (1:3.0.3-1) unstable; urgency=low
+
+ * New upstream version
+ - w works with KOI8 locale Closes: #153043
+ - fix top for non-SMP 2.2.xx and 2.0.xx Closes: #164231
+ - negative idle time fixed Closes: #126260
+ - sysctl handles new vlan interface Closes: #140386
+ - vmstat documentation update Closes: #157935, #155684
+ - "skill -n blah blah blah" lets you test options Closes: #158630
+ - fixed ps --sort crash Closes: #164226
+ - vmstat compiles with latest gcc-3.x Closes: #164348
+ - bad (int*) cast in top removed Closes: #164468
+ - Support new/improved statistics interfaces in 2.5 /proc Closes: #164013
+ - top defaults to old layout and sort by pid Closes: #164277
+ - rant moved out of top.1 man page Closes: #164520
+ - top runs much faster Closes: #87779
+
+ -- Craig Small <csmall@debian.org> Sat, 12 Oct 2002 21:44:23 +1000
+
+procps (1:3.0.0-2) unstable; urgency=low
+
+ * Fixed pkill and skill, auto* is so broken. Closes: #163940, #163944
+ * Moved libproc.so.3 into /lib as it's needed early Closes: #163981,#163899
+
+ -- Craig Small <csmall@debian.org> Thu, 10 Oct 2002 07:19:45 +1000
+
+procps (1:3.0.0-1) unstable; urgency=low
+
+ * New upstream source Closes: #163698
+ - top has SMP Closes: #162316, #62282, #94896, #98277, #117040, #117735
+ - SELINUX support Closes: #142892
+ - top defaults to signal 15 to kill pids with Closes: #72158
+ - 64-bit time reduces the overflow problem Closes: #82382, #155908
+ - "w" program better at determining what a user is doing
+ Closes: #88758, 115529
+ - oldps is gone Closes: #101917
+ - Handles /proc/tty/drivers Closes: #108654, #154046
+ - Supports scanf where locale uses , for decimal point Closes: #126873
+ - uptime --help now calls itself uptime Closes: #128880
+ - ps faster Closes: #147731
+ - top calculates screen better Closes: #162296
+ - top allows sorts by pid or swapped memory Closes: #79868, #159446
+ - support for s/390 Closes: #126295
+ - statm buffer size increased Closes: #145085
+ - new top has no typos Closes: #148918
+ - new top can handle lots of tasks Closes: #70900
+ * Fixed init.d file so it follows policy Closes: #121945
+
+ -- Craig Small <csmall@debian.org> Tue, 8 Oct 2002 12:29:45 +1000
+
+procps (1:2.0.7-10) unstable; urgency=low
+
+ * Fixes ps crash when system.map is exact multiple of 1024
+ Thankyou Colin for the patch! Closes: #109237, #142292
+
+ -- Craig Small <csmall@eye-net.com.au> Fri, 12 Apr 2002 21:02:04 +1000
+
+procps (1:2.0.7-9) unstable; urgency=low
+
+ * Sanity checking for Number of CPU Closes: #127561, #122617
+ This will keep the m68k and ARM boys happy.
+ * uptime usage fixed Closes: #138351
+ * kill man page NAME updated Closes: #119400
+ * Better explanation for load average in uptime(1) Closes: #140902
+ * utmp in correct location in w(1) Closes: #115725
+
+ -- Craig Small <csmall@eye-net.com.au> Mon, 8 Apr 2002 14:18:20 +1000
+
+procps (1:2.0.7-8) unstable; urgency=low
+
+ * Removed -ggdb Closes: #117203
+
+ -- Craig Small <csmall@eye-net.com.au> Sat, 27 Oct 2001 07:02:38 +1000
+
+procps (1:2.0.7-7) unstable; urgency=low
+
+ * Added os/390 patches Closes: #113494
+ * w manpage now gives correct location for utmp Closes: #110723
+ * watch wrap-around problem fixed Closes: #111759
+ * Programs are compilied -O2 Closes: #108652
+
+ -- Craig Small <csmall@eye-net.com.au> Wed, 26 Sep 2001 08:08:43 +1000
+
+procps (1:2.0.7-6) unstable; urgency=low
+
+ * Re-fixed command line interpretation Closes: #103101
+
+ -- Craig Small <csmall@eye-net.com.au> Tue, 10 Jul 2001 17:32:02 +1000
+
+procps (1:2.0.7-5) unstable; urgency=low
+
+ * watch now copys strings better. Closes: #95404, #97948, #99780
+ * skill.1 loads tbl Closes: #92242, #67899
+ * long usernames are shown Closes: #86205, #94957
+ * Remove /etc/rcS.d/S30procps with good ole rm Closes: #92184
+ * /etc/init.d/procps.sh calls itself that Closes: #93302
+ * Cannot write config in secure mode Closes: #93948
+
+ -- Craig Small <csmall@debian.org> Sun, 29 Apr 2001 14:55:20 +1000
+
+procps (1:2.0.7-4) unstable; urgency=low
+
+ * watch doesnt crash with long command lines Closes: #88592, #87693
+ * bumped up a buffer in a proc read Closes: #85775
+ * Now we use 24 hour time Closes: #85640
+ * pgrep calls itself pgrep Closes: #86630
+ * Corrected symlink in libproc-dev Closes: #87865
+
+ -- Craig Small <csmall@debian.org> Mon, 19 Mar 2001 10:13:27 +1100
+
+procps (1:2.0.7-3) unstable; urgency=low
+
+ * Conflicts with pgrep, so no more conflicts Closes: #82835
+ * Minor fixes to ps.1 manpage Closes: #81921
+ * watch highlighs correctly Closes: #77737
+ * top wont die with evil HOMEs, Closes: #81452
+ * removed reference to suidregister
+ * Remore -L reference in kill.1 Closes: #85913
+
+ -- Craig Small <csmall@debian.org> Thu, 8 Feb 2001 12:44:58 +1100
+
+procps (1:2.0.7-2) unstable; urgency=medium
+
+ * Moved kill back again, dammit why is the makefile so broken
+ Closes: #82747
+ * Watch uses locale Closes: #82739
+ * top's scanf is protected from evil locale problems Closes: #82671, #69128
+ (Big thankyou to Guillaume for lending a test account)
+
+ -- Craig Small <csmall@debian.org> Fri, 19 Jan 2001 09:15:36 +1100
+
+procps (1:2.0.7-1) unstable; urgency=medium
+
+ * New upstream version (010114)
+ - Merges 2.0.7 code from RedHat Closes: #80832
+ - Made note that Shared memory report will be junk due to kernel
+ Closes: #77818
+ - Remove html and null stuff from man page Closes: #81920
+ - vsize is kB in man page Closes: #82210
+ - man pages look better Closes: #70055, #70941
+ - vmstat increase Closes: #77886
+ - w tries harder to find things Closes: #24531
+ - 15 char user names Closes: #71211
+ * No HOME overflow in top Closes: 81452
+ * Added menu Hints Closes: #80051, #82324
+
+
+ -- Craig Small <csmall@debian.org> Wed, 17 Jan 2001 08:57:24 +1100
+
+procps (1:2.0.6-9) unstable; urgency=medium
+
+ * added libncurses5-dev to build-depends, Closes: #67533
+ * Put kill back into /bin Closes #67580, #67582
+ * Put 2.0.6-7 ps.1 back in Closes: #67451
+
+ -- Craig Small <csmall@debian.org> Tue, 25 Jul 2000 08:13:21 +1000
+
+procps (1:2.0.6-8) unstable; urgency=low
+
+ * New upstream source:
+ * - sysctl EOF bug fixed Closes: #62877
+ * - stop crashes with unmounted /proc Closes: #63512, #55177
+ * - Versions checking tolerates RH /proc/ksyms Closes: #59798
+ * - Top works better with SMP Closes: #34734, #56547, #59703
+
+ -- Craig Small <csmall@debian.org> Fri, 14 Jul 2000 22:33:44 +1000
+
+procps (1:2.0.6-7) unstable; urgency=low
+
+ * Fixed missing version Closes: #62207, #62484, #59112
+ * Stop crashes with umounted /proc Closes: #63512, #55177
+ * Nicer man pages Closes: #63495, #59406
+ * Fixed sysctl eof bug Closes: #62877
+ * watch wraps properly Closes: #60913
+ * watch handles tabs Closes: #46213
+ * watch honors locale settings Closes: #63762
+ * now versioned replaces line for bsdutils
+
+ -- Craig Small <csmall@debian.org> Mon, 29 May 2000 13:31:54 +1000
+
+procps (1:2.0.6-6) unstable; urgency=low
+
+ * New patchlevel (000221)
+ * ps 'f' ASCII art forest fixed. Closes: #57134, #58644
+ * let insane people run ps setuid. Closes: #56701
+ * note that kernel 2.3 is now faster. Closes: #49130
+ * top with WCHAN was leaking memory. Closes: #58172, #52257, #56889
+ * can show current CPU. Closes: #37023
+ * w looks better now. Closes: #55952
+ * init.d/procps removed if exists. Closes: #55137, #55852
+ * Fixed skill/snice man page (thanks man-db maint!) Closes: #53736, #46743
+ * Fixed ps man page Closes: #58365
+
+ -- Craig Small <csmall@debian.org> Wed, 23 Feb 2000 10:31:37 +1100
+
+procps (1:2.0.6-5) unstable; urgency=low
+
+ * New upstream source
+ * kill "_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}" Closes: #54394, #53208
+ * added type 'S' and scan past machine types Closes: #54396
+ * Fixed w(1) manpage Closes: #54709
+ * top now agrees with ps for RSS Closes: #52679
+
+ -- Craig Small <csmall@debian.org> Tue, 11 Jan 2000 08:23:56 +1100
+
+procps (1:2.0.6-4) unstable; urgency=low
+
+ * procps init.d script quietly dies if not /etc/default/rcS Closes:
+ #52839
+ * Put the NEWS changelog back in Closes: #52678
+ * Fixed that damn Rsmp annoying message bug Closes: #48686
+ * Remove /etc/init.d/procps Closes: #53818
+
+ -- Craig Small <csmall@debian.org> Mon, 20 Dec 1999 11:14:53 +1100
+
+procps (1:2.0.6-3) unstable; urgency=low
+
+ * Patched ps so it complains if you chmod 711 /proc Closes: #52481
+ * Did the same for top.
+ * Ditto for oldps.
+ * Changed and int to a char* Closes: #52482
+ * sysctl.conf file references sysctl.conf (5) not 8 Closes: #52385
+ * props init.d script changed to procps.sh Closes: #52228
+
+ -- Craig Small <csmall@debian.org> Mon, 13 Dec 1999 11:57:01 +1100
+
+procps (1:2.0.6-2) unstable; urgency=low
+
+ * Changed psmisc from reccomends to suggests
+ * %MEM now works, Closes: #50010 #50055 #50148 #50356
+ * top doesn't crash with > 204 processes Closes: #50055
+ * Another libproc fd leak fixed Closes: #45398
+ * ps silently ignores m and -m for future compatibility Closes: #48308
+ * Added a sysctl.conf and other files as suggested Closes: #51098
+ * Fix start field instability
+ * ps.1 fixed Closes: #35137
+
+ -- Craig Small <csmall@debian.org> Tue, 7 Dec 1999 14:42:51 +1100
+
+procps (1:2.0.6-1) unstable; urgency=low
+
+ * New upstream
+ * sysctl crash fixed, Closes: #49015
+ * libproc file descriptor leak fixed, Closes: #45398
+ * False positive System.map mismatches killed, Closes: #49047
+ * Supports 64 Hz for StrongARM/Shark Closes: #47461
+ * pr_time fixed, Closes: #46223
+ * libc num cpu workaround back in, Closes: #49039
+ * Fixed kill manpage, Closes: #47018
+ * This version definitely, absolutely has kill, Closes: #46762
+
+
+ -- Craig Small <csmall@debian.org> Fri, 5 Nov 1999 12:46:05 +1100
+
+procps (1:2.0.3-5) unstable; urgency=low
+
+ * Changed conflicts with replaces
+
+ -- Craig Small <csmall@debian.org> Wed, 6 Oct 1999 14:36:48 +1000
+
+procps (1:2.0.3-4) unstable; urgency=low
+
+ * support SMP systems with versioned kernel modules Closes: #45621, #46465
+ * Added kill to this (it is removed from bsdutils).
+ * We now need kill manpage, Closes: #46004
+
+ -- Craig Small <csmall@debian.org> Tue, 5 Oct 1999 10:28:01 +1000
+
+procps (1:2.0.3-3) unstable; urgency=low
+
+ * System.map support for non-i386 Closes: #45592, #45250
+ * Do not require /proc/ksyms Closes: #45128, #45132, #45619
+ * Alternative w.1 points to the right spot, Closes: #45331
+ * Copyright for skill and snice and ps fixed, Closes: #45119
+
+ -- Craig Small <csmall@debian.org> Tue, 21 Sep 1999 16:31:59 +1000
+
+procps (1:2.0.3-2) unstable; urgency=medium
+
+ * Now with top! Closes: #45106
+
+ -- Craig Small <csmall@debian.org> Wed, 15 Sep 1999 11:12:34 +1000
+
+procps (1:2.0.3-1) unstable; urgency=low
+
+ * New upstream source
+ * Debian personality does m flag Closes: #44832
+ * Corrected typo in top.1 Closes: #44836
+ * New improved watch Closes: #29970
+
+ -- Craig Small <csmall@debian.org> Mon, 13 Sep 1999 16:59:16 +1000
+
+procps (1:2.0.2-4) unstable; urgency=high
+
+ * Fixed the nasty ps formatting problem (Bug #40859 #40856 #40839 )
+
+ -- Craig Small <csmall@debian.org> Wed, 7 Jul 1999 08:41:54 +1000
+
+procps (1:2.0.2-3) unstable; urgency=low
+
+ * New upstream patches/source
+ * SMB Hz wierdness fixed (Bug #33023 #33284)
+ * non-tty output does not get chopped at 80 columns (bug #36688)
+ * BSD personalities set the default selection and output format (bug #36698)
+ * Fixed collumn spacing problem (Bug #35309)
+ * Work around for borken libs that return 0 processors (Bug #36902)
+ * skill now uses process name not command line (Bug #19208)
+
+ -- Craig Small <csmall@debian.org> Mon, 5 Jul 1999 07:29:47 +1000
+
+procps (1:2.0.2-2) unstable; urgency=low
+
+ * Removed kill and manual page (Bug #36421 #36551 #36375)
+ * Put in patch for bogus sysconf return (Bug #36494 #36532 #36581)
+
+ -- Craig Small <csmall@debian.org> Wed, 28 Apr 1999 09:04:59 +1000
+
+procps (1:2.0.2-1) unstable; urgency=low
+
+ * New upstream version (Bug #34394 #27291 #34250 #34956 #35240 #35247
+ #35520 #35756 #34580 )
+
+ -- Craig Small <csmall@debian.org> Mon, 19 Apr 1999 13:26:48 +1000
+
+procps (1:2.0.0-1) unstable; urgency=low
+
+ * New upstream version (Bug #33083 23347 33462 10556 33266 33371 )
+
+
+ -- Craig Small <csmall@debian.org> Mon, 15 Mar 1999 14:21:57 +1100
+
+procps (1:1.9.0-2) unstable; urgency=low
+
+ * top now resumes (Bug #32106 )
+ * debhelper text problem fixed in postinst (Bug #32963 #33122 #33003 33117 )
+ * oldps and ps now use alternatives (Bug #33083 )
+ * ps s format now not ugly (Bug #28266 )
+ * watch command line help and man page correct (Bug #31702 )
+ * sessreg removed from package (Bug #32294 )
+ * ps doesn't display extra spaces (Bug #27799 )
+ * top has spaces in command lines again (Bug #33060 )
+ * ps now has personality (Bug #22923 #18429 )
+ * moved non-free skill and snice to non-free package.
+
+ -- Craig Small <csmall@debian.org> Tue, 9 Feb 1999 15:10:58 +1100
+
+procps (1:1.9.0-1) unstable; urgency=low
+
+ * New Upstream source
+
+ -- Craig Small <csmall@debian.org> Thu, 4 Feb 1999 14:48:37 +1100
+
+procps (1:1.2.9-3) unstable; urgency=low
+
+ * Linked to ncurses4
+
+ -- Craig Small <csmall@debian.org> Fri, 30 Oct 1998 14:13:02 +1100
+
+procps (1:1.2.9-2) unstable; urgency=low
+
+ * top now has spaces in between command lines (Bug #28178 )
+ * --version or -V now shows proper version.
+
+ -- Craig Small <csmall@debian.org> Mon, 26 Oct 1998 08:55:59 +1100
+
+procps (1:1.2.9-1) unstable; urgency=low
+
+ * New upstream version (Bug #27573 )
+ * Menu entry changed from System to Menu/System (Bug #27438 )
+ * Char variables changed to int for powerpc (Bug #26624 )
+ * libproc now nulls allocated structure (Bug #26225 )
+ * No longer uses psdevtab (yay!) (Bug #25388 )
+ * ps doesn't double space command line parameters (Bug #25306 #24293 )
+ * ps now silently ignores g flag for those BSD heads (Bug #24075 )
+
+ -- Craig Small <csmall@debian.org> Fri, 9 Oct 1998 09:15:11 +1000
+
+procps (1:1.2.7-2) unstable; urgency=low
+
+ * Top can suspend twice (or three times even) (Bug #22997 )
+ * Libraries properly built (Bug #20010 )
+
+ -- Craig Small <csmall@debian.org> Mon, 1 Jun 1998 09:16:09 +1000
+
+procps (1:1.2.7-1) frozen unstable; urgency=high
+
+ * New upstream source, fixes security bug (Bug #21475)
+
+ -- Craig Small <csmall@debian.org> Thu, 23 Apr 1998 08:04:54 +1000
+
+procps (1:1.2.6-2) unstable; urgency=low
+
+ * Fixed Shared library dependencies (bugs #18388 #18394 18392 )
+ * Conflicts with earlier versions of w-bassman (bug #18389 )
+
+ -- Craig Small <csmall@debian.org> Mon, 23 Feb 1998 09:05:54 +1100
+
+procps (1:1.2.6-1) unstable; urgency=low
+
+ * New upstream source.
+ * xload is undiverted (Bug #17102 )
+ * ps_fields.7.gz removed (Bug #18090 )
+ * Colour patches removed, color-related bugs gone (Bug #18008 #17217 #18090 )
+ * POSIX patches removed, cmd line bugs gone (Bug #15537 )
+ * w is now an alternative w.procps (Bug #17960 )
+ * skill now works with process names (Bug #17087 )
+ * ps and top man pages have their field descriptions (Bug #17360 )
+ * ps u fixed (Bug #17313 )
+ * Source code is no longer FUBAR (Bug #17892 )
+ * top now redraws screen after config screen (Bug #11896 )
+ * Circular dependency removed (Bug #16966 )
+ * top does mess up screens with wrong cmd line (Bug #17230 )
+ * top suspends with ctrl-Z (Bug #16703 )
+
+ -- Craig Small <csmall@debian.org> Tue, 17 Feb 1998 08:31:21 +1100
+
+procps (1:1.2.5-2) unstable; urgency=low
+
+ * Moved /bin into /bin/ps (Bug #17001 )
+
+ -- Craig Small <csmall@debian.org> Tue, 13 Jan 1998 07:50:43 +1100
+
+procps (1:1.2.5-1) unstable; urgency=low
+
+ * TTY selection works ( #16724 )
+ * top and ps now accept --colour and *_COLOURS
+ * xproc copyright file not compressed ( #14491 )
+ * All #include <proc/*.h> now #include "proc/*.h" ( #13482 )
+ * Copyright doesn't mention psmisc now ( #16704 )
+ * Moved ps back into /bin ( #16737 #16705 )
+ * New upstream source ( #16795 )
+
+ -- Craig Small <csmall@debian.org> Mon, 12 Jan 1998 08:35:10 +1100
+
+procps (1:1.2.2-1) unstable; urgency=low
+
+ * New maintainer
+ * Updated upstream source to 1.2.2 (instead of 1.2)
+ * Merged Helmut's color/command line patches into upstream.
+ * Copyright file is not compressed ( #14493 #14415 )
+ * psdatabase refreshed when installing ( #10693 )
+ * Fixed +/- line in free ( #10785 #10870 #11566 #12027 #12245 #12374 )
+ * w collumns corrected ( #10898 #13117 )
+ * top saves sort type ( #11553 )
+ * Linked to libc6 ( #11725 )
+ * top doesn't coredump with S option ( #11855 )
+ * skill works with given patch ( #12023 )
+ * libproc-dev has proper sym link ( #12697 )
+ * top -h doesn't change terminal settings ( #13513 )
+ * ps -s has "CAUGHT" not "CATCHED" ( #14342 )
+ * ps_colors.7 and ps_fields.7 reformatted ( #14109 #14544 #14545 )
+ * ps checks for tty before using colors ( #14596 )
+ * top sets stop signal handler later, stopping race ( #14769 )
+ * When using POSIX personality, processes show up ( #14780 )
+ * top checks for valid term type ( #15807 )
+ * xproc now Depends on procps-1.2.*-* ( #10762 #13347 )
+ * xproc dependencies fixed ( #12698 )
+
+ -- Craig Small <csmall@debian.org> Tue, 30 Dec 1997 11:33:54 +1100
+
+procps (1.12.2.1) unstable; urgency=low
+
+ * Non-maintainer release, built for libc6.
+ * Added in free.c from new procps version 1.2.3 (from sunsite), seems to
+ fix all the problems with free reporting bogus valus.
+
+ -- Joey Hess <joeyh@master.debian.org> Fri, 24 Oct 1997 13:34:35 -0400
+
+procps (1.12.2) stable unstable; urgency=low
+
+ * fixed meminfo handling again, as the fix wouldn't work on pre-2.1.x
+ kernels.
+ * fixed free to use the meminfo routines from libproc.
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Fri, 13 Jun 1997 22:42:14 +0200
+
+procps (1.12.1) stable unstable; urgency=low
+
+ * fixed several bugs
+ * replaced utmp handling to support wrappers.
+ * added /proc/meminfo support for 2.1.x kernels.
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Tue, 10 Jun 1997 23:59:41 +0200
+
+procps (1.11.6) frozen unstable; urgency=medium
+
+ * psmisc 1.14 : new upstream version (mainly bugfixes)
+ * added a lot of new serial device major numbers to the device lookup
+ code. Somehow it seems there are new serial devices every other week.
+ * fixed a bug in top: broken .toprc may cause a segmentation fault.
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Sun, 4 May 1997 09:50:30 +0200
+
+procps (1.11.5) frozen unstable; urgency=medium
+
+ * minor changes to make it compile with libc6
+ * fix top behaviour on machines having nonstandard NR_TASKS up to 4k
+ tasks
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Fri, 18 Apr 1997 02:07:46 +0200
+
+procps (1.11.4) unstable; urgency=medium
+
+ * strip libproc.so from unneeded symbols (Bug# 8311)
+ * fixed watch.1 example (Bug# 8169)
+ * partly fixed fuser sigsegv core dump (Bug# 8004)
+ * menu entries for xproc and procps (Bug# 8325)
+ * divertions for xmem and xload. (Bug# 7565)
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Sun, 13 Apr 1997 20:55:05 +0200
+
+procps (1.11.3) unstable; urgency=low
+
+ * fixed uptime again, minor Makefile changes
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Fri, 21 Feb 1997 16:22:04 +0100
+
+procps (1.11.2) unstable; urgency=medium
+
+ * fixed typo in ps_fields.7 (Bug#5457)
+ * fixed typo in debian/rules (Bug #5585)
+ * fixed bug in w introduced in 1.11.1 (Bugs #5489, #5694, #5695, #5705).
+ * added support for non-standard serial devices (long overdue - Bug
+ #5771).
+ * fixed uptime option handling (Bug #6099).
+ * fixed top problems with missing/corrupted utmp (Bug #5819).
+ * fixed manpage problems (Bug #5936).
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Wed, 19 Feb 1997 18:49:26 +0100
+
+procps (1.11.1) unstable; urgency=low
+
+ * fixed bug in w <username>
+ * automatic resize if field length is exceeded. This changed the shared
+ library, so popping the major number.
+ * fixed numeric WCHAN output on Alphas and stupid bug in ps (again,
+ thanks to H. Koenig).
+ * fixed top memory statistics for systems with more than 100M memory or
+ swap.
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Tue, 12 Nov 1996 02:57:18 +0100
+
+procps (1.10.1) unstable; urgency=low
+
+ * merged in ALPHA & Sparc patches
+ (from ftp.azstarnet.com:/pub/linux/axp/glibc)
+ * fixed ps --deselect/-N for pid lists
+ * use shared libs from now on, install shlibs again
+ * manpages for libproc
+ * static lib compiled without -fPIC
+ * added libproc package for development installing libproc headers,
+ manpages and static library.
+ * fixed PROC_REAL bug when PROC_FILLSTATUS isn't set.
+ * fixed color bug in ps --forest
+ * added xproc package for xload, xmem, xidle, xcpustate
+ (XConsole left out as we use xconsole & klogd on debian)
+ * adapted xload manpage for xidle and xmem
+ * fixed xmem to cope with newer kernels (where shared pages are counted
+ once for each additional reference)
+ * fixed top change_fields bug (a field needs 24, not 21 spaces)
+ * fixed several Alpha bugs (thanks to Harald Koenig)
+ * due to popular demand, the old format for time intervals is back.
+ for all program using this, a toggle command line option has been
+ provided. The default behaviour depends on the compile time option
+ NEW_TIME_DEFAULT (see main Makefile)
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Wed, 23 Oct 1996 21:30:54 +0200
+
+procps (1.09.2) unstable; urgency=low
+
+ * fixed cpu nice % in summary
+ * fixed topsetup initialisation in top.h I messed up in last revision
+ * fixed pipe output bug
+ * fixed --deselect bug in SVR4/POSIX mode
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Sun, 20 Oct 1996 13:57:11 +0200
+
+procps (1.09.1) unstable; urgency=low
+
+ * fixed ps -w bug displaying too many empty lines
+ * fixed SIGSEGV bug in ps -www
+ * fixed bug in top not calculating length of the command/args/env fields
+ at the field selection screen in some circumstances.
+ * fixed SIGSEGV bug when using environ field
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Sat, 12 Oct 1996 07:59:29 +0200
+
+procps (1.09) experimental; urgency=low
+
+ * This is an experimental release of the procps suite. A lot of features have
+ been added since the 1.01(a) release:
+ - support for both BSD and POSIX (SVR4) style command line options.
+ - completely configurable display of information.
+ - colour markup of processes exceeding limits or belonging to a user.
+ Please take a look at /usr/doc/procps/NEWS and the manpages for a concise
+ list. This is how the next upstream release of procps may look
+ (i.e. it will look like this if there is not too much resistance).
+
+ -- Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> Sat, 5 Oct 1996 14:26:57 +0200
+
diff --git a/smartt-top/debian/compat b/smartt-top/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/smartt-top/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/smartt-top/debian/control b/smartt-top/debian/control
new file mode 100644
index 0000000..5f4e87f
--- /dev/null
+++ b/smartt-top/debian/control
@@ -0,0 +1,36 @@
+Source: procps
+Section: admin
+Priority: important
+Maintainer: Ubuntu Core Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Craig Small <csmall@debian.org>
+Build-Depends: debhelper (>= 7.2.3~), libncurses5-dev, libncursesw5-dev
+Standards-Version: 3.8.4
+Vcs-Git: git://git.debian.org/collab-maint/procps.git
+Vcs-Browser: http://git.debian.org/?p=collab-maint/procps.git;a=summary
+Homepage: http://procps.sf.net/
+
+Package: procps
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base (>= 3.0-10), initscripts
+Provides: watch
+Conflicts: watch, libproc-dev (<< 1:1.2.6-2), w-bassman (<< 1.0-3), procps-nonfree, pgrep (<< 3.3-5)
+Replaces: watch, bsdutils (<< 2.9x-1)
+Recommends: psmisc
+Description: /proc file system utilities
+ This package provides command line and full screen utilities for browsing
+ procfs, a "pseudo" file system dynamically generated by the kernel to
+ provide information about the status of entries in its process table
+ (such as whether the process is running, stopped, or a "zombie").
+ .
+ It contains free, kill, pkill, pgrep, pmap, ps, pwdx, skill, slabtop,
+ snice, sysctl, tload, top, uptime, vmstat, w, and watch.
+
+Package: libproc-dev
+Architecture: any
+Section: libdevel
+Priority: optional
+Depends: ${misc:Depends}, libc6-dev, procps (= ${binary:Version})
+Description: library for accessing process information from /proc
+ These are the header files for libproc. Some packages using libproc
+ to access process information from /proc need these to compile.
+
diff --git a/smartt-top/debian/copyright b/smartt-top/debian/copyright
new file mode 100644
index 0000000..2590c5f
--- /dev/null
+++ b/smartt-top/debian/copyright
@@ -0,0 +1,63 @@
+This is the Debian Linux prepackaged version of the /proc file
+system utilities.
+
+This package was downloaded from:
+ http://procps.sourceforge.net/
+
+
+Upstream Authors:
+Werner Almesberger <almesber@di.epfl.ch>, Roger Binns, Charles
+Blake <cblake@ucsd.edu>, Brian Edmonds, David Engel <david@ods.com>,
+Larry Greenfield <greenfie@gauss.rutgers.edu>, Michael K. Johnson
+<johnsonm@sunsite.unc.edu>, Branko Lankester <lankeste@fwi.uva.nl>,
+Robert Nation <nation@rocket.sanders.lockheed.com>, Michael Shields
+<mjshield@nyx.cs.du.edu>, Henry Ware <al172@yfn.ysu.edu>, Matt
+Welsh <mdw@sunsite.unc.edu>, Albert D. Cahalan, Jim C. Warner
+<warnerjc@worldnet.att.net>, and Kjetil Torgrim Homme
+<kjetilho@ifi.uio.no>
+
+Copyright:
+ free.c:
+ Copyright 2003 Robert Love
+ Copyright 2004 Albert Cahalan
+ minimal.c:
+ Copyright 1998,2004 Albert Cahalan
+ pgrep.c:
+ Copyright 2000 Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+ pmap.c:
+ Copyright 2002 by Albert Cahalan
+ pwdx.c:
+ Copyright 2004 Nicholas Miell
+ skill.c:
+ Copyright 1998-2002 by Albert Cahalan
+ slabtop.c:
+ Copyright 2003 Chris Rivera
+ sysctl.c:
+ Copyright 1999 George Staikos
+ tload.c:
+ Copyright 1992 Branko Lankester
+ top.c:
+ Copyright 2002 James C. Warner
+ vmstat.c:
+ Copyright 1994 Henry Ware <al172@yfn.ysu.edu>
+ Copyright 2002 Albert Cahalan
+ ps/display.c, ps/parser.c:
+ Copyright 1998-2003 by Albert Cahalan
+ ps/global.c, ps/global.c:
+ Copyright 1998-2002 by Albert Cahalan
+ ps/help.c, ps/sortformat.c:
+ Copyright 1998-2004 by Albert Cahalan
+ ps/output.c:
+ Copyright 1999-2004 by Albert Cahalan
+
+License:
+The programs sysctl and pgrep copyright by their
+authors and redistributable under the terms of the GNU General
+Public License. On Debian Linux systems, the complete text of
+the GNU General Public License can be found in
+`/usr/share/common-licenses/GPL-2'.
+
+All other software is copyright by their authors and redistributable under
+the terms of the GNU Library General Public License. On Debian Linux
+systems, the complete text of the GNU Library General Public License can
+be found in `/usr/share/common-licenses/LGPL-2'.
diff --git a/smartt-top/debian/docs b/smartt-top/debian/docs
new file mode 100644
index 0000000..dfb2bf7
--- /dev/null
+++ b/smartt-top/debian/docs
@@ -0,0 +1,4 @@
+BUGS
+TODO
+README.top
+
diff --git a/smartt-top/debian/examples b/smartt-top/debian/examples
new file mode 100644
index 0000000..9b25702
--- /dev/null
+++ b/smartt-top/debian/examples
@@ -0,0 +1,2 @@
+debian/sysctl.conf
+
diff --git a/smartt-top/debian/libproc-dev.README b/smartt-top/debian/libproc-dev.README
new file mode 100644
index 0000000..e419b13
--- /dev/null
+++ b/smartt-top/debian/libproc-dev.README
@@ -0,0 +1,12 @@
+README for libproc-dev
+======================
+
+This README is for people who want to use the libraries for their own
+programs. If you just want to use procps tools you don't need to use this
+and you can probably remove libproc-dev too.
+
+It is generally a bad idea to dynamically link to libproc. The API changes
+a fair bit and I cannot guarantee that it will stay the same between minor
+versions (though it will stay the same between Debian versions). I've now
+re-included the libproc.a file so use that.
+
diff --git a/smartt-top/debian/libproc-dev.dirs b/smartt-top/debian/libproc-dev.dirs
new file mode 100644
index 0000000..5401fc8
--- /dev/null
+++ b/smartt-top/debian/libproc-dev.dirs
@@ -0,0 +1,2 @@
+usr/lib
+usr/include/proc
diff --git a/smartt-top/debian/libproc-dev.install b/smartt-top/debian/libproc-dev.install
new file mode 100644
index 0000000..43c8280
--- /dev/null
+++ b/smartt-top/debian/libproc-dev.install
@@ -0,0 +1,3 @@
+proc/*.h usr/include/proc
+static/libproc.a usr/lib
+static/libproc.so usr/lib
diff --git a/smartt-top/debian/patches/00list b/smartt-top/debian/patches/00list
new file mode 100644
index 0000000..b6cdb04
--- /dev/null
+++ b/smartt-top/debian/patches/00list
@@ -0,0 +1,53 @@
+10_free.1
+10_output_sort_time
+10_pmap.1
+10_ps.1
+10_skill.1
+10_skill_perror
+10_slabtop
+10_sysctl.8
+10_sysctl_options
+10_tload.1
+10_top_irix
+10_top_no_openproc
+10_top_stdineof
+10_top_uid_length
+10_uptime.1
+10_vmstat.8
+10_watch.1
+10_w_time
+20_kill.1
+20_kill_warncr
+20_module_mk_shared
+20_sysinfo_c
+20_top_manpage
+20_top_c_resize
+20_w-bassman
+30_top.1_cpustate
+20_watch_8bitchar
+30_library_map_freeproc
+30_pgrep_start_time
+30_readproc_c
+30_sysinfo_7numbers
+30_tload_no_optargs
+30_w.1
+30_w-columns
+30_watch_exec_beep
+35_path_max
+35_w_envlength
+40_gnu-kbsd-version
+40_pgrep_coption
+40_ps_cgroup_display
+40_watch_precision_time
+45_proc_complain_unmounted_proc
+45_ps_supgid_display
+50_dev_null_makefile_fix
+50_pgrep.1
+50_ps_size_sz
+50_top_mintime
+50_vmstat_headers
+55_pgrep_usage_exitcode
+55_ps_1_options
+55_top_highlight
+60_top_nohz
+65_fix_partition_format
diff --git a/smartt-top/debian/patches/10_ps.1.patch b/smartt-top/debian/patches/10_ps.1.patch
new file mode 100644
index 0000000..c07203e
--- /dev/null
+++ b/smartt-top/debian/patches/10_ps.1.patch
@@ -0,0 +1,62 @@
+Author: <csmall@debian.org>
+Description: ps.1 normal indent widths
+pri field explained
+--no-heading comment is an alias for --no-headers
+Removed setproctitle(1) reference
+Bug-Debian: http://bugs.debian.org/465761
+Bug-Debian: http://bugs.debian.org/529045
+Index: b/ps/ps.1
+===================================================================
+--- a/ps/ps.1 2009-11-24 20:53:06.000000000 +1100
++++ b/ps/ps.1 2009-11-24 21:00:31.000000000 +1100
+@@ -18,19 +18,13 @@
+ .nh
+ .if n .ss 12 0
+ .\"
+-.\" See /usr/share/groff/current/tmac/an-old.tmac for what these do.
+-.\" Setting them to zero provides extra space, but only do that for
+-.\" plain text output. PostScript and such will remain indented.
+-.if n .nr IN 0n
+-.if n .nr an-prevailing-indent 0n
+-.\"
+ .\"
+ .\" ColSize is used for the format spec table.
+ .\" It's the left margin, minus the right, minus
+ .\" the space needed for the 1st two columns.
+ .\" Making it messy: inches, ens, points, scaled points...
+ .\"
+-.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-20u)n)
++.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-26u)n)
+ .\"
+ .\" This is for command options
+ .nr OptSize (16u)
+@@ -499,7 +493,7 @@
+ .opt c
+ Show the true command name. This is derived from the name of the
+ executable file, rather than from the argv value. Command arguments
+-and any modifications to them (see\ \fIsetproctitle\fR(3)) are
++and any modifications to them are
+ thus not shown. This option
+ effectively turns the \fBargs\fR format keyword into the \fBcomm\fR
+ format keyword; it is useful with the \fB\-f\fR format option and with
+@@ -588,7 +582,8 @@
+ repeat header lines, one per page of output
+
+ .opt \-\-no\-headers
+-print no header line at all
++print no header line at all. \-\-no\-heading is an alias for this
++option.
+
+ .opt \-\-lines \ n
+ set screen height
+@@ -1159,6 +1154,10 @@
+ parent process ID.
+ T}
+
++pri PRI T{
++priority of the process. Higher number means lower priority
++T}
++
+ psr PSR T{
+ processor that process is currently assigned to.
+ T}
diff --git a/smartt-top/debian/patches/complain_unmounted_proc.patch b/smartt-top/debian/patches/complain_unmounted_proc.patch
new file mode 100644
index 0000000..6558a2e
--- /dev/null
+++ b/smartt-top/debian/patches/complain_unmounted_proc.patch
@@ -0,0 +1,19 @@
+Author: <hesso@hesso.pool.math.tu-berlin.de>
+Description: Complain when /proc/version cannot be found instead of
+exiting silently.
+Index: b/proc/version.c
+===================================================================
+--- a/proc/version.c 2009-11-24 21:00:44.000000000 +1100
++++ b/proc/version.c 2009-11-24 21:00:46.000000000 +1100
+@@ -39,8 +39,10 @@
+ FILE *fp;
+ char buf[256];
+
+- if ( (fp=fopen("/proc/version","r")) == NULL) /* failure implies impending death */
++ if ( (fp=fopen("/proc/version","r")) == NULL) {
++ fprintf(stderr, "Cannot find /proc/version - is /proc mounted?\n");
+ exit(1);
++ }
+ if (fgets(buf, 256, fp) == NULL) {
+ fprintf(stderr, "Cannot read kernel version from /proc/version\n");
+ fclose(fp);
diff --git a/smartt-top/debian/patches/free.1.patch b/smartt-top/debian/patches/free.1.patch
new file mode 100644
index 0000000..22587a8
--- /dev/null
+++ b/smartt-top/debian/patches/free.1.patch
@@ -0,0 +1,108 @@
+Description: Several bugs fixed for the free manpage:
+ -g flag documented #286900
+ hypen escaped #282168
+ Reformated completely #534198
+ free -o better documented #526604
+Bug-Debian: http://bugs.debian.org/286900
+Bug-Debian: http://bugs.debian.org/282168
+Bug-Debian: http://bugs.debian.org/534198
+Bug-Debian: http://bugs.debian.org/526604
+Author: <csmall@debian.org>
+--- a/free.1
++++ b/free.1
+@@ -1,47 +1,67 @@
+ .\" -*-Nroff-*-
+ .\" This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu.
+ .\" Freely distributable under the terms of the GPL
+-.TH FREE 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual"
++.TH FREE 1 "5 Oct 2009 " "Cohesive Systems" "Linux User's Manual"
+ .SH NAME
+ free \- Display amount of free and used memory in the system
+ .SH SYNOPSIS
+-.BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s"
+-.I delay
+-.RB "] [" "\-t" "] [" "\-V" ]
++.B free
++.RB [ \-b | \-k | \-m | \-g ]
++.RB [ \-c
++.IR count ]
++.RB [ \-l ]
++.RB [ \-o ]
++.RB [ \-t ]
++.RB [ \-s
++.IR delay ]
++.RB [ \-V ]
+ .SH DESCRIPTION
+ \fBfree\fP displays the total amount of free and used physical and swap
+ memory in the system, as well as the buffers used by the kernel.
+ The shared memory column should be ignored; it is obsolete.
+-.SS Options
+-The \fB-b\fP switch displays the amount of memory in bytes; the
+-\fB-k\fP switch (set by default) displays it in kilobytes; the \fB-m\fP
+-switch displays it in megabytes.
+-.PP
+-The \fB-t\fP switch displays a line containing the totals.
+-.PP
+-The \fB-o\fP switch disables the display of a "buffer adjusted" line.
+-If the -o option is not specified, \fBfree\fP subtracts buffer memory
+-from the used memory and adds it to the free memory reported.
+-.PP
+-The \fB-s\fP switch activates continuous polling \fIdelay\fP seconds apart. You
++.SS OPTIONS
++.TP
++\fB\-b\fR
++Display the amount of memory in bytes.
++.TP
++\fB\-c\fR \fIcount\fR
++Display the result \fIcount\fR times. Requires the \fB\-s\fR option.
++.TP
++\fB\-g\fR
++Display the amount of memory in gigabytes.
++.TP
++\fB\-k\fR
++Display the amount of memory in kilobytes. This is the default.
++.TP
++\fB\-l\fR
++Show detailed low and high memory statistics.
++.TP
++\fB\-m\fR
++Display the amount of memory in megabytes.
++.TP
++\fB\-o\fR
++Display the output in old format, the only difference being this option
++will disable the display of the "buffer adjusted" line.
++.TP
++\fB\-s\fR
++Continuously display the result \fIdelay\fP seconds apart. You
+ may actually specify any floating point number for \fIdelay\fP,
+ .BR usleep (3)
+ is used for microsecond resolution delay times.
+-.PP
+-The \fB\-V\fP displays version information.
++.TP
++\fB\-t\fR
++Display a line showing the column totals.
++.TP
++\fB\-V\fR
++Display version information.
+ .SH FILES
+-.ta
+-.IR /proc/meminfo "\-\- memory information"
++.nf
++/proc/meminfo memory information
+ .fi
+-
+-.SH "SEE ALSO"
+-.BR ps (1),
+-.BR slabtop (1),
+-.BR vmstat (8),
+-.BR top(1)
+-
+ .SH AUTHORS
+ Written by Brian Edmonds.
+
+ Send bug reports to <albert@users.sf.net>
+-
++.SH "SEE ALSO"
++.BR ps "(1), " slabtop "(1), " top "(1), " vmstat (8).
++.\"{{{}}}
diff --git a/smartt-top/debian/patches/gnu-kbsd-version.patch b/smartt-top/debian/patches/gnu-kbsd-version.patch
new file mode 100644
index 0000000..693dc68
--- /dev/null
+++ b/smartt-top/debian/patches/gnu-kbsd-version.patch
@@ -0,0 +1,36 @@
+Author: <csmall@debian.org>
+Description: Rework version parsing so its ok with other OSes
+Index: b/proc/version.c
+===================================================================
+--- a/proc/version.c 2009-11-24 20:53:02.000000000 +1100
++++ b/proc/version.c 2009-11-24 21:00:44.000000000 +1100
+@@ -35,15 +35,23 @@
+
+ static void init_Linux_version(void) __attribute__((constructor));
+ static void init_Linux_version(void) {
+- static struct utsname uts;
+ int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */
++ FILE *fp;
++ char buf[256];
+
+- if (uname(&uts) == -1) /* failure implies impending death */
+- exit(1);
+- if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3)
++ if ( (fp=fopen("/proc/version","r")) == NULL) /* failure implies impending death */
++ exit(1);
++ if (fgets(buf, 256, fp) == NULL) {
++ fprintf(stderr, "Cannot read kernel version from /proc/version\n");
++ fclose(fp);
++ exit(1);
++ }
++ fclose(fp);
++ if (sscanf(buf, "Linux version %d.%d.%d", &x, &y, &z) < 3)
+ fprintf(stderr, /* *very* unlikely to happen by accident */
+ "Non-standard uts for running kernel:\n"
+- "release %s=%d.%d.%d gives version code %d\n",
+- uts.release, x, y, z, LINUX_VERSION(x,y,z));
++ "release %s=%d.%d.%d gives version code %d\n",
++ buf,
++ x, y, z, LINUX_VERSION(x,y,z));
+ linux_version_code = LINUX_VERSION(x, y, z);
+ }
diff --git a/smartt-top/debian/patches/kill.1.patch b/smartt-top/debian/patches/kill.1.patch
new file mode 100644
index 0000000..5a8ae3e
--- /dev/null
+++ b/smartt-top/debian/patches/kill.1.patch
@@ -0,0 +1,147 @@
+Author: Craig Small <csmall@debian.org>
+Description: Fix kill.1 manual page
+Changed may to cannot or might
+Bug-Debian: http://bugs.debian.org/375739
+Index: b/kill.1
+===================================================================
+--- a/kill.1 2009-11-24 20:53:04.000000000 +1100
++++ b/kill.1 2009-11-24 21:00:37.000000000 +1100
+@@ -10,23 +10,18 @@
+ kill \- send a signal to a process
+
+ .SH SYNOPSIS
+-.TS
+-l l.
+-kill pid ... Send SIGTERM to every process listed.
+-kill -signal pid ... Send a signal to every process listed.
+-kill -s signal pid ... Send a signal to every process listed.
+-kill -l List all signal names.
+-kill -L List all signal names in a nice table.
+-kill -l signal Convert a signal number into a name.
+-kill -V,--version Show version of program
+-.TE
++\fBkill\fR [ \-\fBsignal\fR | \-s \fBsignal\fR ] \fBpid\fR ...
++.br
++\fBkill\fR [ \-L | -V, \-\-version ]
++.br
++\fBkill\fR \-l [ \fBsignal\fR ]
+
+ .SH DESCRIPTION
+-The default signal for kill is TERM. Use -l or -L to list available signals.
++The default signal for kill is TERM. Use \-l or \-L to list available signals.
+ Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+-Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
++Alternate signals may be specified in three ways: \-9 \-SIGKILL \-KILL.
+ Negative PID values may be used to choose whole process groups; see the
+-PGID column in ps command output. A PID of -1 is special; it indicates
++PGID column in ps command output. A PID of \-1 is special; it indicates
+ all processes except the kill process itself and init.
+
+ .SH SIGNALS
+@@ -37,12 +32,11 @@
+ lB rB lB lB
+ lfCW r l l.
+ Name Num Action Description
+-.TH
+ 0 0 n/a exit code indicates if a signal may be sent
+ ALRM 14 exit
+ HUP 1 exit
+ INT 2 exit
+-KILL 9 exit this signal may not be blocked
++KILL 9 exit cannot be blocked
+ PIPE 13 exit
+ POLL exit
+ PROF exit
+@@ -50,15 +44,15 @@
+ USR1 exit
+ USR2 exit
+ VTALRM exit
+-STKFLT exit may not be implemented
+-PWR ignore may exit on some systems
++STKFLT exit might not be implemented
++PWR ignore might exit on some systems
+ WINCH ignore
+ CHLD ignore
+ URG ignore
+-TSTP stop may interact with the shell
+-TTIN stop may interact with the shell
+-TTOU stop may interact with the shell
+-STOP stop this signal may not be blocked
++TSTP stop might interact with the shell
++TTIN stop might interact with the shell
++TTOU stop might interact with the shell
++STOP stop cannot be blocked
+ CONT restart continue if stopped, otherwise ignore
+ ABRT 6 core
+ FPE 8 core
+@@ -66,11 +60,11 @@
+ QUIT 3 core
+ SEGV 11 core
+ TRAP 5 core
+-SYS core may not be implemented
+-EMT core may not be implemented
+-BUS core core dump may fail
+-XCPU core core dump may fail
+-XFSZ core core dump may fail
++SYS core might not be implemented
++EMT core might not be implemented
++BUS core core dump might fail
++XCPU core core dump might fail
++XFSZ core core dump might fail
+ .TE
+
+ .SH NOTES
+@@ -79,36 +73,30 @@
+ the conflict.
+
+ .SH EXAMPLES
+-
+-.SS
+-.B "kill -9 -1"
+-.nf
++.TP
++.B kill \-9 \-1
+ Kill all processes you can kill.
+-.fi
+-.PP
+-.SS
+-.B "kill -l 11"
+-.nf
++.TP
++.B kill \-l 11
+ Translate number 11 into a signal name.
+-.fi
+-.PP
+-.SS
+-.B "kill -L"
+-.nf
++.TP
++.B kill -L
+ List the available signal choices in a nice table.
+-.fi
+-.PP
+-.SS
+-.B "kill 123 543 2341 3453"
+-.nf
++.TP
++.B kill 123 543 2341 3453
+ Send the default signal, SIGTERM, to all those processes.
+-.fi
+-.PP
++
+ .SH "SEE ALSO"
+-pkill(1) skill(1) kill(2) renice(1) nice(1) signal(7) killall(1)
++.BR pkill (1),
++.BR skill (1),
++.BR kill (2),
++.BR renice (1),
++.BR nice (1),
++.BR signal (7),
++.BR killall (1).
+
+ .SH STANDARDS
+-This command meets appropriate standards. The -L flag is Linux-specific.
++This command meets appropriate standards. The \-L flag is Linux-specific.
+
+ .SH AUTHOR
+ Albert Cahalan <albert@users.sf.net> wrote kill in 1999 to replace a
diff --git a/smartt-top/debian/patches/kill_warncr.patch b/smartt-top/debian/patches/kill_warncr.patch
new file mode 100644
index 0000000..328dfaa
--- /dev/null
+++ b/smartt-top/debian/patches/kill_warncr.patch
@@ -0,0 +1,25 @@
+Author: <csmall@debian.org>
+Description: Add CR to warning line
+Bug-Debian: http://bugs.debian.org/331419
+Index: b/skill.c
+===================================================================
+--- a/skill.c 2009-11-24 21:00:31.000000000 +1100
++++ b/skill.c 2009-11-24 21:00:37.000000000 +1100
+@@ -128,7 +128,7 @@
+ sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */
+ fd = open(buf,O_RDONLY);
+ if(fd==-1){ /* process exited maybe */
+- if(pids && w_flag) printf("WARNING: process %d could not be found.",pid);
++ if(pids && w_flag) printf("WARNING: process %d could not be found.\n",pid);
+ return;
+ }
+ fstat(fd, &statbuf);
+@@ -342,7 +342,7 @@
+ }else{
+ fprintf(stderr,
+ "Usage: snice [new priority] [options] process selection criteria\n"
+- "Example: snice netscape crack +7\n"
++ "Example: snice +7 netscape crack \n"
+ "\n"
+ "The default priority is +4. (snice +4 ...)\n"
+ "Priority numbers range from +20 (slowest) to -20 (fastest).\n"
diff --git a/smartt-top/debian/patches/library_map_freeproc.patch b/smartt-top/debian/patches/library_map_freeproc.patch
new file mode 100644
index 0000000..b57ae97
--- /dev/null
+++ b/smartt-top/debian/patches/library_map_freeproc.patch
@@ -0,0 +1,15 @@
+Author: Craig Small <csmall@debian.org>
+Description: Added freeproc to library.map
+Index: b/proc/library.map
+===================================================================
+--- a/proc/library.map 2009-11-24 20:53:03.000000000 +1100
++++ b/proc/library.map 2009-11-24 21:00:40.000000000 +1100
+@@ -7,7 +7,7 @@
+
+ readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command;
+ escape_str; escape_strlist;
+- openproc; closeproc;
++ openproc; closeproc; freeproc;
+ tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
+ display_version; procps_version; linux_version_code;
+ Hertz; smp_num_cpus; have_privs;
diff --git a/smartt-top/debian/patches/makefile_dev_null.patch b/smartt-top/debian/patches/makefile_dev_null.patch
new file mode 100644
index 0000000..8790156
--- /dev/null
+++ b/smartt-top/debian/patches/makefile_dev_null.patch
@@ -0,0 +1,13 @@
+Author: <xaiki@gonzo>
+Description: Make sure Makefile doesnt zot random files while checking GCC
+--- a/Makefile
++++ b/Makefile
+@@ -120,7 +120,7 @@
+ # Unlike the kernel one, this check_gcc goes all the way to
+ # producing an executable. There might be a -m64 that works
+ # until you go looking for a 64-bit curses library.
+-check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o /dev/null $(CURSES) $(CURSESW) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
++check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o will_this_file_really_exist.tmp $(CURSES) $(CURSESW) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ; rm -f will_this_file_really_exist.tmp)
+
+ # Be 64-bit if at all possible. In a cross-compiling situation, one may
+ # do "make m64=-m32 lib64=lib" to produce 32-bit executables. DO NOT
diff --git a/smartt-top/debian/patches/module_mk_shared.patch b/smartt-top/debian/patches/module_mk_shared.patch
new file mode 100644
index 0000000..73ce2c7
--- /dev/null
+++ b/smartt-top/debian/patches/module_mk_shared.patch
@@ -0,0 +1,15 @@
+Author: Craig Small <csmall@debian.org>
+Description: Made SHARED variable overwritable
+Index: b/proc/module.mk
+===================================================================
+--- a/proc/module.mk 2009-11-24 20:53:04.000000000 +1100
++++ b/proc/module.mk 2009-11-24 21:00:38.000000000 +1100
+@@ -17,7 +17,7 @@
+ # numbers for future use, the ELF soname can be set equal to the
+ # file name until some future date when a stable ABI is declared.
+
+-SHARED := 1
++SHARED ?= 1
+
+ # for lib$(NAME).so and /usr/include/($NAME) and such
+ NAME := proc
diff --git a/smartt-top/debian/patches/output_sort_time.patch b/smartt-top/debian/patches/output_sort_time.patch
new file mode 100644
index 0000000..32ec134
--- /dev/null
+++ b/smartt-top/debian/patches/output_sort_time.patch
@@ -0,0 +1,93 @@
+Description: More ps time sorting
+Author: Alfredo Esteban <aedelatorre@gmail.com>
+Bug-Debian: http://bugs.debian.org/508435
+Author: <csmall@debian.org>
+Index: b/ps/output.c
+===================================================================
+--- a/ps/output.c 2009-11-24 20:53:06.000000000 +1100
++++ b/ps/output.c 2009-11-24 21:00:30.000000000 +1100
+@@ -110,6 +110,20 @@
+ return (int)(P->NAME) - (int)(Q->NAME); \
+ }
+
++#define cook_time(P) (P->utime + P->stime) / Hertz
++
++#define cook_etime(P) seconds_since_boot - (unsigned long)(P->start_time / Hertz)
++
++#define CMP_COOKED_TIME(NAME) \
++static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
++ unsigned long p_time,q_time; \
++ p_time=cook_ ##NAME (P); \
++ q_time=cook_ ##NAME (Q); \
++ if (p_time < q_time) return -1; \
++ if (p_time > q_time) return 1; \
++ return 0; \
++}
++
+ CMP_INT(rtprio)
+ CMP_SMALL(sched)
+ CMP_INT(cutime)
+@@ -185,6 +199,9 @@
+
+ CMP_SMALL(state)
+
++CMP_COOKED_TIME(time)
++CMP_COOKED_TIME(etime)
++
+ /* approximation to: kB of address space that could end up in swap */
+ static int sr_swapable(const proc_t* P, const proc_t* Q) {
+ unsigned long p_swapable = P->vm_data + P->vm_stack;
+@@ -408,7 +425,7 @@
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ char *cp = outbuf;
+- t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz);
++ t = cook_etime(pp);
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+@@ -476,7 +493,7 @@
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ int c;
+- t = (pp->utime + pp->stime) / Hertz;
++ t = cook_time(pp);
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+@@ -1286,7 +1303,7 @@
+ {"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT},
+ {"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT},
+ {"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/
+-{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
++{"atime", "TIME", pr_time, sr_time, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+ {"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/
+ {"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT},
+ {"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT},
+@@ -1305,7 +1322,7 @@
+ {"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/
+ {"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
+ {"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide!
+-{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/
++{"cputime", "TIME", pr_time, sr_time, 8, 0, DEC, ET|RIGHT}, /*time*/
+ {"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT},
+ {"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts?
+ {"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+@@ -1320,7 +1337,7 @@
+ {"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT},
+ {"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED},
+ {"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT},
+-{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, ET|RIGHT}, /* was 7 wide */
++{"etime", "ELAPSED", pr_etime, sr_etime, 11, 0, U98, ET|RIGHT}, /* was 7 wide */
+ {"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT},
+ {"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER},
+ {"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/
+@@ -1463,7 +1480,7 @@
+ {"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+ {"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT},
+ {"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT},
+-{"time", "TIME", pr_time, sr_nop, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
++{"time", "TIME", pr_time, sr_time, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+ {"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+ {"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+ {"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
diff --git a/smartt-top/debian/patches/path_max.patch b/smartt-top/debian/patches/path_max.patch
new file mode 100644
index 0000000..3cccc18
--- /dev/null
+++ b/smartt-top/debian/patches/path_max.patch
@@ -0,0 +1,76 @@
+Author: Madhusudan.C.S <madhusudancs@gmail.com>
+Description: Uses alloc instead of fixed PATH_MAX value
+Bug-Debian: http://bugs.debian.org/496274
+Index: b/proc/readproc.c
+===================================================================
+--- a/proc/readproc.c 2009-11-24 21:00:41.000000000 +1100
++++ b/proc/readproc.c 2009-11-24 21:00:44.000000000 +1100
+@@ -1036,7 +1036,7 @@
+ * and filled out proc_t structure.
+ */
+ proc_t * get_proc_stats(pid_t pid, proc_t *p) {
+- static char path[PATH_MAX], sbuf[1024];
++ static char path[32], sbuf[1024];
+ struct stat statbuf;
+
+ sprintf(path, "/proc/%d", pid);
+Index: b/pwdx.c
+===================================================================
+--- a/pwdx.c 2009-11-24 20:53:02.000000000 +1100
++++ b/pwdx.c 2009-11-24 21:00:44.000000000 +1100
+@@ -35,7 +35,6 @@
+
+ int main(int argc, char* argv[])
+ {
+- char buf[PATH_MAX+1];
+ regex_t re;
+ int i;
+
+@@ -59,6 +58,7 @@
+
+ for (i = 1; i < argc; i++) {
+ if (regexec(&re, argv[i], 0, NULL, 0) != 0) {
++ char buf[27 + strlen (argv[i]) + 1]; // Constant 27 is the length of the error string "pwdx: ... "
+ snprintf(buf, sizeof buf, "pwdx: invalid process id: %s\n", argv[i]);
+ die(buf);
+ }
+@@ -68,9 +68,13 @@
+
+ regfree(&re);
+
++ int alloclen = 128;
++ char *pathbuf = malloc(alloclen);
++
+ for (i = 1; i < argc; i++) {
+- char * s = buf;
++ char * s;
+ int len;
++ char buf[10 + strlen(argv[i]) + 1]; // Constant 10 is the length of strings "/proc/" + "/cwd" + 1
+
+ // At this point, all arguments are in the form /proc/nnnn
+ // or nnnn, so a simple check based on the first char is
+@@ -82,14 +86,22 @@
+
+ // buf contains /proc/nnnn/cwd symlink name on entry, the
+ // target of that symlink on return
+- if ((len = readlink(buf, buf, PATH_MAX)) < 0) {
++ while ((len = readlink(buf, pathbuf, alloclen)) == alloclen) {
++ alloclen *= 2;
++ pathbuf = realloc(pathbuf, alloclen);
++ }
++
++ if (len < 0) {
+ s = strerror(errno == ENOENT ? ESRCH : errno);
+ } else {
+- buf[len] = 0;
++ pathbuf[len] = 0;
++ s = pathbuf;
+ }
+
+ printf("%s: %s\n", argv[i], s);
+ }
+
++ free(pathbuf);
++
+ return 0;
+ }
diff --git a/smartt-top/debian/patches/pgrep.1.patch b/smartt-top/debian/patches/pgrep.1.patch
new file mode 100644
index 0000000..a71e33d
--- /dev/null
+++ b/smartt-top/debian/patches/pgrep.1.patch
@@ -0,0 +1,208 @@
+Author: <csmall@debian.org>
+Description: Cleanup pgrep manual page #282168
+Uses .BR for see also section #437678
+Index: b/pgrep.1
+===================================================================
+--- a/pgrep.1 2009-11-24 21:00:45.000000000 +1100
++++ b/pgrep.1 2009-11-24 21:00:47.000000000 +1100
+@@ -2,35 +2,39 @@
+ .\" Licensed under version 2 of the GNU General Public License.
+ .\" Copyright 2000 Kjetil Torgrim Homme
+ .\"
+-.TH PGREP 1 "June 25, 2000" "Linux" "Linux User's Manual"
++.TH PGREP 1 "October 5, 2007" "Linux" "Linux User's Manual"
+ .SH NAME
+ pgrep, pkill \- look up or signal processes based on name and other attributes
+
+ .SH SYNOPSIS
+-pgrep [\-cflvx] [\-d \fIdelimiter\fP] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
+-.br
+- [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
+-.br
+- [\-t \fIterm\fP,...] [\fIpattern\fP]
+-
+-pkill [\-\fIsignal\fP] [\-fvx] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
+-.br
+- [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
+-.br
+- [\-t \fIterm\fP,...] [\fIpattern\fP]
++.na
++\fBpgrep\fR [\fB\-cflvx\fR] [\fB\-d\ \fIdelimiter\fR] [\fB\-n\fR|\fB\-o\fR] \
++[\fB\-P\ \fIppid\fR,...] [\fB\-g\ \fIpgrp\fR,...] [\fB\-s\ \fIsid\fR,...] \
++[\fB\-u\ \fIeuid\fR,...] [\fB\-U\ \fIuid\fR,...] [\fB\-G\ \fIgid\fR,...] \
++[\fB\-t\ \fIterm\fR,...] [\fIpattern\fR]
++
++.HP
++\fBpkill\fR [\fB\-\fIsignal\fR] [\fB\-fvx\fR] [\fB\-n\fR|\fB\-o\fR] \
++[\fB\-P\ \fIppid\fR,...] [\fB\-g\ \fIpgrp\fR,...] [\fB\-s\ \fIsid\fR,...] \
++[\fB\-u\ \fIeuid\fR,...] [\fB\-U\ \fIuid\fR,...] [\fB\-G\ \fIgid\fR,...] \
++[\fB\-t\ \fIterm\fR,...] [\fIpattern\fR]
+
+ .SH DESCRIPTION
+ \fBpgrep\fP looks through the currently running processes and lists the
+ process IDs which matches the selection criteria to stdout. All
+ the criteria have to match. For example,
+
+-pgrep -u root sshd
++.IP
++$ pgrep \-u root sshd
+
++.PP
+ will only list the processes called \fBsshd\fP AND owned by \fBroot\fP.
+ On the other hand,
+
+-pgrep -u root,daemon
++.IP
++$ pgrep \-u root,daemon
+
++.PP
+ will list the processes owned by \fBroot\fP OR \fBdaemon\fP.
+
+ \fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP)
+@@ -38,18 +42,18 @@
+
+ .SH OPTIONS
+ .TP
+-\-c
++\fB\-c\fR
+ Suppress normal output; instead print a count of matching processes.
+ .TP
+-\-d \fIdelimiter\fP
++\fB\-d \fIdelimiter\fP
+ Sets the string used to delimit each process ID in the output (by
+ default a newline). (\fBpgrep\fP only.)
+ .TP
+-\-f
++\fB\-f\fR
+ The \fIpattern\fP is normally only matched against the process name.
+-When \-f is set, the full command line is used.
++When \fB\-f\fR is set, the full command line is used.
+ .TP
+-\-g \fIpgrp\fP,...
++\fB\-g \fIpgrp\fP,...
+ Only match processes in the process group IDs listed. Process group 0
+ is translated into \fBpgrep\fP's or \fBpkill\fP's own process group.
+ .TP
+@@ -57,40 +61,40 @@
+ Only match processes whose real group ID is listed. Either the
+ numerical or symbolical value may be used.
+ .TP
+-\-l
++\fB\-l\fR
+ List the process name as well as the process ID. (\fBpgrep\fP only.)
+ .TP
+-\-n
++\fB\-n\fR
+ Select only the newest (most recently started) of the matching
+ processes.
+ .TP
+-\-o
++\fB\-o\fR
+ Select only the oldest (least recently started) of the matching
+ processes.
+ .TP
+-\-P \fIppid\fP,...
++\fB\-P \fIppid\fP,...
+ Only match processes whose parent process ID is listed.
+ .TP
+-\-s \fIsid\fP,...
++\fB\-s \fIsid\fP,...
+ Only match processes whose process session ID is listed. Session ID 0
+ is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID.
+ .TP
+-\-t \fIterm\fP,...
++\fB\-t \fIterm\fP,...
+ Only match processes whose controlling terminal is listed. The
+ terminal name should be specified without the "/dev/" prefix.
+ .TP
+-\-u \fIeuid\fP,...
++\fB\-u \fIeuid\fP,...
+ Only match processes whose effective user ID is listed. Either the
+ numerical or symbolical value may be used.
+ .TP
+-\-U \fIuid\fP,...
++\fB\-U \fIuid\fP,...
+ Only match processes whose real user ID is listed. Either the
+ numerical or symbolical value may be used.
+ .TP
+-\-v
++\fB\-v\fR
+ Negates the matching.
+ .TP
+-\-x
++\fB\-x\fR
+ Only match processes whose name (or command line if \-f is specified)
+ \fBexactly\fP match the \fIpattern\fP.
+ .TP
+@@ -107,32 +111,40 @@
+ .SH EXAMPLES
+ Example 1: Find the process ID of the \fBnamed\fP daemon:
+
+-unix$ pgrep \-u root named
++.IP
++$ pgrep \-u root named
+
++.PP
+ Example 2: Make \fBsyslog\fP reread its configuration file:
+
+-unix$ pkill \-HUP syslogd
++.IP
++$ pkill \-HUP syslogd
+
++.PP
+ Example 3: Give detailed information on all \fBxterm\fP processes:
+
+-unix$ ps \-fp $(pgrep \-d, \-x xterm)
++.IP
++$ ps \-fp $(pgrep \-d, \-x xterm)
+
++.PP
+ Example 4: Make all \fBnetscape\fP processes run nicer:
+
+-unix$ renice +4 `pgrep netscape`
++.IP
++$ renice +4 `pgrep netscape`
+
+ .SH "EXIT STATUS"
++.PD 0
+ .TP
+-.I "0"
++.I 0
+ One or more processes matched the criteria.
+ .TP
+-.I "1"
++.I 1
+ No processes matched.
+ .TP
+-.I "2"
++.I 2
+ Syntax error in the command line.
+ .TP
+-.I "3"
++.I 3
+ Fatal error: out of memory etc.
+
+ .SH NOTES
+@@ -144,13 +156,19 @@
+ itself as a match.
+
+ .SH BUGS
+-The options \-n and \-o and \-v can not be combined. Let me know if
+-you need to do this.
++The options \fB\-n\fP and \fB\-o\fP and \fB\-v\fP can not be combined.
++Let me know if you need to do this.
+
+ Defunct processes are reported.
+
+ .SH "SEE ALSO"
+-ps(1) regex(7) signal(7) killall(1) skill(1) kill(1) kill(2)
++.BR ps (1),
++.BR regex (7),
++.BR signal (7),
++.BR killall (1),
++.BR skill (1),
++.BR kill (1),
++.BR kill (2)
+
+ .SH STANDARDS
+ \fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This
diff --git a/smartt-top/debian/patches/pgrep_c_option.patch b/smartt-top/debian/patches/pgrep_c_option.patch
new file mode 100644
index 0000000..63502ed
--- /dev/null
+++ b/smartt-top/debian/patches/pgrep_c_option.patch
@@ -0,0 +1,85 @@
+Description: c option for pgrep for counting number of matched proceesses
+Author: Craig Small <csmall@debian.org>
+Bug-Debian: http://bugs.debian.org/375791
+Index: b/pgrep.1
+===================================================================
+--- a/pgrep.1 2009-11-24 20:53:02.000000000 +1100
++++ b/pgrep.1 2009-11-24 21:00:45.000000000 +1100
+@@ -7,7 +7,7 @@
+ pgrep, pkill \- look up or signal processes based on name and other attributes
+
+ .SH SYNOPSIS
+-pgrep [\-flvx] [\-d \fIdelimiter\fP] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
++pgrep [\-cflvx] [\-d \fIdelimiter\fP] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
+ .br
+ [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
+ .br
+@@ -38,6 +38,9 @@
+
+ .SH OPTIONS
+ .TP
++\-c
++Suppress normal output; instead print a count of matching processes.
++.TP
+ \-d \fIdelimiter\fP
+ Sets the string used to delimit each process ID in the output (by
+ default a newline). (\fBpgrep\fP only.)
+Index: b/pgrep.c
+===================================================================
+--- a/pgrep.c 2009-11-24 21:00:41.000000000 +1100
++++ b/pgrep.c 2009-11-24 21:00:45.000000000 +1100
+@@ -54,6 +54,7 @@
+ static int opt_newest = 0;
+ static int opt_negate = 0;
+ static int opt_exact = 0;
++static int opt_count = 0;
+ static int opt_signal = SIGTERM;
+ static int opt_lock = 0;
+ static int opt_case = 0;
+@@ -79,7 +80,7 @@
+ if (i_am_pkill)
+ fprintf (fp, "Usage: pkill [-SIGNAL] [-fvx] ");
+ else
+- fprintf (fp, "Usage: pgrep [-flvx] [-d DELIM] ");
++ fprintf (fp, "Usage: pgrep [-cflvx] [-d DELIM] ");
+ fprintf (fp, "[-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n"
+ "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] "
+ "[PATTERN]\n");
+@@ -565,7 +566,7 @@
+ strcat (opts, "ld:");
+ }
+
+- strcat (opts, "LF:fnovxP:g:s:u:U:G:t:?V");
++ strcat (opts, "LF:cfnovxP:g:s:u:U:G:t:?V");
+
+ while ((opt = getopt (argc, argv, opts)) != -1) {
+ switch (opt) {
+@@ -613,6 +614,9 @@
+ exit(EXIT_SUCCESS);
+ // case 'c': // Solaris: match by contract ID
+ // break;
++ case 'c':
++ opt_count = 1;
++ break;
+ case 'd': // Solaris: change the delimiter
+ opt_delim = strdup (optarg);
+ break;
+@@ -724,10 +728,14 @@
+ procs[i].num, strerror (errno));
+ }
+ } else {
+- if (opt_long)
+- output_strlist(procs,num);
+- else
+- output_numlist(procs,num);
++ if (opt_count) {
++ fprintf(stdout, "%ld\n", num);
++ } else {
++ if (opt_long)
++ output_strlist (procs,num);
++ else
++ output_numlist (procs,num);
++ }
+ }
+ return !num; // exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE)
+ }
diff --git a/smartt-top/debian/patches/pgrep_start_time.patch b/smartt-top/debian/patches/pgrep_start_time.patch
new file mode 100644
index 0000000..68cc0bc
--- /dev/null
+++ b/smartt-top/debian/patches/pgrep_start_time.patch
@@ -0,0 +1,16 @@
+Author: Craig Small <csmall@debian.org>
+Description: pgrep.c saved_start_time was missed
+Index: b/pgrep.c
+===================================================================
+--- a/pgrep.c 2009-11-24 20:53:03.000000000 +1100
++++ b/pgrep.c 2009-11-24 21:00:41.000000000 +1100
+@@ -430,7 +430,8 @@
+ preg = do_regcomp();
+
+ if (opt_newest) saved_start_time = 0ULL;
+- if (opt_oldest) saved_start_time = ~0ULL;
++ else
++ saved_start_time = ~0ULL;
+ if (opt_newest) saved_pid = 0;
+ if (opt_oldest) saved_pid = INT_MAX;
+
diff --git a/smartt-top/debian/patches/pgrep_usage_exitcode.patch b/smartt-top/debian/patches/pgrep_usage_exitcode.patch
new file mode 100644
index 0000000..efe752b
--- /dev/null
+++ b/smartt-top/debian/patches/pgrep_usage_exitcode.patch
@@ -0,0 +1,15 @@
+Author: <hesso@pool.math.tu-berlin.de>
+Description: Distinguish between invalid commandline parameters and '-?'.
+Index: b/pgrep.c
+===================================================================
+--- a/pgrep.c 2009-11-24 21:00:45.000000000 +1100
++++ b/pgrep.c 2009-11-24 21:00:49.000000000 +1100
+@@ -681,7 +681,7 @@
+ // case 'z': // Solaris: match by zone ID
+ // break;
+ case '?':
+- usage (opt);
++ usage (optopt?optopt:opt);
+ break;
+ }
+ }
diff --git a/smartt-top/debian/patches/pmap.1.patch b/smartt-top/debian/patches/pmap.1.patch
new file mode 100644
index 0000000..114fe86
--- /dev/null
+++ b/smartt-top/debian/patches/pmap.1.patch
@@ -0,0 +1,48 @@
+Description: Cleanup pmap.1 manual page
+Author: Brendan O'Dea <bod@debian.org>
+Bug-Debian: http://bugs.debian.org/282168
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/pmap.1
+===================================================================
+--- a/pmap.1 2009-11-24 20:53:06.000000000 +1100
++++ b/pmap.1 2009-11-24 21:00:30.000000000 +1100
+@@ -10,25 +10,29 @@
+ pmap \- report memory map of a process
+
+ .SH SYNOPSIS
+-.nf
+-pmap [ -x | -d ] [ -q ] pids...
+-pmap -V
+-.fi
++.B pmap
++.RB [ \-x | \-d ]
++.RB [ \-q ]
++.I pid
++\& ...
++.br
++.B pmap \-V
+
+ .SH DESCRIPTION
+ The pmap command reports the memory map of a process or processes.
+
+ .SH "GENERAL OPTIONS"
+ .TS
+-l l l.
+--x extended Show the extended format.
+--d device Show the device format.
+--q quiet Do not display some header/footer lines.
+--V show version Displays version of program.
++lB l l.
++\-x extended Show the extended format.
++\-d device Show the device format.
++\-q quiet Do not display some header/footer lines.
++\-V show version Displays version of program.
+ .TE
+
+ .SH "SEE ALSO"
+-ps(1) pgrep(1)
++.BR ps(1),
++.BR pgrep(1)
+
+ .SH STANDARDS
+ No standards apply, but pmap looks an awful lot like a SunOS command.
diff --git a/smartt-top/debian/patches/pmaps_smaps.patch b/smartt-top/debian/patches/pmaps_smaps.patch
new file mode 100644
index 0000000..02561fd
--- /dev/null
+++ b/smartt-top/debian/patches/pmaps_smaps.patch
@@ -0,0 +1,141 @@
+Description: provides information for pmap -x option
+ Similiar idea to pmap written by Robert Love
+Bug-Debian: http://bugs.debian.org/347476
+Bug-Debian: http://bugs.debian.org/505571
+Author: Craig Small <csmall@debian.org>
+--- a/pmap.c
++++ b/pmap.c
+@@ -126,24 +126,37 @@
+ char buf[32];
+ char mapbuf[9600];
+ char cmdbuf[512];
++ FILE *fp;
+ unsigned long total_shared = 0ul;
+ unsigned long total_private_readonly = 0ul;
+ unsigned long total_private_writeable = 0ul;
+
++ char *cp2=NULL;
++ unsigned long long rss = 0ull;
++ unsigned long long private_dirty = 0ull;
++ unsigned long long shared_dirty = 0ull;
++ unsigned long long total_rss = 0ull;
++ unsigned long long total_private_dirty = 0ull;
++ unsigned long long total_shared_dirty = 0ull;
++
+ // Overkill, but who knows what is proper? The "w" prog
+ // uses the tty width to determine this.
+ int maxcmd = 0xfffff;
+
+ sprintf(buf,"/proc/%u/maps",p->tgid);
+- if(!freopen(buf, "r", stdin)) return 1;
++ if ( (fp = fopen(buf, "r")) == NULL) return 1;
++ if (x_option) {
++ sprintf(buf,"/proc/%u/smaps",p->tgid);
++ if ( (fp = freopen(buf, "r", fp)) == NULL) return 1;
++ }
+
+ escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS);
+ printf("%u: %s\n", p->tgid, cmdbuf);
+
+ if(!q_option && (x_option|d_option)){
+ if(x_option){
+- if(sizeof(KLONG)==4) printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
+- else printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
++ if(sizeof(KLONG)==4) printf("Address Kbytes RSS Dirty Mode Mapping\n");
++ else printf("Address Kbytes RSS Dirty Mode Mapping\n");
+ }
+ if(d_option){
+ if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n");
+@@ -151,12 +164,54 @@
+ }
+ }
+
+- while(fgets(mapbuf,sizeof mapbuf,stdin)){
++ while(fgets(mapbuf,sizeof mapbuf,fp)){
+ char flags[32];
+ char *tmp; // to clean up unprintables
+- unsigned KLONG start, end, diff;
++ unsigned KLONG start, end, diff=0;
+ unsigned long long file_offset, inode;
+ unsigned dev_major, dev_minor;
++ unsigned long long smap_value;
++ char smap_key[20];
++
++ /* hex values are lower case or numeric, keys are upper */
++ if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') {
++ /* Its a key */
++ if (sscanf(mapbuf,"%20[^:]: %llu", smap_key, &smap_value) == 2) {
++ if (strncmp("Rss", smap_key, 3) == 0) {
++ rss = smap_value;
++ total_rss += smap_value;
++ continue;
++ }
++ if (strncmp("Shared_Dirty", smap_key, 12) == 0) {
++ shared_dirty = smap_value;
++ total_shared_dirty += smap_value;
++ continue;
++ }
++ if (strncmp("Private_Dirty", smap_key, 13) == 0) {
++ private_dirty = smap_value;
++ total_private_dirty += smap_value;
++ continue;
++ }
++ if (strncmp("Swap", smap_key, 4) == 0) { /*doesnt matter as long as last*/
++ printf(
++ (sizeof(KLONG)==8)
++ ? "%016"KLF"x %7lu %7llu %7llu %s %s\n"
++ : "%08lx %7lu %7llu %7llu %s %s\n",
++ start,
++ (unsigned long)(diff>>10),
++ rss,
++ (private_dirty + shared_dirty),
++ flags,
++ cp2
++ );
++ /* reset some counters */
++ rss = shared_dirty = private_dirty = 0ull;
++ continue;
++ }
++ /* Other keys */
++ continue;
++ }
++ }
+ sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
+
+ if(start > range_high)
+@@ -186,16 +241,9 @@
+ flags[5] = '\0';
+
+ if(x_option){
+- const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
+- printf(
+- (sizeof(KLONG)==8)
+- ? "%016"KLF"x %7lu - - - %s %s\n"
+- : "%08lx %7lu - - - %s %s\n",
+- start,
+- (unsigned long)(diff>>10),
+- flags,
+- cp
+- );
++ cp2 = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
++ /* printed with the keys */
++ continue;
+ }
+ if(d_option){
+ const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
+@@ -232,10 +280,12 @@
+ if(!q_option){
+ if(x_option){
+ if(sizeof(KLONG)==8){
+- printf("---------------- ------ ------ ------ ------\n");
++ printf("---------------- ------ ------ ------\n");
+ printf(
+- "total kB %15ld - - -\n",
+- (total_shared + total_private_writeable + total_private_readonly) >> 10
++ "total kB %15ld %7llu %7llu\n",
++ (total_shared + total_private_writeable + total_private_readonly) >> 10,
++ total_rss, (total_shared_dirty+total_private_dirty)
++
+ );
+ }else{
+ printf("-------- ------- ------- ------- -------\n");
diff --git a/smartt-top/debian/patches/proc_version_constructor.patch b/smartt-top/debian/patches/proc_version_constructor.patch
new file mode 100644
index 0000000..932ac72
--- /dev/null
+++ b/smartt-top/debian/patches/proc_version_constructor.patch
@@ -0,0 +1,37 @@
+Description: Call libproc constructors in strict order
+ Having one constructor depend on another means if the order is reversed
+ you get a different result. Patched based on idea by Tom Evans.
+Bug-Debian: http://bugs.debian.org/460331
+Author: Craig Small <csmall@debian.org>
+--- a/proc/sysinfo.c
++++ b/proc/sysinfo.c
+@@ -213,6 +213,7 @@
+ static void init_libproc(void) __attribute__((constructor));
+ static void init_libproc(void){
+ have_privs = check_for_privs();
++ init_Linux_version(); /* Must be called before we check code */
+ // ought to count CPUs in /proc/stat instead of relying
+ // on glibc, which foolishly tries to parse /proc/cpuinfo
+ //
+--- a/proc/version.c
++++ b/proc/version.c
+@@ -33,8 +33,7 @@
+
+ int linux_version_code;
+
+-static void init_Linux_version(void) __attribute__((constructor));
+-static void init_Linux_version(void) {
++void init_Linux_version(void) {
+ int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */
+ FILE *fp;
+ char buf[256];
+--- a/proc/version.h
++++ b/proc/version.h
+@@ -14,6 +14,7 @@
+
+ EXTERN_C_BEGIN
+
++void init_Linux_version(void); /* Get Linux version */
+ extern void display_version(void); /* display suite version */
+ extern const char procps_version[]; /* global buf for suite version */
+
diff --git a/smartt-top/debian/patches/ps_1_flt_output.patch b/smartt-top/debian/patches/ps_1_flt_output.patch
new file mode 100644
index 0000000..ea8f62a
--- /dev/null
+++ b/smartt-top/debian/patches/ps_1_flt_output.patch
@@ -0,0 +1,30 @@
+Description: Documenting maj_flt and min_flt output specifiers in ps.1
+Bug-Debian: http://bugs.debian.org/434221
+Author: Craig Small <csmall@debian.org>
+Last-Update: 2010-02-25
+--- a/ps/ps.1
++++ b/ps/ps.1
+@@ -4,7 +4,7 @@
+ .\" Quick hack conversion by Albert Cahalan, 1998.
+ .\" Licensed under version 2 of the Gnu General Public License.
+ .\"
+-.TH PS 1 "July 28, 2004" "Linux" "Linux User's Manual"
++.TH PS 1 "February 25, 2010" "Linux" "Linux User's Manual"
+ .\"
+ .\" To render this page:
+ .\" groff -t -b -man -X -P-resolution -P100 -Tps ps.1 &
+@@ -1084,6 +1084,14 @@
+ (alias\ \fBspid\fR,\ \fBtid\fR).
+ T}
+
++maj_flt MAJFLT T{
++The number of major page faults that have occured with this process.
++T}
++
++min_flt MINFLT T{
++The number of minor page faults that have occured with this process.
++T}
++
+ ni NI T{
+ nice value. This ranges from 19 (nicest) to \-20 (not\ nice to\ others),
+ see\ \fInice\fR(1). (alias\ \fBnice\fR).
diff --git a/smartt-top/debian/patches/ps_1_options.patch b/smartt-top/debian/patches/ps_1_options.patch
new file mode 100644
index 0000000..a03b335
--- /dev/null
+++ b/smartt-top/debian/patches/ps_1_options.patch
@@ -0,0 +1,548 @@
+Author: Jari Aalto <jari.aalto@cante.net>
+Description: Re-sort ps.1 options
+Bug-Debian: http://bugs.debian.org/518620
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/ps/ps.1
+===================================================================
+--- a/ps/ps.1 2009-11-24 21:00:48.000000000 +1100
++++ b/ps/ps.1 2009-11-24 21:00:49.000000000 +1100
+@@ -158,20 +158,6 @@
+ .PP
+ .\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+ .SH "SIMPLE PROCESS SELECTION"
+-.opt \-A
+-Select all processes. Identical to \fB\-e\fR.
+-
+-.opt \-N
+-Select all processes except those that fulfill the specified conditions.
+-(negates the selection) Identical to \fB\-\-deselect\fR.
+-
+-.opt T
+-Select all processes associated with this terminal. Identical to the
+-\fBt\fR option without any argument.
+-
+-.opt \-a
+-Select all processes except both session leaders (see \fIgetsid\fR(2)) and
+-processes not associated with a terminal.
+
+ .opt a
+ Lift the BSD\-style "only yourself" restriction, which is imposed upon
+@@ -183,9 +169,20 @@
+ list all processes with a terminal (tty),
+ or to list all processes when used together with the \fBx\fR option.
+
++.opt \-A
++Select all processes. Identical to \fB\-e\fR.
++
++.opt \-a
++Select all processes except both session leaders (see \fIgetsid\fR(2)) and
++processes not associated with a terminal.
++
+ .opt \-d
+ Select all processes except session leaders.
+
++.opt \-\-deselect
++Select all processes except those that fulfill the specified conditions.
++(negates the selection) Identical to \fB\-N\fR.
++
+ .opt \-e
+ Select all processes. Identical to \fB\-A\fR.
+
+@@ -194,11 +191,20 @@
+ .\" add in the group leaders -- at least according to the SunOS 4
+ .\" man page on the FreeBSD site. Uh oh. I think I had tested SunOS
+ .\" though, so maybe the code is correct.
++
+ .opt g
+ Really all, even session leaders. This flag is obsolete and may be
+ discontinued in a future release. It is normally implied by the \fBa\fR flag,
+ and is only useful when operating in the sunos4 personality.
+
++.opt \-N
++Select all processes except those that fulfill the specified conditions.
++(negates the selection) Identical to \fB\-\-deselect\fR.
++
++.opt T
++Select all processes associated with this terminal. Identical to the
++\fBt\fR option without any argument.
++
+ .opt r
+ Restrict the selection to only running processes.
+
+@@ -212,10 +218,6 @@
+ list all processes owned by you (same EUID as \fBps\fR),
+ or to list all processes when used together with the \fBa\fR option.
+
+-.opt \-\-deselect
+-Select all processes except those that fulfill the specified conditions.
+-(negates the selection) Identical to \fB\-N\fR.
+-
+ .\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+ .PD
+ .PP
+@@ -224,6 +226,13 @@
+ or comma\-separated list. They can be used multiple times.
+ For\ example:\ \fBps\ \-p\ "1\ 2"\ \-p\ 3,4\fR
+ .P
++
++.opt \-\fI123\fR
++Identical to \fB\-\-sid\ \fI123\fR.
++
++.opt \fI123\fR
++Identical to \fB\-\-pid\ \fI123\fR.
++
+ .opt \-C \ cmdlist
+ Select by command name.
+ .br
+@@ -237,24 +246,6 @@
+ \fIgrplist\fR list. The real group ID identifies the group of the user
+ who created the process, see \fIgetgid\fR(2).
+
+-.opt U \ userlist
+-Select by effective user ID (EUID) or name.
+-.br
+-This selects the processes whose effective user name
+-or ID is in \fIuserlist\fR.
+-The effective user\ ID describes the user whose file
+-access permissions are used by the process
+-(see\ \fIgeteuid\fR(2)).
+-Identical to \fB\-u\fR and\ \fB\-\-user\fR.
+-
+-.opt \-U \ userlist
+-select by real user ID (RUID) or name.
+-.br
+-It selects the processes whose real user name or ID is in the
+-\fIuserlist\fR list.
+-The real user ID identifies the user who created the process,
+-see\ \fIgetuid\fR(2).
+-
+ .opt \-g \ grplist
+ Select by session OR by effective group name.
+ .br
+@@ -266,6 +257,17 @@
+ Group ID numbers will work only when some group names are also specified.
+ See the \fB\-s\fR and \fB\-\-group\fR options.
+
++.opt \-\-Group \ grplist
++Select by real group ID (RGID) or name. Identical to \fB\-G\fR.
++
++.opt \-\-group \ grplist
++Select by effective group ID (EGID) or name.
++.br
++This selects the processes whose effective group name or ID is in
++\fIgrouplist\fR. The effective group ID describes the group whose file
++access permissions are used by the process (see\ \fIgeteuid\fR(2)).
++The \fB\-g\fR option is often an alternative to\ \fB\-\-group\fR.
++
+ .opt p \ pidlist
+ Select by process ID. Identical to \fB\-p\fR and\ \fB\-\-pid\fR.
+
+@@ -275,12 +277,25 @@
+ This selects the processes whose process ID numbers appear in
+ \fIpidlist\fR. Identical to \fBp\fR and\ \fB\-\-pid\fR.
+
++.opt \-\-pid \ pidlist
++Select by process\ ID. Identical to \fB\-p\fR\ and\ \fBp\fR.
++
++.opt \-\-ppid \ pidlist
++Select by parent process\ ID.
++This selects the processes
++with a parent\ process\ ID in \fRpidlist\fR.
++That\ is, it selects processes that are children
++of those listed in \fRpidlist\fR.
++
+ .opt \-s \ sesslist
+ Select by session ID.
+ .br
+ This selects the processes
+ with a session ID specified in\ \fIsesslist\fR.
+
++.opt \-\-sid \ sesslist
++Select by session\ ID. Identical to\ \fB\-s\fR.
++
+ .opt t \ ttylist
+ Select by tty. Nearly identical to \fB\-t\fR and \fB\-\-tty\fR,
+ but can also be used with an empty \fIttylist\fR to indicate
+@@ -297,6 +312,27 @@
+ forms: /dev/ttyS1, ttyS1, S1.
+ A\ plain "\-" may be used to select processes not attached to any terminal.
+
++.opt \-\-tty \ ttylist
++Select by terminal. Identical to \fB\-t\fR and\ \fBt\fR.
++
++.opt U \ userlist
++Select by effective user ID (EUID) or name.
++.br
++This selects the processes whose effective user name
++or ID is in \fIuserlist\fR.
++The effective user\ ID describes the user whose file
++access permissions are used by the process
++(see\ \fIgeteuid\fR(2)).
++Identical to \fB\-u\fR and\ \fB\-\-user\fR.
++
++.opt \-U \ userlist
++select by real user ID (RUID) or name.
++.br
++It selects the processes whose real user name or ID is in the
++\fIuserlist\fR list.
++The real user ID identifies the user who created the process,
++see\ \fIgetuid\fR(2).
++
+ .opt \-u \ userlist
+ Select by effective user ID (EUID) or name.
+ .br
+@@ -305,46 +341,13 @@
+ access permissions are used by the process (see\ \fIgeteuid\fR(2)).
+ Identical to \fBU\fR and \fB\-\-user\fR.
+
+-.opt \-\-Group \ grplist
+-Select by real group ID (RGID) or name. Identical to \fB\-G\fR.
+-
+ .opt \-\-User \ userlist
+ Select by real user ID (RUID) or name. Identical to \fB\-U\fR.
+
+-.opt \-\-group \ grplist
+-Select by effective group ID (EGID) or name.
+-.br
+-This selects the processes whose effective group name or ID is in
+-\fIgrouplist\fR. The effective group ID describes the group whose file
+-access permissions are used by the process (see\ \fIgeteuid\fR(2)).
+-The \fB\-g\fR option is often an alternative to\ \fB\-\-group\fR.
+-
+-.opt \-\-pid \ pidlist
+-Select by process\ ID. Identical to \fB\-p\fR\ and\ \fBp\fR.
+-
+-.opt \-\-ppid \ pidlist
+-Select by parent process\ ID.
+-This selects the processes
+-with a parent\ process\ ID in \fRpidlist\fR.
+-That\ is, it selects processes that are children
+-of those listed in \fRpidlist\fR.
+-
+-.opt \-\-sid \ sesslist
+-Select by session\ ID. Identical to\ \fB\-s\fR.
+-
+-.opt \-\-tty \ ttylist
+-Select by terminal. Identical to \fB\-t\fR and\ \fBt\fR.
+-
+ .opt \-\-user \ userlist
+ Select by effective user ID (EUID) or name.
+ Identical to \fB\-u\fR and\ \fBU\fR.
+
+-.opt \-\fI123\fR
+-Identical to \fB\-\-sid\ \fI123\fR.
+-
+-.opt \fI123\fR
+-Identical to \fB\-\-pid\ \fI123\fR.
+-
+ .\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+ .PD
+ .PP
+@@ -353,38 +356,12 @@
+ The output may differ by personality.
+ .PP
+
+-.opt \-F
+-extra full format. See the \fB\-f\fR option, which \fB\-F\fR implies.
+-
+-.opt \-O \ format
+-is like \fB\-o\fR, but preloaded with some default columns.
+-Identical to \fB\-o\ pid,\fIformat\fB,state,tname,time,command\fR
+-or \fB\-o\ pid,\fIformat\fB,tname,time,cmd\fR, see\ \fB\-o\fR\ below.
+-
+-.opt O \ format
+-is preloaded \fBo\fR (overloaded).
+-.br
+-The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
+-format with some common fields predefined) or can be used to specify
+-sort order. Heuristics are used to determine the behavior of this
+-option. To ensure that the desired behavior is obtained (sorting or
+-formatting), specify the option in some other way
+-(e.g. with \fB\-O\fR or \fB\-\-sort\fR).
+-When used as a formatting option, it is identical to \fB\-O\fR, with the
+-BSD\ personality.
+-
+-.opt \-M
+-Add a column of security data. Identical to \fBZ\fR. (for\ SE\ Linux)
+-
+-.opt X
+-Register format.
+-
+-.opt Z
+-Add a column of security data. Identical to \fB\-M\fR. (for\ SE\ Linux)
+-
+ .opt \-c
+ Show different scheduler information for the \fB\-l\fR option.
+
++.opt \-\-context
++Display security context format. (for\ SE\ Linux)
++
+ .opt \-f
+ does full\-format listing. This option can be combined with many
+ other UNIX\-style options to add additional columns. It also causes
+@@ -393,6 +370,12 @@
+ See the \fBc\fR option, the format keyword \fBargs\fR, and the
+ format keyword \fBcomm\fR.
+
++.opt \-F
++extra full format. See the \fB\-f\fR option, which \fB\-F\fR implies.
++
++.opt \-\-format \ format
++user\-defined format. Identical to \fB\-o\fR and \fBo\fR.
++
+ .opt j
+ BSD job control format.
+
+@@ -405,6 +388,26 @@
+ .opt \-l
+ long format. The \fB\-y\fR option is often useful with this.
+
++.opt \-M
++Add a column of security data. Identical to \fBZ\fR. (for\ SE\ Linux)
++
++.opt O \ format
++is preloaded \fBo\fR (overloaded).
++.br
++The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
++format with some common fields predefined) or can be used to specify
++sort order. Heuristics are used to determine the behavior of this
++option. To ensure that the desired behavior is obtained (sorting or
++formatting), specify the option in some other way
++(e.g. with \fB\-O\fR or \fB\-\-sort\fR).
++When used as a formatting option, it is identical to \fB\-O\fR, with the
++BSD\ personality.
++
++.opt \-O \ format
++is like \fB\-o\fR, but preloaded with some default columns.
++Identical to \fB\-o\ pid,\fIformat\fB,state,tname,time,command\fR
++or \fB\-o\ pid,\fIformat\fB,tname,time,cmd\fR, see\ \fB\-o\fR\ below.
++
+ .opt o \ format
+ specify user\-defined format. Identical to \fB\-o\fR and
+ \fB\-\-format\fR.
+@@ -440,15 +443,15 @@
+ .opt v
+ display virtual memory format
+
++.opt X
++Register format.
++
+ .opt \-y
+ Do not show flags; show rss in place of addr.
+ This option can only be used with \fB\-l\fR.
+
+-.opt \-\-format \ format
+-user\-defined format. Identical to \fB\-o\fR and \fBo\fR.
+-
+-.opt \-\-context
+-Display security context format. (for\ SE\ Linux)
++.opt Z
++Add a column of security data. Identical to \fB\-M\fR. (for\ SE\ Linux)
+
+ .\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+ .PD
+@@ -459,37 +462,6 @@
+ .\" .B C
+ .\" use raw CPU time for %CPU instead of decaying average
+
+-.opt \-H
+-show process hierarchy (forest)
+-
+-.opt N \ namelist
+-Specify namelist file. Identical to \fB\-n\fR, see \fB\-n\fR above.
+-
+-.opt O \ order
+-Sorting order. (overloaded)
+-.br
+-The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
+-format with some common fields predefined) or can be used to specify
+-sort order. Heuristics are used to determine the behavior of this
+-option. To ensure that the desired behavior is obtained (sorting or
+-formatting), specify the option in some other way (e.g. with \fB\-O\fR
+-or \fB\-\-sort\fR).
+-
+-For sorting, obsolete BSD \fBO\fR option syntax is
+-\fBO\fR[\fB+\fR|\fB\-\fR]\fIk1\fR[,[\fB+\fR|\fB\-\fR]\fIk2\fR[,...]].
+-It orders the processes listing according to the multilevel sort specified by
+-the sequence of one\-letter short keys \fIk1\fR, \fIk2\fR, ... described
+-in the \fBOBSOLETE SORT KEYS\fR section below.
+-The\ "+" is currently optional,
+-merely re\-iterating the default direction on a key,
+-but may help to distinguish an \fBO\fR sort from an \fBO\fR format.
+-The\ "\-" reverses direction only on the key it precedes.
+-
+-.opt S
+-Sum up some information, such as CPU usage, from dead child processes
+-into their parent. This is useful for examining a system where a
+-parent process repeatedly forks off short\-lived children to do work.
+-
+ .opt c
+ Show the true command name. This is derived from the name of the
+ executable file, rather than from the argv value. Command arguments
+@@ -502,12 +474,24 @@
+ See the \fB\-f\fR option, the format keyword \fBargs\fR, and the
+ format keyword \fBcomm\fR.
+
++.opt \-\-cols \ n
++set screen width
++
++.opt \-\-columns \ n
++set screen width
++
++.opt \-\-cumulative
++include some dead child process data (as a sum with the parent)
++
+ .opt e
+ Show the environment after the command.
+
+ .opt f
+ ASCII\-art process hierarchy (forest)
+
++.opt \-\-forest
++ASCII art process tree
++
+ .opt h
+ No header. (or, one header per screen in the BSD personality)
+ .br
+@@ -521,6 +505,12 @@
+ and \fB\-\-no\-headers\fR to enable printing headers each page or
+ disable headers entirely, respectively.
+
++.opt \-H
++show process hierarchy (forest)
++
++.opt \-\-headers
++repeat header lines, one per page of output
++
+ .opt k \ spec
+ specify sorting order. Sorting syntax is
+ [\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]]
+@@ -557,40 +547,47 @@
+ .br
+ /System.map
+
++.opt \-\-lines \ n
++set screen height
++
+ .opt n
+ Numeric output for WCHAN and USER. (including all types of UID and GID)
+
+-.opt \-w
+-Wide output. Use this option twice for unlimited width.
+-
+-.opt w
+-Wide output. Use this option twice for unlimited width.
+-
+-.opt \-\-cols \ n
+-set screen width
+-
+-.opt \-\-columns \ n
+-set screen width
+-
+-.opt \-\-cumulative
+-include some dead child process data (as a sum with the parent)
++.opt N \ namelist
++Specify namelist file. Identical to \fB\-n\fR, see \fB\-n\fR above.
+
+-.opt \-\-forest
+-ASCII art process tree
++.opt O \ order
++Sorting order. (overloaded)
++.br
++The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
++format with some common fields predefined) or can be used to specify
++sort order. Heuristics are used to determine the behavior of this
++option. To ensure that the desired behavior is obtained (sorting or
++formatting), specify the option in some other way (e.g. with \fB\-O\fR
++or \fB\-\-sort\fR).
+
+-.opt \-\-headers
+-repeat header lines, one per page of output
++For sorting, obsolete BSD \fBO\fR option syntax is
++\fBO\fR[\fB+\fR|\fB\-\fR]\fIk1\fR[,[\fB+\fR|\fB\-\fR]\fIk2\fR[,...]].
++It orders the processes listing according to the multilevel sort specified by
++the sequence of one\-letter short keys \fIk1\fR, \fIk2\fR, ... described
++in the \fBOBSOLETE SORT KEYS\fR section below.
++The\ "+" is currently optional,
++merely re\-iterating the default direction on a key,
++but may help to distinguish an \fBO\fR sort from an \fBO\fR format.
++The\ "\-" reverses direction only on the key it precedes.
+
+ .opt \-\-no\-headers
+ print no header line at all. \-\-no\-heading is an alias for this
+ option.
+
+-.opt \-\-lines \ n
+-set screen height
+-
+ .opt \-\-rows \ n
+ set screen height
+
++.opt S
++Sum up some information, such as CPU usage, from dead child processes
++into their parent. This is useful for examining a system where a
++parent process repeatedly forks off short\-lived children to do work.
++
+ .opt \-\-sort \ spec
+ specify sorting order. Sorting syntax is
+ [\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]]
+@@ -599,6 +596,12 @@
+ lexicographic order. Identical to\ \fBk\fR.
+ For example: \fBps\ jax\ \-\-sort=uid,\-ppid,+pid\fR
+
++.opt w
++Wide output. Use this option twice for unlimited width.
++
++.opt \-w
++Wide output. Use this option twice for unlimited width.
++
+ .opt \-\-width \ n
+ set screen width
+
+@@ -614,35 +617,35 @@
+ .opt \-L
+ Show threads, possibly with LWP and NLWP columns
+
+-.opt \-T
+-Show threads, possibly with SPID column
+-
+ .opt m
+ Show threads after processes
+
+ .opt \-m
+ Show threads after processes
+
++.opt \-T
++Show threads, possibly with SPID column
++
+ .\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+ .PD
+ .PP
+ .SH "OTHER INFORMATION"
+ .PD 0
+
++.opt \-\-help
++Print a help message.
++
++.opt \-\-info
++Print debugging info.
++
+ .opt L
+ List all format specifiers.
+
+-.opt \-V
+-Print the procps version.
+-
+ .opt V
+ Print the procps version.
+
+-.opt \-\-help
+-Print a help message.
+-
+-.opt \-\-info
+-Print debugging info.
++.opt \-V
++Print the procps version.
+
+ .opt \-\-version
+ Print the procps version.
diff --git a/smartt-top/debian/patches/ps_cgroup_display.patch b/smartt-top/debian/patches/ps_cgroup_display.patch
new file mode 100644
index 0000000..362c8f6
--- /dev/null
+++ b/smartt-top/debian/patches/ps_cgroup_display.patch
@@ -0,0 +1,157 @@
+Description: ps displays cgroup
+Author: Swann Perarnau <swann.perarnau@imag.fr>
+Bug-Debian: http://bugs.debian.org/469669
+Reviewed-by: Craig Small <csmall@debian.org>
+--- a/proc/readproc.c
++++ b/proc/readproc.c
+@@ -598,6 +598,17 @@
+ p->environ = file2strvec(path, "environ");
+ else
+ p->environ = NULL;
++
++ if(linux_version_code>=LINUX_VERSION(2,6,24) && (flags & PROC_FILLCGROUP)) {
++ p->cgroup = file2strvec(path, "cgroup"); /* read /proc/#/cgroup */
++ if(p->cgroup && *p->cgroup) {
++ int i = strlen(*p->cgroup);
++ if( (*p->cgroup)[i-1]=='\n' )
++ (*p->cgroup)[i-1] = ' '; //little hack to remove trailing \n
++ }
++ }
++ else
++ p->cgroup = NULL;
+
+ return p;
+ next_proc:
+@@ -686,7 +697,7 @@
+ t->cmdline = p->cmdline; // better not free these until done with all threads!
+ t->environ = p->environ;
+ #endif
+-
++ t->cgroup = p->cgroup;
+ t->ppid = p->ppid; // ought to put the per-task ppid somewhere
+
+ return t;
+@@ -896,6 +907,8 @@
+ free((void*)*p->cmdline);
+ if (p->environ)
+ free((void*)*p->environ);
++ if (p->cgroup)
++ free((void*)*p->cgroup);
+ free(p);
+ }
+
+--- a/proc/readproc.h
++++ b/proc/readproc.h
+@@ -139,6 +139,7 @@
+ tpgid, // stat terminal process group id
+ exit_signal, // stat might not be SIGCHLD
+ processor; // stat current (or most recent?) CPU
++ char **cgroup; // cgroup current cgroup, looks like a classic filepath
+ } proc_t;
+
+ // PROCTAB: data structure holding the persistent information readproc needs
+@@ -236,8 +237,9 @@
+ #define PROC_FILLSTAT 0x0040 // read stat -- currently unconditional
+ #define PROC_FILLWCHAN 0x0080 // look up WCHAN name
+ #define PROC_FILLARG 0x0100 // alloc and fill in `cmdline'
++#define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup`
+
+-#define PROC_LOOSE_TASKS 0x0200 // threat threads as if they were processes
++#define PROC_LOOSE_TASKS 0x2000 // threat threads as if they were processes
+
+ // Obsolete, consider only processes with one of the passed:
+ #define PROC_PID 0x1000 // process id numbers ( 0 terminated)
+--- a/ps/display.c
++++ b/ps/display.c
+@@ -223,8 +223,7 @@
+
+ #define needs_for_format (proc_format_needs|task_format_needs)
+
+-#define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM)
+-
++#define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM|PROC_FILLCGROUP)
+ /***** munge lists and determine openproc() flags */
+ static void lists_and_needs(void){
+ check_headers();
+@@ -342,6 +341,7 @@
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
++ if(buf.cgroup) free((void*)*buf.cgroup);
+ }
+ break;
+ case TF_show_proc|TF_loose_tasks: // H option
+@@ -354,6 +354,7 @@
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
++ if(buf.cgroup) free((void*)*buf.cgroup);
+ }
+ break;
+ case TF_show_proc|TF_show_task: // m and -m options
+@@ -366,7 +367,8 @@
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+- }
++ if(buf.cgroup) free((void*)*buf.cgroup);
++ }
+ break;
+ case TF_show_task: // -L and -T options
+ while(readproc(ptp,&buf)){
+@@ -377,7 +379,8 @@
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+- }
++ if(buf.cgroup) free((void*)*buf.cgroup);
++ }
+ break;
+ }
+ closeproc(ptp);
+--- a/ps/output.c
++++ b/ps/output.c
+@@ -376,6 +376,26 @@
+ return max_rightward-rightward;
+ }
+
++static int pr_cgroup(char *restrict const outbuf,const proc_t *restrict const pp) {
++ if(pp->cgroup && *pp->cgroup) {
++ char *endp = outbuf;
++ int rightward=max_rightward;
++ if(forest_prefix){
++ int fh = forest_helper(outbuf);
++ endp += fh;
++ rightward -= fh;
++ }
++ if(rightward>1){
++ *endp++ = ' ';
++ rightward--;
++ endp += escape_str(endp, *pp->cgroup, OUTBUF_SIZE, &rightward);
++ }
++ return max_rightward-rightward;
++ }
++ else
++ return pr_nop(outbuf,pp);
++}
++
+ /* "ucomm" is the same thing: short unless -f */
+ static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+@@ -1274,7 +1294,7 @@
+ #define GRP PROC_FILLGRP /* gid_t -> group names */
+ #define WCH PROC_FILLWCHAN /* do WCHAN lookup */
+
+-
++#define CGRP PROC_FILLCGROUP /* read cgroup */
+ /* TODO
+ * pull out annoying BSD aliases into another table (to macro table?)
+ * add sorting functions here (to unify names)
+@@ -1310,6 +1330,7 @@
+ {"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT},
+ {"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT},
+ {"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/
++{"cgroup", "CGROUP", pr_cgroup, sr_nop, 27, CGRP, LNX, PO|UNLIMITED},
+ {"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT},
+ {"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/
+ {"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT},
diff --git a/smartt-top/debian/patches/ps_size_sz.patch b/smartt-top/debian/patches/ps_size_sz.patch
new file mode 100644
index 0000000..ff30815
--- /dev/null
+++ b/smartt-top/debian/patches/ps_size_sz.patch
@@ -0,0 +1,29 @@
+Author: <csmall@debian.org>
+Description: Renames second SZ to SIZE, means we don't follow SCO but less confusion
+Bug-Debian: http://bugs.debian.org/541061
+Index: b/ps/output.c
+===================================================================
+--- a/ps/output.c 2009-11-24 21:00:47.000000000 +1100
++++ b/ps/output.c 2009-11-24 21:00:48.000000000 +1100
+@@ -1522,7 +1522,7 @@
+ {"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/
+ {"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/
+ {"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/
+-{"size", "SZ", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT},
++{"size", "SIZE", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT},
+ {"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT},
+ {"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT},
+ {"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/
+Index: b/ps/ps.1
+===================================================================
+--- a/ps/ps.1 2009-11-24 21:00:47.000000000 +1100
++++ b/ps/ps.1 2009-11-24 21:00:48.000000000 +1100
+@@ -1254,7 +1254,7 @@
+ see\ \fBblocked\fR. (alias\ \fBblocked\fR,\ \fBsig_block\fR).
+ T}
+
+-size SZ T{
++size SIZE T{
+ approximate amount of swap space that would be required
+ if the process were to dirty all writable pages and then
+ be swapped out.
diff --git a/smartt-top/debian/patches/ps_supgid_display.patch b/smartt-top/debian/patches/ps_supgid_display.patch
new file mode 100644
index 0000000..378daa1
--- /dev/null
+++ b/smartt-top/debian/patches/ps_supgid_display.patch
@@ -0,0 +1,446 @@
+Description: 506303 ps displays supplementary groups
+Author: Alfredo Esteban <aedelatorre@gmail.com>
+Bug-Debian: http://bugs.debian.org/506303
+Reviewed-by: Craig Small <csmall@debian.org>
+--- a/proc/library.map
++++ b/proc/library.map
+@@ -7,7 +7,7 @@
+
+ readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command;
+ escape_str; escape_strlist;
+- openproc; closeproc; freeproc;
++ openproc; closeproc; freeproc; allocsupgrp; freesupgrp;
+ tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
+ display_version; procps_version; linux_version_code;
+ Hertz; smp_num_cpus; have_privs;
+--- a/proc/readproc.c
++++ b/proc/readproc.c
+@@ -20,6 +20,7 @@
+ #include <errno.h>
+ #include <stdarg.h>
+ #include <string.h>
++#include <limits.h>
+ #include <unistd.h>
+ #include <signal.h>
+ #include <fcntl.h>
+@@ -85,63 +86,68 @@
+ long Threads = 0;
+ long Tgid = 0;
+ long Pid = 0;
++ int hash = 0;
++ int isupgid = 0;
+
+- static const unsigned char asso[] =
++ static const unsigned char asso[] =
+ {
+- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+- 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+- 61, 61, 61, 61, 61, 61, 61, 61, 15, 61,
+- 61, 61, 61, 61, 61, 61, 30, 3, 5, 5,
+- 61, 5, 61, 8, 61, 61, 3, 61, 10, 61,
+- 6, 61, 13, 0, 30, 25, 0, 61, 61, 61,
+- 61, 61, 61, 61, 61, 61, 61, 3, 61, 13,
+- 0, 0, 61, 30, 61, 25, 61, 61, 61, 0,
+- 61, 61, 61, 61, 5, 61, 0, 61, 61, 61,
+- 0, 61, 61, 61, 61, 61, 61, 61
++ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
++ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
++ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
++ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
++ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
++ 66, 66, 66, 66, 66, 66, 66, 66, 0, 66,
++ 66, 66, 66, 66, 66, 66, 3, 30, 20, 30,
++ 66, 25, 66, 20, 66, 66, 30, 66, 25, 66,
++ 0, 66, 8, 10, 3, 18, 5, 66, 66, 66,
++ 66, 66, 66, 66, 66, 66, 66, 3, 66, 10,
++ 0, 0, 66, 25, 66, 5, 66, 66, 66, 25,
++ 66, 5, 66, 66, 0, 66, 0, 0, 66, 66,
++ 25, 66, 66, 66, 66, 66, 66, 66
+ };
+
+ static const status_table_struct table[] = {
+- F(VmStk)
++ F(Pid)
+ NUL NUL
+- F(State)
++ F(Threads)
++ NUL
++ F(PPid)
++ NUL NUL
++ F(Tgid)
+ NUL
+- F(VmExe)
+ F(ShdPnd)
++ NUL NUL
++ F(State)
+ NUL
+- F(VmData)
++ F(VmStk)
++ NUL NUL
++ F(Uid)
+ NUL
+- F(Name)
++ F(VmSize)
+ NUL NUL
+ F(VmRSS)
+- NUL NUL
+- F(VmLck)
+- NUL NUL NUL
++ NUL
+ F(Gid)
+- F(Pid)
+- NUL NUL NUL
+- F(VmSize)
+ NUL NUL
+- F(VmLib)
+- NUL NUL
+- F(PPid)
+- NUL
+- F(SigCgt)
++ F(VmData)
+ NUL
+- F(Threads)
++ F(Groups)
++ NUL NUL NUL NUL
+ F(SigPnd)
++ NUL NUL
++ F(SigBlk)
+ NUL
++ F(VmLib)
++ NUL NUL NUL NUL
++ F(VmLck)
++ NUL NUL NUL NUL
++ F(Name)
++ NUL NUL NUL NUL
+ F(SigIgn)
+- NUL
+- F(Uid)
+- NUL NUL NUL NUL NUL NUL NUL NUL NUL
+- NUL NUL NUL NUL NUL
+- F(Tgid)
+ NUL NUL NUL NUL
+- F(SigBlk)
+- NUL NUL NUL
++ F(VmExe)
++ NUL NUL NUL NUL
++ F(SigCgt)
+ };
+
+ #undef F
+@@ -157,6 +163,9 @@
+ P->vm_exe = 0;
+ P->vm_lib = 0;
+ P->nlwp = 0;
++ P->nsupgid = 0;
++ P->supgid = NULL;
++ P->supgrp = NULL;
+ P->signal[0] = '\0'; // so we can detect it as missing for very old kernels
+
+ goto base;
+@@ -173,7 +182,9 @@
+ // examine a field name (hash and compare)
+ base:
+ if(unlikely(!*S)) break;
+- entry = table[63 & (asso[(int)S[3]] + asso[(int)S[2]] + asso[(int)S[0]])];
++ hash = asso[S[3]] + asso[S[2]] + asso[S[0]];
++ if (hash > 65) continue;
++ entry = table[hash];
+ colon = strchr(S, ':');
+ if(unlikely(!colon)) break;
+ if(unlikely(colon[1]!='\t')) break;
+@@ -271,6 +282,21 @@
+ P->sgid = strtol(S,&S,10);
+ P->fgid = strtol(S,&S,10);
+ continue;
++ case_Groups:
++ isupgid = 0;
++ if (*S != '\n'){ // Is there any supplementary group ?
++ P->supgid = (int *) xmalloc(0x0004 * sizeof(int));
++ int vctsize = 0x0004;
++ while (S[1] != '\n' && isupgid<INT_MAX){ // There is one blank before '\n'
++ if (isupgid == vctsize){
++ vctsize *= 2;
++ P->supgid = (int *)xrealloc(P->supgid,vctsize * sizeof(int));
++ }
++ P->supgid[isupgid++] = strtol(S,&S,10);
++ P->nsupgid++;
++ }
++ }
++ continue;
+ case_VmData:
+ P->vm_data = strtol(S,&S,10);
+ continue;
+@@ -589,6 +615,13 @@
+ }
+ }
+
++ if (flags & PROC_FILLSUPGRP && p->nsupgid > 0){
++ allocsupgrp(p);
++ int i;
++ for (i=0; i < p->nsupgid; i++)
++ memcpy(p->supgrp[i], group_from_gid(p->supgid[i]), P_G_SZ);
++ }
++
+ if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
+ p->cmdline = file2strvec(path, "cmdline");
+ else
+@@ -683,6 +716,13 @@
+ }
+ }
+
++ if (flags & PROC_FILLSUPGRP && t->nsupgid > 0){
++ allocsupgrp(t);
++ int i;
++ for (i=0; i < t->nsupgid; i++)
++ memcpy(t->supgrp[i], group_from_gid(t->supgid[i]), P_G_SZ);
++ }
++
+ #if 0
+ if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
+ t->cmdline = file2strvec(path, "cmdline");
+@@ -897,6 +937,23 @@
+ }
+ }
+
++// allocate memory for supgrp
++void allocsupgrp(proc_t *p) {
++ if (!p || p->nsupgid == 0) return;
++ p->supgrp = (char**)xmalloc(p->nsupgid * sizeof(char*));
++ int i;
++ for (i=0; i<p->nsupgid; i++)
++ p->supgrp[i] = (char*)xmalloc(P_G_SZ * sizeof(char));
++}
++
++// free memory allocated for supgrp
++void freesupgrp(proc_t *p) {
++ int i;
++ for (i=0; i<p->nsupgid; i++)
++ if (p->supgrp[i]) free(p->supgrp[i]);
++ free(p->supgrp);
++}
++
+ // deallocate the space allocated by readproc if the passed rbuf was NULL
+ void freeproc(proc_t* p) {
+ if (!p) /* in case p is NULL */
+--- a/proc/readproc.h
++++ b/proc/readproc.h
+@@ -122,6 +122,7 @@
+ egroup[P_G_SZ], // status effective group name
+ sgroup[P_G_SZ], // status saved group name
+ fgroup[P_G_SZ], // status filesystem group name
++ **supgrp, // status supplementary groups
+ cmd[16]; // stat,status basename of executable file in call to exec(2)
+ struct proc_t
+ *ring, // n/a thread group ring
+@@ -137,6 +138,8 @@
+ suid, sgid, // status saved
+ fuid, fgid, // status fs (used for file access only)
+ tpgid, // stat terminal process group id
++ nsupgid, // status number of supplementary groups
++ *supgid, // status supplementary gid's
+ exit_signal, // stat might not be SIGCHLD
+ processor; // stat current (or most recent?) CPU
+ char **cgroup; // cgroup current cgroup, looks like a classic filepath
+@@ -198,6 +201,12 @@
+ // clean-up open files, etc from the openproc()
+ extern void closeproc(PROCTAB* PT);
+
++// allocate memory for supgrp
++extern void allocsupgrp(proc_t *p);
++
++// free memory allocated for supgrp
++extern void freesupgrp(proc_t *p);
++
+ // retrieve the next process matching the criteria set by the openproc()
+ extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p);
+ extern proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t);
+@@ -238,6 +247,7 @@
+ #define PROC_FILLWCHAN 0x0080 // look up WCHAN name
+ #define PROC_FILLARG 0x0100 // alloc and fill in `cmdline'
+ #define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup`
++#define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id number -> group name
+
+ #define PROC_LOOSE_TASKS 0x2000 // threat threads as if they were processes
+
+--- a/ps/display.c
++++ b/ps/display.c
+@@ -342,6 +342,8 @@
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
++ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
++ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ case TF_show_proc|TF_loose_tasks: // H option
+@@ -349,12 +351,16 @@
+ proc_t buf2;
+ // must still have the process allocated
+ while(readtask(ptp,&buf,&buf2)){
+- if(!want_this_proc(&buf)) continue;
+- show_one_proc(&buf2, task_format_list);
++ if(want_this_proc(&buf)) show_one_proc(&buf2, task_format_list);
++ if(buf2.nsupgid > 0 && buf2.supgid && buf.supgid!=buf2.supgid) free(buf2.supgid);
++ if((ptp->flags & PROC_FILLSUPGRP) && buf2.nsupgid>0 && buf2.supgrp && buf.supgrp!=buf2.supgrp)
++ freesupgrp(&buf2);
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
++ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
++ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ case TF_show_proc|TF_show_task: // m and -m options
+@@ -363,11 +369,18 @@
+ proc_t buf2;
+ show_one_proc(&buf, proc_format_list);
+ // must still have the process allocated
+- while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
++ while(readtask(ptp,&buf,&buf2)){
++ show_one_proc(&buf2, task_format_list);
++ if(buf2.nsupgid > 0 && buf2.supgid && buf.supgid!=buf2.supgid) free(buf2.supgid);
++ if(ptp->flags & PROC_FILLSUPGRP && buf2.nsupgid>0 && buf2.supgrp && buf.supgrp!=buf2.supgrp)
++ freesupgrp(&buf2);
++ }
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
++ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
++ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ case TF_show_task: // -L and -T options
+@@ -375,11 +388,18 @@
+ if(want_this_proc(&buf)){
+ proc_t buf2;
+ // must still have the process allocated
+- while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
++ while(readtask(ptp,&buf,&buf2)){
++ show_one_proc(&buf2, task_format_list);
++ if(buf2.nsupgid > 0 && buf2.supgid && buf.supgid!=buf2.supgid) free(buf2.supgid);
++ if(ptp->flags & PROC_FILLSUPGRP && buf2.nsupgid>0 && buf2.supgrp && buf.supgrp!=buf2.supgrp)
++ freesupgrp(&buf2);
++ }
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
++ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
++ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ }
+@@ -542,6 +562,12 @@
+ qsort(processes, n, sizeof(proc_t*), compare_two_procs);
+ if(forest_type) show_forest(n);
+ else show_proc_array(ptp,n);
++ int i;
++ for (i=0; i<n; i++)
++ if (processes[i]->nsupgid>0 && processes[i]->supgid) free(processes[i]->supgid);
++ if (ptp->flags & PROC_FILLSUPGRP)
++ for (i=0; i<n; i++)
++ if (processes[i]->nsupgid>0 && processes[i]->supgrp) freesupgrp(processes[i]);
+ closeproc(ptp);
+ }
+
+--- a/ps/output.c
++++ b/ps/output.c
+@@ -211,6 +211,32 @@
+ return 0;
+ }
+
++static int sr_supgid(const proc_t* P, const proc_t* Q){
++ int i;
++ for (i = 0; i < INT_MAX; i++){
++ if (P->nsupgid == i){
++ if (Q->nsupgid == i) return 0;
++ else return -1;
++ }
++ if (Q->nsupgid == i) return 1;
++ if (P->supgid[i] != Q->supgid[i]) return P->supgid[i] - Q->supgid[i];
++ }
++ return 0;
++}
++
++static int sr_supgrp(const proc_t* P, const proc_t* Q){
++ int i;
++ for (i = 0; i < INT_MAX; i++){
++ if (P->nsupgid == i){
++ if (Q->nsupgid == i) return 0;
++ else return -1;
++ }
++ if (Q->nsupgid == i) return 1;
++ int cmp = strncmp(P->supgrp[i],Q->supgrp[i],P_G_SZ);
++ if (cmp != 0) return cmp;
++ }
++ return 0;
++}
+
+ /***************************************************************************/
+ /************ Lots of format functions, starting with the NOP **************/
+@@ -1062,6 +1088,24 @@
+ return snprintf(outbuf, COLWID, "%d", pp->fuid);
+ }
+
++static int pr_supgid(char *restrict const outbuf, const proc_t *restrict const pp){
++ if (pp->nsupgid == 0) return snprintf(outbuf,2,"-");
++ int rest = COLWID;
++ int i = 0;
++ for (i = 0; i < pp->nsupgid && rest > 5; i++)
++ rest-= snprintf(outbuf+COLWID-rest, rest, "%d ", pp->supgid[i]);
++ return COLWID-rest;
++}
++
++static int pr_supgrp(char *restrict const outbuf, const proc_t *restrict const pp){
++ if (pp->nsupgid == 0) return snprintf(outbuf,2,"-");
++ int rest = COLWID;
++ int i = 0;
++ for (i = 0; i < pp->nsupgid && rest > sizeof( pp->supgrp[i] ) + 1; i++)
++ rest-= snprintf(outbuf+COLWID-rest, rest, "%s ", pp->supgrp[i]);
++ return COLWID-rest;
++}
++
+ // The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
+ // requires that user and group names print as decimal numbers if there is
+ // not enough room in the column, so tough luck if you don't like it.
+@@ -1293,6 +1337,7 @@
+ #define USR PROC_FILLUSR /* uid_t -> user names */
+ #define GRP PROC_FILLGRP /* gid_t -> group names */
+ #define WCH PROC_FILLWCHAN /* do WCHAN lookup */
++#define SUPGRP PROC_FILLSUPGRP /* supgid -> supplementary group names */
+
+ #define CGRP PROC_FILLCGROUP /* read cgroup */
+ /* TODO
+@@ -1490,6 +1535,8 @@
+ {"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+ {"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */
+ {"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT},
++{"supgid", "SUPGID", pr_supgid, sr_supgid, 27, 0, LNX, PO|UNLIMITED},
++{"supgrp", "SUPGRP", pr_supgrp, sr_supgrp, 27, SUPGRP, LNX, PO|UNLIMITED},
+ {"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER},
+ {"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT},
+ {"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
+--- a/ps/ps.1
++++ b/ps/ps.1
+@@ -1303,6 +1303,16 @@
+ saved user\ ID. (alias\ \fBsvuid\fR).
+ T}
+
++supgid SUPGID T{
++gid of supplementary groups, see
++.BR getgroups (2).
++T}
++
++supgrp SUPGRP T{
++names of supplementary groups, see
++.BR getgroups (2).
++T}
++
+ suser SUSER T{
+ saved user name. This will be the textual user\ ID,
+ if\ it can be obtained and the field width permits,
diff --git a/smartt-top/debian/patches/readproc_c.patch b/smartt-top/debian/patches/readproc_c.patch
new file mode 100644
index 0000000..7b44420
--- /dev/null
+++ b/smartt-top/debian/patches/readproc_c.patch
@@ -0,0 +1,25 @@
+Author: Craig Small <csmall@debian.org>
+Description: readproc.c some type conversion help
+return if unable to open /proc
+Index: b/proc/readproc.c
+===================================================================
+--- a/proc/readproc.c 2009-11-24 20:53:03.000000000 +1100
++++ b/proc/readproc.c 2009-11-24 21:00:41.000000000 +1100
+@@ -173,7 +173,7 @@
+ // examine a field name (hash and compare)
+ base:
+ if(unlikely(!*S)) break;
+- entry = table[63 & (asso[S[3]] + asso[S[2]] + asso[S[0]])];
++ entry = table[63 & (asso[(int)S[3]] + asso[(int)S[2]] + asso[(int)S[0]])];
+ colon = strchr(S, ':');
+ if(unlikely(!colon)) break;
+ if(unlikely(colon[1]!='\t')) break;
+@@ -942,6 +942,8 @@
+ else
+ PT = openproc(flags);
+ va_end(ap);
++ if (!PT)
++ return 0;
+ do { /* read table: */
+ tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
+ tab[n] = readproc_direct(PT, NULL); /* final null to terminate */
diff --git a/smartt-top/debian/patches/readproc_double_free.patch b/smartt-top/debian/patches/readproc_double_free.patch
new file mode 100644
index 0000000..669cb64
--- /dev/null
+++ b/smartt-top/debian/patches/readproc_double_free.patch
@@ -0,0 +1,21 @@
+Description: zeros out reused structures to stop double free problems
+Bug-Debian: http://bugs.debian.org/550009
+Author: Arnaud Giersch <arnaud.giersch@iut-bm.univ-fcomte.fr>
+--- a/proc/readproc.c
++++ b/proc/readproc.c
+@@ -828,6 +828,7 @@
+
+ saved_p = p;
+ if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
++ else memset(p, 0, sizeof *p);
+
+ for(;;){
+ // fills in the path, plus p->tid and p->tgid
+@@ -856,6 +857,7 @@
+
+ saved_t = t;
+ if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */
++ else memset(t, 0, sizeof *t);
+
+ // 1. got to fake a thread for old kernels
+ // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!)
diff --git a/smartt-top/debian/patches/series b/smartt-top/debian/patches/series
new file mode 100644
index 0000000..26c3f61
--- /dev/null
+++ b/smartt-top/debian/patches/series
@@ -0,0 +1,68 @@
+slabtop_once.patch
+free.1.patch
+output_sort_time.patch
+pmap.1.patch
+10_ps.1.patch
+skill.1.patch
+skill_perror.patch
+slabtop_1.patch
+sysctl.8.patch
+sysctl_options.patch
+tload.1.patch
+top_irix.patch
+top_no_openproc.patch
+top_stdin_eof.patch
+top_uid_length.patch
+uptime.1.patch
+vmstat.8.patch
+watch.1.patch
+w_time.patch
+kill.1.patch
+kill_warncr.patch
+module_mk_shared.patch
+sysinfo_elfnote.patch
+top.1.patch
+top_c_resize.patch
+w-bassman.patch
+top.1_cpustates.patch
+watch_8bitchar.patch
+library_map_freeproc.patch
+pgrep_start_time.patch
+readproc_c.patch
+sysinfo_7_numbers.patch
+tload_no_optargs.patch
+w.1.patch
+w_columns.patch
+watch_exec_beep.patch
+path_max.patch
+w_envlength.patch
+gnu-kbsd-version.patch
+pgrep_c_option.patch
+ps_cgroup_display.patch
+watch_precision_time.patch
+watch_unicode.patch
+complain_unmounted_proc.patch
+ps_supgid_display.patch
+makefile_dev_null.patch
+pgrep.1.patch
+ps_size_sz.patch
+top_mintime.patch
+vmstat_headers.patch
+pgrep_usage_exitcode.patch
+ps_1_options.patch
+top_highlight.patch
+top_nohz.patch
+vmstat_part_format.patch
+readproc_double_free.patch
+vmstat_units.patch
+skill_null_argv.patch
+skill_multiarg.patch
+proc_version_constructor.patch
+pmaps_smaps.patch
+top_numeric_args.patch
+top_1_swap.patch
+w_userproc.patch
+sysinfo_kfreebsd_hertz.patch
+ps_1_flt_output.patch
+watch_ansi_colour.patch
+top_username_parse.patch
diff --git a/smartt-top/debian/patches/skill.1.patch b/smartt-top/debian/patches/skill.1.patch
new file mode 100644
index 0000000..a327800
--- /dev/null
+++ b/smartt-top/debian/patches/skill.1.patch
@@ -0,0 +1,116 @@
+Description: Cleanup man page #282168
+Bug-Debian: http://bugs.debian.org/282168
+Author: Brendan O'Dea <bod@debian.org>
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/skill.1
+===================================================================
+--- a/skill.1 2009-11-24 20:53:05.000000000 +1100
++++ b/skill.1 2009-11-24 21:00:31.000000000 +1100
+@@ -11,37 +11,38 @@
+ skill, snice \- send a signal or report process status
+
+ .SH SYNOPSIS
+-.nf
+-skill [signal to send] [options] process selection criteria
+-snice [new priority] [options] process selection criteria
+-.fi
++.B skill
++.RI [ "signal to send" ]
++.RI [ options ]
++.I process selection criteria
++.br
++.B snice
++.RI [ "new priority" ]
++.RI [ options ]
++.I process selection criteria
+
+ .SH DESCRIPTION
+ These tools are probably obsolete and unportable. The command
+ syntax is poorly defined. Consider using the killall, pkill,
+ and pgrep commands instead.
+
+-The default signal for skill is TERM. Use -l or -L to list available signals.
++The default signal for skill is TERM. Use \-l or \-L to list available signals.
+ Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+-Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
++Alternate signals may be specified in three ways: \-9 \-SIGKILL \-KILL.
+
+ The default priority for snice is +4. (snice +4 ...)
+-Priority numbers range from +20 (slowest) to -20 (fastest).
++Priority numbers range from +20 (slowest) to \-20 (fastest).
+ Negative priority numbers are restricted to administrative users.
+
+ .SH "GENERAL OPTIONS"
+ .TS
+-l l l.
+--f fast mode This is not currently useful.
+--i interactive use T{
+-You will be asked to approve each action.
+-T}
+--v verbose output T{
+-Display information about selected processes.
+-T}
+--w warnings enabled This is not currently useful.
+--n no action This only displays the process ID.
+--V show version Displays version of program.
++lB l l.
++\-f fast mode This is not currently useful.
++\-i interactive use You will be asked to approve each action.
++\-v verbose output Display information about selected processes.
++\-w warnings enabled This is not currently useful.
++\-n no action This only displays the process ID.
++\-V show version Displays version of program.
+ .TE
+
+ .SH "PROCESS SELECTION OPTIONS"
+@@ -49,11 +50,11 @@
+ The options below may be used to ensure correct interpretation.
+ Do not blame Albert for this interesting interface.
+ .TS
+-l l.
+--t The next argument is a terminal (tty or pty).
+--u The next argument is a username.
+--p The next argument is a process ID number.
+--c The next argument is a command name.
++lB l.
++\-t The next argument is a terminal (tty or pty).
++\-u The next argument is a username.
++\-p The next argument is a process ID number.
++\-c The next argument is a command name.
+ .TE
+
+ .SH SIGNALS
+@@ -63,7 +64,6 @@
+ lB rB lB lB
+ lfCW r l l.
+ Name Num Action Description
+-.TH
+ 0 0 n/a exit code indicates if a signal may be sent
+ ALRM 14 exit
+ HUP 1 exit
+@@ -104,15 +104,20 @@
+ lB lB
+ lfCW l.
+ Command Description
+-.TC
+ snice seti crack +7 Slow down seti and crack
+-skill -KILL -v /dev/pts/* Kill users on new-style PTY devices
+-skill -STOP viro lm davem Stop 3 users
+-snice -17 root bash Give priority to root's shell
++skill \-KILL \-v /dev/pts/* Kill users on new-style PTY devices
++skill \-STOP viro lm davem Stop 3 users
++snice \-17 root bash Give priority to root's shell
+ .TE
+
+ .SH "SEE ALSO"
+-killall(1) pkill(1) kill(1) renice(1) nice(1) signal(7) kill(2)
++.BR killall (1),
++.BR pkill (1),
++.BR kill (1),
++.BR renice (1),
++.BR nice(1),
++.BR kill(2),
++.BR signal(7)
+
+ .SH STANDARDS
+ No standards apply.
diff --git a/smartt-top/debian/patches/skill_multiarg.patch b/smartt-top/debian/patches/skill_multiarg.patch
new file mode 100644
index 0000000..83b6e74
--- /dev/null
+++ b/smartt-top/debian/patches/skill_multiarg.patch
@@ -0,0 +1,18 @@
+Description: Fixed -v and -i and other flags in skill/snice
+ There was a too greedy option parser
+ Patch based on submitted patch by Yoshio Nakamura
+Bug-Debian: http://bugs.debian.org/331419
+Bug-Debian: http://bugs.debian.org/569030
+Author: Craig Small <csmall@debian.org>
+
+--- a/skill.c
++++ b/skill.c
+@@ -464,6 +464,8 @@
+ switch(force){ /* fall through each data type */
+ default: skillsnice_usage();
+ case 0: /* not forced */
++ if (argptr && argptr[0] == '-') /* its the next argument not a parameter */
++ continue;
+ case 't':
+ if(argc){
+ struct stat sbuf;
diff --git a/smartt-top/debian/patches/skill_null_argv.patch b/smartt-top/debian/patches/skill_null_argv.patch
new file mode 100644
index 0000000..cafbd00
--- /dev/null
+++ b/smartt-top/debian/patches/skill_null_argv.patch
@@ -0,0 +1,14 @@
+Author: WANG Yunfeng <uhuruh@gmail.com>
+Description: Don't treat skill null parameter as 0
+Bug-Debian: http://bugs.debian.org/551173
+--- a/skill.c
++++ b/skill.c
+@@ -307,7 +307,7 @@
+ long pid;
+ char *endp;
+ pid = strtol(argv[argc],&endp,10);
+- if(!*endp){
++ if(!*endp && (endp != argv[argc])){
+ if(!kill((pid_t)pid,signo)) continue;
+ // The UNIX standard contradicts itself. If at least one process
+ // is matched for each PID (as if processes could share PID!) and
diff --git a/smartt-top/debian/patches/skill_perror.patch b/smartt-top/debian/patches/skill_perror.patch
new file mode 100644
index 0000000..e9b64d0
--- /dev/null
+++ b/smartt-top/debian/patches/skill_perror.patch
@@ -0,0 +1,16 @@
+Description: kill prints perror
+Author: Justin Pryzby <justinpryzby@users.sourceforge.net>
+Bug-Debian: http://bugs.debian.org/468363
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/skill.c
+===================================================================
+--- a/skill.c 2009-11-24 20:53:05.000000000 +1100
++++ b/skill.c 2009-11-24 21:00:31.000000000 +1100
+@@ -317,6 +317,7 @@
+ // The standard says we return non-zero if an error occurs. Thus if
+ // killing two processes gives 0 for one and EPERM for the other,
+ // we are required to return both zero and non-zero. Quantum kill???
++ perror("kill");
+ exitvalue = 1;
+ continue;
+ }
diff --git a/smartt-top/debian/patches/slabtop_1.patch b/smartt-top/debian/patches/slabtop_1.patch
new file mode 100644
index 0000000..24b7764
--- /dev/null
+++ b/smartt-top/debian/patches/slabtop_1.patch
@@ -0,0 +1,97 @@
+Description: Cleanup slabtop man page
+Author: Brendan O'Dea <bod@debian.org>
+Bug-Debian: http://bugs.debian.org/282168
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/slabtop.1
+===================================================================
+--- a/slabtop.1 2009-11-24 20:53:05.000000000 +1100
++++ b/slabtop.1 2009-11-24 21:00:32.000000000 +1100
+@@ -7,36 +7,39 @@
+ slabtop \- display kernel slab cache information in real time
+
+ .SH SYNOPSIS
+-.BI "slabtop [ " options " ] "
++.B slabtop
++.RI [ options ]
+
+ .SH DESCRIPTION
+-.BR slabtop (1)
++.B slabtop
+ displays detailed kernel slab cache information in real time. It displays a
+ listing of the top caches sorted by one of the listed sort criteria. It also
+ displays a statistics header filled with slab layer information.
+
+ .SH OPTIONS
+ Normal invocation of
+-.BR slabtop (1)
++.B slabtop
+ does not require any options. The behavior, however, can be fine-tuned by
+ specifying one or more of the following flags:
+ .TP
+-.B \-\^\-delay=n, \-d n
+-Refresh the display every n seconds. By default,
+-.BR slabtop (1)
++.B \-\-delay=\fIn\fR, \fB\-d \fIn
++Refresh the display every
++.I n
++in seconds. By default,
++.B slabtop
+ refreshes the display every three seconds. To exit the program, hit
+ .BR q.
+ .TP
+-.B \-\^\-sort=S, \-s S
+-Sort by S, where S is one of the sort criteria.
++.B \-\-sort=\fIS\fR, \fB\-s\fR \fIS
++Sort by \fIS\fR, where \fIS\fR is one of the sort criteria.
+ .TP
+-.B \-\^\-once, \-o
++.B \-\-once\fR, \fB\-o
+ Display the output once and then exit.
+ .TP
+-.B \-\^\-version, \-V
++.B \-\-version\fR, \fB\-V
+ Display version information and exit.
+ .TP
+-.B \-\^\-help
++.B \-\-help
+ Display usage information and exit.
+
+ .SH SORT CRITERIA
+@@ -78,7 +81,7 @@
+ sort by cache utilization
+
+ .SH COMMANDS
+-.BR slabtop (1)
++.B slabtop
+ accepts keyboard commands from the user during use. The following are
+ supported. In the case of letters, both cases are accepted.
+
+@@ -94,7 +97,9 @@
+ Quit the program.
+
+ .SH FILES
+-.IR /proc/slabinfo " \-\- slab information"
++.TP
++.I /proc/slabinfo
++slab information
+
+ .SH "SEE ALSO"
+ .BR free (1),
+@@ -104,7 +109,7 @@
+
+ .SH NOTES
+ Currently,
+-.BR slabtop (1)
++.B slabtop
+ requires a 2.4 or later kernel (specifically, a version 1.1 or later
+ .IR /proc/slabinfo ).
+ Kernel 2.2 should be supported in the future.
+@@ -116,7 +121,7 @@
+ .SH AUTHORS
+ Written by Chris Rivera and Robert Love.
+
+-.BR slabtop (1)
++.B slabtop
+ was inspired by Martin Bligh's perl script,
+ .BR vmtop .
+ The procps package is maintained by Albert Cahalan <albert@users.sf.net>.
diff --git a/smartt-top/debian/patches/slabtop_once.patch b/smartt-top/debian/patches/slabtop_once.patch
new file mode 100644
index 0000000..3c212bd
--- /dev/null
+++ b/smartt-top/debian/patches/slabtop_once.patch
@@ -0,0 +1,116 @@
+Description: Don't use ncurses for running once
+ Using ncurses initscr/endwin clears the screen for xterm/etc it
+ now prints raw text using printf
+Bug-Debian: http://bugs.debian.org/503089
+Author: Craig Small <csmall@debian.org>
+--- a/slabtop.c
++++ b/slabtop.c
+@@ -268,11 +268,13 @@
+ }
+ }
+
++#define print_line(fmt, args...) if (run_once) printf(fmt, ## args); else printw(fmt, ## args)
+ int main(int argc, char *argv[])
+ {
+ int o;
+ unsigned short old_rows;
+ struct slab_info *slab_list = NULL;
++ int run_once=0;
+
+ struct option longopts[] = {
+ { "delay", 1, NULL, 'd' },
+@@ -306,6 +308,7 @@
+ sort_func = set_sort_func(optarg[0]);
+ break;
+ case 'o':
++ run_once=1;
+ delay = 0;
+ break;
+ case 'V':
+@@ -322,11 +325,13 @@
+ if (tcgetattr(0, &saved_tty) == -1)
+ perror("tcgetattr");
+
+- initscr();
+- term_size(0);
+ old_rows = rows;
+- resizeterm(rows, cols);
+- signal(SIGWINCH, term_size);
++ term_size(0);
++ if (!run_once) {
++ initscr();
++ resizeterm(rows, cols);
++ signal(SIGWINCH, term_size);
++ }
+ signal(SIGINT, sigint_handler);
+
+ do {
+@@ -340,13 +345,13 @@
+ if (get_slabinfo(&slab_list, &stats))
+ break;
+
+- if (old_rows != rows) {
++ if (!run_once && old_rows != rows) {
+ resizeterm(rows, cols);
+ old_rows = rows;
+ }
+
+ move(0,0);
+- printw( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n"
++ print_line( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n"
+@@ -361,14 +366,14 @@
+ slab_list = slabsort(slab_list);
+
+ attron(A_REVERSE);
+- printw( "%6s %6s %4s %8s %6s %8s %10s %-23s\n",
++ print_line( "%6s %6s %4s %8s %6s %8s %10s %-23s\n",
+ "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS",
+ "OBJ/SLAB", "CACHE SIZE", "NAME");
+ attroff(A_REVERSE);
+
+ curr = slab_list;
+ for (i = 0; i < rows - 8 && curr->next; i++) {
+- printw("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
++ print_line("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
+ curr->nr_objs, curr->nr_active_objs, curr->use,
+ curr->obj_size / 1024.0, curr->nr_slabs,
+ curr->objs_per_slab, (unsigned)(curr->cache_size / 1024),
+@@ -376,22 +381,24 @@
+ curr = curr->next;
+ }
+
+- refresh();
+ put_slabinfo(slab_list);
+
+- FD_ZERO(&readfds);
+- FD_SET(0, &readfds);
+- tv.tv_sec = delay;
+- tv.tv_usec = 0;
+- if (select(1, &readfds, NULL, NULL, &tv) > 0) {
+- if (read(0, &c, 1) != 1)
+- break;
+- parse_input(c);
+- }
++ if (!run_once) {
++ refresh();
++ FD_ZERO(&readfds);
++ FD_SET(0, &readfds);
++ tv.tv_sec = delay;
++ tv.tv_usec = 0;
++ if (select(1, &readfds, NULL, NULL, &tv) > 0) {
++ if (read(0, &c, 1) != 1)
++ break;
++ parse_input(c);
++ }
++ }
+ } while (delay);
+
+ tcsetattr(0, TCSAFLUSH, &saved_tty);
+ free_slabinfo(slab_list);
+- endwin();
++ if (!run_once) endwin();
+ return 0;
+ }
diff --git a/smartt-top/debian/patches/sysctl.8.patch b/smartt-top/debian/patches/sysctl.8.patch
new file mode 100644
index 0000000..394cb4d
--- /dev/null
+++ b/smartt-top/debian/patches/sysctl.8.patch
@@ -0,0 +1,147 @@
+Author: <csmall@debian.org>
+Description: -p flag has OPTIONAL filename
+cleanup sysctl man page
+Bug-Debian: http://bugs.debian.org/297144
+Bug-Debian: http://bugs.debian.org/282168
+Index: b/sysctl.8
+===================================================================
+--- a/sysctl.8 2009-11-24 20:53:05.000000000 +1100
++++ b/sysctl.8 2009-11-24 21:00:32.000000000 +1100
+@@ -10,76 +10,108 @@
+ .SH NAME
+ sysctl \- configure kernel parameters at runtime
+ .SH SYNOPSIS
+-.B "sysctl [-n] [-e] variable ..."
++.B sysctl
++.RB [ \-n ]
++.RB [ \-e ]
++.I variable
++\&...
+ .br
+-.B "sysctl [-n] [-e] [-q] -w variable=value ..."
++.B sysctl
++.RB [ \-n ]
++.RB [ \-e ]
++.RB [ \-q ]
++.B \-w
++.IR variable = value
++\&...
+ .br
+-.B "sysctl [-n] [-e] [-q] -p <filename>"
++.B sysctl
++.RB [ \-n ]
++.RB [ \-e ]
++.RB [ \-q ]
++.B \-p
++.RI [ filename ]
+ .br
+-.B "sysctl [-n] [-e] -a"
++.B sysctl
++.RB [ \-n ]
++.RB [ \-e ]
++.B \-a
+ .br
+-.B "sysctl [-n] [-e] -A"
++.B sysctl
++.RB [ \-n ]
++.RB [ \-e ]
++.B \-A
+ .SH DESCRIPTION
+ .B sysctl
+ is used to modify kernel parameters at runtime. The parameters available
+ are those listed under /proc/sys/. Procfs is required for
+-.B sysctl(8)
++.B sysctl
+ support in Linux. You can use
+-.B sysctl(8)
++.B sysctl
+ to both read and write sysctl data.
+ .SH PARAMETERS
+ .TP
+-.B "variable"
++.I variable
+ The name of a key to read from. An example is kernel.ostype. The '/'
+ separator is also accepted in place of a '.'.
+ .TP
+-.B "variable=value"
+-To set a key, use the form variable=value, where variable is the key and
+-value is the value to set it to. If the value contains quotes or characters
++.IR variable = value
++To set a key, use the form
++.IR variable = value
++where
++.I variable
++is the key and
++.I value
++is the value to set it to. If the value contains quotes or characters
+ which are parsed by the shell, you may need to enclose the value in double
+-quotes. This requires the -w parameter to use.
++quotes. This requires the
++.B \-w
++parameter to use.
+ .TP
+-.B "-n"
++.B \-n
+ Use this option to disable printing of the key name when printing values.
+ .TP
+-.B "-e"
++.B \-e
+ Use this option to ignore errors about unknown keys.
+ .TP
+-.B "-N"
++.B \-N
+ Use this option to only print the names. It may be useful with shells that
+ have programmable completion.
+ .TP
+-.B "-q"
++.B \-q
+ Use this option to not display the values set to stdout.
+ .TP
+-.B "-w"
++.B \-w
+ Use this option when you want to change a sysctl setting.
+ .TP
+-.B "-p"
++.B \-p
+ Load in sysctl settings from the file specified or /etc/sysctl.conf if none given.
+ Specifying \- as filename means reading data from standard input.
+ .TP
+-.B "-a"
++.B \-a
+ Display all values currently available.
+ .TP
+-.B "-A"
++.B \-A
+ Display all values currently available in table form.
+ .SH EXAMPLES
+-.TP
+-/sbin/sysctl -a
+-.TP
+-/sbin/sysctl -n kernel.hostname
+-.TP
+-/sbin/sysctl -w kernel.domainname="example.com"
+-.TP
+-/sbin/sysctl -p /etc/sysctl.conf
++/sbin/sysctl \-a
++.br
++/sbin/sysctl \-n kernel.hostname
++.br
++/sbin/sysctl \-w kernel.domainname="example.com"
++.br
++/sbin/sysctl \-p /etc/sysctl.conf
+ .SH FILES
+ .I /proc/sys
++.br
+ .I /etc/sysctl.conf
+ .SH SEE ALSO
+ .BR sysctl.conf (5)
+ .SH BUGS
+-The -A parameter behaves just as -a does.
++The
++.B \-A
++parameter behaves just as
++.B \-a
++does.
+ .SH AUTHOR
+ George Staikos, <staikos@0wned.org>
+
diff --git a/smartt-top/debian/patches/sysctl_options.patch b/smartt-top/debian/patches/sysctl_options.patch
new file mode 100644
index 0000000..55bbaf4
--- /dev/null
+++ b/smartt-top/debian/patches/sysctl_options.patch
@@ -0,0 +1,46 @@
+Author: <csmall@debian.org>
+Description: Fix up some option processing
+Index: b/sysctl.c
+===================================================================
+--- a/sysctl.c 2009-11-24 20:53:05.000000000 +1100
++++ b/sysctl.c 2009-11-24 21:00:33.000000000 +1100
+@@ -421,6 +421,7 @@
+ const char *me = (const char *)basename(argv[0]);
+ bool SwitchesAllowed = true;
+ bool WriteMode = false;
++ bool DisplayAllOpt = false;
+ int ReturnCode = 0;
+ const char *preloadfile = DEFAULT_PRELOAD;
+
+@@ -486,8 +487,8 @@
+ case 'a': // string and integer values (for Linux, all of them)
+ case 'A': // same as -a -o
+ case 'X': // same as -a -x
+- SwitchesAllowed = false;
+- return DisplayAll(PROC_PATH);
++ DisplayAllOpt = true;
++ break;
+ case 'V':
+ fprintf(stdout, "sysctl (%s)\n",procps_version);
+ exit(0);
+@@ -502,6 +503,8 @@
+ } else {
+ if (NameOnly && Quiet) // nonsense
+ return Usage(me);
++ if (DisplayAllOpt) // We cannot have values with -a
++ return Usage(me);
+ SwitchesAllowed = false;
+ if (WriteMode || index(*argv, '='))
+ ReturnCode = WriteSetting(*argv);
+@@ -509,6 +512,11 @@
+ ReturnCode = ReadSetting(*argv);
+ }
+ }
++ if (DisplayAllOpt) {
++ if (Quiet)
++ return Usage(me);
++ return DisplayAll(PROC_PATH);
++ }
+
+ return ReturnCode;
+ }
diff --git a/smartt-top/debian/patches/sysinfo_7_numbers.patch b/smartt-top/debian/patches/sysinfo_7_numbers.patch
new file mode 100644
index 0000000..a105197
--- /dev/null
+++ b/smartt-top/debian/patches/sysinfo_7_numbers.patch
@@ -0,0 +1,36 @@
+Author: Craig Small <csmall@debian.org>
+Description: Adds up 7 cpu numbers not 4
+Bug-Debian: http://bugs.debian.org/460331
+Index: b/proc/sysinfo.c
+===================================================================
+--- a/proc/sysinfo.c 2009-11-24 21:00:38.000000000 +1100
++++ b/proc/sysinfo.c 2009-11-24 21:00:42.000000000 +1100
+@@ -124,24 +124,25 @@
+ unsigned long long Hertz;
+
+ static void old_Hertz_hack(void){
+- unsigned long long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */
++ unsigned long long user_j, nice_j, sys_j, other_j, wait_j, hirq_j, sirq_j, stol_j; /* jiffies (clock ticks) */
+ double up_1, up_2, seconds;
+ unsigned long long jiffies;
+ unsigned h;
+ char *restrict savelocale;
+
++ wait_j = hirq_j = sirq_j = stol_j = 0;
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ do{
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_1);
+ /* uptime(&up_1, NULL); */
+ FILE_TO_BUF(STAT_FILE,stat_fd);
+- sscanf(buf, "cpu %Lu %Lu %Lu %Lu", &user_j, &nice_j, &sys_j, &other_j);
++ sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &user_j, &nice_j, &sys_j, &other_j, &wait_j, &hirq_j, &sirq_j, &stol_j);
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_2);
+ /* uptime(&up_2, NULL); */
+ } while((long long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
+ setlocale(LC_NUMERIC, savelocale);
+- jiffies = user_j + nice_j + sys_j + other_j;
++ jiffies = user_j + nice_j + sys_j + other_j + wait_j + hirq_j + sirq_j + stol_j ;
+ seconds = (up_1 + up_2) / 2;
+ h = (unsigned)( (double)jiffies/seconds/smp_num_cpus );
+ /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
diff --git a/smartt-top/debian/patches/sysinfo_elfnote.patch b/smartt-top/debian/patches/sysinfo_elfnote.patch
new file mode 100644
index 0000000..1e4a199
--- /dev/null
+++ b/smartt-top/debian/patches/sysinfo_elfnote.patch
@@ -0,0 +1,23 @@
+Description: Find AT_CLKTCK elf note only on Linux
+Author: Petr Salinger <Petr.Salinger@seznam.cz>
+Bug-Debian: http://bugs.debian.org/378157
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/proc/sysinfo.c
+===================================================================
+--- a/proc/sysinfo.c 2009-11-24 20:53:04.000000000 +1100
++++ b/proc/sysinfo.c 2009-11-24 21:00:38.000000000 +1100
+@@ -221,12 +221,13 @@
+ // _SC_NPROCESSORS_ONLN returns 1, which should work OK
+ smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if(smp_num_cpus<1) smp_num_cpus=1; /* SPARC glibc is buggy */
+-
++#ifdef __linux__
+ if(linux_version_code > LINUX_VERSION(2, 4, 0)){
+ Hertz = find_elf_note(AT_CLKTCK);
+ if(Hertz!=NOTE_NOT_FOUND) return;
+ fputs("2.4+ kernel w/o ELF notes? -- report this\n", stderr);
+ }
++#endif
+ old_Hertz_hack();
+ }
+
diff --git a/smartt-top/debian/patches/sysinfo_kfreebsd_hertz.patch b/smartt-top/debian/patches/sysinfo_kfreebsd_hertz.patch
new file mode 100644
index 0000000..c6bc7a4
--- /dev/null
+++ b/smartt-top/debian/patches/sysinfo_kfreebsd_hertz.patch
@@ -0,0 +1,20 @@
+Description: Fix Hertz calculation for kfreebsd
+Bug-Debian: http://bugs.debian.org/460331
+Author: Petr Salinger
+--- a/proc/sysinfo.c
++++ b/proc/sysinfo.c
+@@ -230,6 +230,14 @@
+ fputs("2.4+ kernel w/o ELF notes? -- report this\n", stderr);
+ }
+ #endif
++#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
++ /* On FreeBSD the Hertz hack is unrelaible, there is no ELF note and
++ * Hertz isn't defined in asm/params.h
++ * See Debian Bug #460331
++ */
++ Hertz = 100;
++ return;
++#endif
+ old_Hertz_hack();
+ }
+
diff --git a/smartt-top/debian/patches/tload.1.patch b/smartt-top/debian/patches/tload.1.patch
new file mode 100644
index 0000000..896876f
--- /dev/null
+++ b/smartt-top/debian/patches/tload.1.patch
@@ -0,0 +1,15 @@
+Author: <csmall@debian.org>
+Description: Reword tload.1 man page for the alarm
+Index: b/tload.1
+===================================================================
+--- a/tload.1 2009-11-24 20:53:05.000000000 +1100
++++ b/tload.1 2009-11-24 21:00:33.000000000 +1100
+@@ -39,7 +39,7 @@
+ .BI "\-d" " delay"
+ option sets the time argument for an
+ .BR alarm (2);
+-if -d 0 is specified, the alarm is set to 0, which will never send the
++if \-d 0 is specified, the alarm is set to 0, which will never send the
+ .B SIGALRM
+ and update the display.
+
diff --git a/smartt-top/debian/patches/tload_no_optargs.patch b/smartt-top/debian/patches/tload_no_optargs.patch
new file mode 100644
index 0000000..1fd365b
--- /dev/null
+++ b/smartt-top/debian/patches/tload_no_optargs.patch
@@ -0,0 +1,16 @@
+Author: Craig Small <csmall@debian.org>
+Description: Removed unneeded optarg and optind variables form tload.c
+Index: b/tload.c
+===================================================================
+--- a/tload.c 2009-11-24 20:53:03.000000000 +1100
++++ b/tload.c 2009-11-24 21:00:42.000000000 +1100
+@@ -30,9 +30,6 @@
+ static int dly=5;
+ static jmp_buf jb;
+
+-extern int optind;
+-extern char *optarg;
+-
+ static void alrm(int signo)
+ {
+ (void)signo;
diff --git a/smartt-top/debian/patches/top.1.patch b/smartt-top/debian/patches/top.1.patch
new file mode 100644
index 0000000..92b00e2
--- /dev/null
+++ b/smartt-top/debian/patches/top.1.patch
@@ -0,0 +1,416 @@
+Description: Fix bold in top manual page
+Fix page fault description because storage may not be disk
+Author: Craig Small <csmall@debian.org>
+Index: b/top.1
+===================================================================
+--- a/top.1 2009-11-24 20:53:04.000000000 +1100
++++ b/top.1 2009-11-24 21:00:38.000000000 +1100
+@@ -42,7 +42,7 @@
+ .ds EM \ \fB\-\-\ \fR
+ \# - these two are for chuckles, makes great grammar
+ .ds Me top
+-.ds ME \fBtop\fR
++.ds NE \fBtop\fR
+ \# - other misc strings for consistent usage/emphasis
+ .ds F \fIOff\fR
+ .ds O \fIOn\fR
+@@ -85,7 +85,7 @@
+ .\" ----------------------------------------------------------------------
+ .SH SYNOPSIS
+ .\" ----------------------------------------------------------------------
+-\*(ME \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI
++\*(NE \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI
+ iterations\fR \-\fBp\fI pid\fR [,\fI pid\fR ...]
+
+ The traditional switches '-' and whitespace are optional.
+@@ -94,7 +94,7 @@
+ .\" ----------------------------------------------------------------------
+ .SH DESCRIPTION
+ .\" ----------------------------------------------------------------------
+-The \*(ME program provides a dynamic real-time view of a running system.
++The \*(NE program provides a dynamic real-time view of a running system.
+ It can display\fB system\fR summary information as well as a list of\fB
+ tasks\fR currently being managed by the Linux kernel.
+ The types of system summary information shown and the types, order and
+@@ -104,7 +104,7 @@
+ The program provides a limited interactive interface for process
+ manipulation as well as a much more extensive interface for personal
+ configuration \*(EM encompassing every aspect of its operation.
+-And while \*(ME is referred to throughout this document, you are free
++And while \*(NE is referred to throughout this document, you are free
+ to name the program anything you wish.
+ That new name, possibly an alias, will then be reflected on \*(Me's display
+ and used when reading and writing a \*(CF.
+@@ -190,7 +190,7 @@
+ 'I' - Irix mode On\ \ (no, 'solaris' smp)
+ * 'p' - PID monitoring Off
+ * 's' - Secure mode Off (unsecured)
+- 'B' - Bold disable Off
++ 'B' - Bold enable Off
+ \fISummary_Area_defaults\fR
+ 'l' - Load Avg/Uptime On\ \ (thus program name)
+ 't' - Task/Cpu states On\ \ (1+1 lines, see '1')
+@@ -413,16 +413,14 @@
+ q:\fB RES\fR \*(EM Resident size (kb)
+ The non-swapped \*(MP a task has used.
+
+-RES = CODE + DATA.
+-
+ .TP 3
+ r:\fB CODE\fR \*(EM Code size (kb)
+-The amount of \*(MP devoted to executable code, also known as
++The amount of \*(MV devoted to executable code, also known as
+ the 'text resident set' size or TRS.
+
+ .TP 3
+ s:\fB DATA\fR \*(EM Data+Stack size (kb)
+-The amount of \*(MP devoted to other than executable code, also known as
++The amount of \*(MV devoted to other than executable code, also known as
+ the 'data resident set' size or DRS.
+
+ .TP 3
+@@ -436,8 +434,8 @@
+ The number of\fB major\fR page faults that have occurred for a task.
+ A page fault occurs when a process attempts to read from or write to a virtual
+ page that is not currently present in its address space.
+-A major page fault is when disk access is involved in making that
+-page available.
++A major page fault is when backing storage access (such as a disk) is involved
++in making that page available.
+
+ .TP 3
+ v:\fB nDRT\fR \*(EM Dirty Pages count
+@@ -504,7 +502,7 @@
+ .\" ......................................................................
+ .SS 2b. SELECTING and ORDERING Columns
+ .\" ----------------------------------------------------------------------
+-After pressing the \*(CIs 'f' (Fields select) or \'o' (Order fields) you will
++After pressing the \*(CIs 'f' (Fields select) or 'o' (Order fields) you will
+ be shown a screen containing the current \fBfields string\fR followed by names
+ and descriptions for all fields.
+
+@@ -580,7 +578,7 @@
+ see current status,
+
+ .TP 7
+-\ \ \'\fB?\fR\' or \'\fBh\fR\' :\fIHelp\fR
++\ \ \<\fB?\fR\> or \<\fBh\fR\> :\fIHelp\fR
+ There are two help levels available.
+ The first will provide a reminder of all the basic \*(CIs.
+ If \*(Me is\fI secured\fR, that screen will be abbreviated.
+@@ -589,7 +587,7 @@
+ applicable to \*(AM.
+
+ .TP 7
+-\ \ \'\fB=\fR\' :\fIExit_Task_Limits\fR
++\ \ \<\fB=\fR\> :\fIExit_Task_Limits\fR
+ Removes restrictions on which tasks are shown.
+ This command will reverse any 'i' (idle tasks) and 'n' (max tasks) commands
+ that might be active.
+@@ -599,13 +597,13 @@
+ When operating in \*(AM this command has a slightly broader meaning.
+
+ .TP 7
+-\ \ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR
++\ \ \<\fBA\fR\> :\fIAlternate_Display_Mode_toggle\fR
+ This command will switch between \*(FM and \*(AM.
+-\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into
+-\*(CWs and field groups.
++\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight
++into \*(CWs and field groups.
+
+ .TP 7
+-\ \ \'\fBB\fR\' :\fIBold_Disable/Enable_toggle\fR
++\ \ \<\fBB\fR\> :\fIBold_Disable/Enable_toggle\fR
+ This command will influence use of the 'bold' terminfo capability and
+ alters\fB both\fR the \*(SA and \*(TA for the \*(CW.
+ While it is intended primarily for use with dumb terminals, it can be
+@@ -617,7 +615,7 @@
+ there will be no visual confirmation that they are even on.
+
+ .TP 7
+-*\ \'\fBd\fR\' or \'\fBs\fR\' :\fIChange_Delay_Time_interval\fR
++*\ \<\fBd\fR\> or \<\fBs\fR\> :\fIChange_Delay_Time_interval\fR
+ You will be prompted to enter the delay time, in seconds, between
+ display updates.
+
+@@ -631,32 +629,32 @@
+ and view the system summary on the second line.
+
+ .TP 7
+-\ \ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR
++\ \ \<\fBG\fR\> :\fIChoose_Another_Window/Field_Group\fR
+ You will be prompted to enter a number between 1 and 4 designating the
+ window/field group which should be made the \*(CW.
+ You will soon grow comfortable with these 4 windows, especially after
+ experimenting with \*(AM.
+
+ .TP 7
+-\ \ \'\fBI\fR\' :\fIIrix/Solaris_Mode_toggle\fR
++\ \ \<\fBI\fR\> :\fIIrix/Solaris_Mode_toggle\fR
+ When operating in 'Solaris mode' ('I' toggled \*F), a task's \*(Pu usage
+ will be divided by the total number of \*(PUs.
+ After issuing this command, you'll be informed of the new state of this toggle.
+
+ .TP 7
+-\ \ \'\fBu\fR\' :\fIselect a user\fR
++\ \ \<\fBu\fR\> :\fIselect a user\fR
+ You will be prompted for a UID or username. Only processes
+ belonging to the selected user will be displayed. This option
+ matches on the effective UID.
+
+ .TP 7
+-\ \ \'\fBU\fR\' :\fIselect a user\fR
++\ \ \<\fBU\fR\> :\fIselect a user\fR
+ You will be prompted for a UID or username. Only processes
+ belonging to the selected user will be displayed. This option
+ matches on the real, effective, saved, and filesystem UID.
+
+ .TP 7
+-*\ \'\fBk\fR\' :\fIKill_a_task\fR
++*\ \<\fBk\fR\> :\fIKill_a_task\fR
+ You will be prompted for a PID and then the signal to send.
+ The default signal, as reflected in the prompt, is SIGTERM.
+ However, you can send any signal, via number or name.
+@@ -667,24 +665,24 @@
+ 2) at the signal prompt, type 0
+
+ .TP 7
+-\ \ \'\fBq\fR\' :\fIQuit\fR
++\ \ \<\fBq\fR\> :\fIQuit\fR
+
+ .TP 7
+-*\ \'\fBr\fR\' :\fIRenice_a_Task\fR
++*\ \<\fBr\fR\> :\fIRenice_a_Task\fR
+ You will be prompted for a PID and then the value to nice it to.
+ Entering a positive value will cause a process to lose priority.
+ Conversely, a negative value will cause a process to be viewed more
+ favorably by the kernel.
+
+ .TP 7
+-\ \ \'\fBW\fR\' :\fIWrite_the_Configuration_File\fR
++\ \ \<\fBW\fR\> :\fIWrite_the_Configuration_File\fR
+ This will save all of your options and toggles plus the current
+ display mode and delay time.
+ By issuing this command just before quitting \*(Me, you will be able restart
+ later in exactly that same state.
+
+ .TP 7
+-\ \ \'\fBZ\fR\' :\fIChange_Color_Mapping
++\ \ \<\fBZ\fR\> :\fIChange_Color_Mapping
+ This key will take you to a separate screen where you can change the
+ colors for the \*(CW, or for all windows.
+ For details regarding this \*(CI \*(Xt 3d. COLOR Mapping.
+@@ -700,25 +698,25 @@
+ of messages and prompts.
+
+ These commands always impact just the \*(CW/field group.
+-\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into
+-\*(CWs and field groups.
++\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight
++into \*(CWs and field groups.
+
+ .TP 7
+-\ \ \'\fBl\fR\' :\fIToggle_Load_Average/Uptime\fR \*(EM On/Off
++\ \ \<\fBl\fR\> :\fIToggle_Load_Average/Uptime\fR \*(EM On/Off
+ This is also the line containing the program name (possibly an alias) when
+ operating in \*(FM or the \*(CW name when operating in \*(AM.
+
+ .TP 7
+-\ \ \'\fBm\fR\' :\fIToggle_Memory/Swap_Usage\fR \*(EM On/Off
++\ \ \<\fBm\fR\> :\fIToggle_Memory/Swap_Usage\fR \*(EM On/Off
+ This command affects two \*(SA lines.
+
+ .TP 7
+-\ \ \'\fBt\fR\' :\fIToggle_Task/Cpu_States\fR \*(EM On/Off
++\ \ \<\fBt\fR\> :\fIToggle_Task/Cpu_States\fR \*(EM On/Off
+ This command affects from 2 to many \*(SA lines, depending on the state
+ of the '1' toggle and whether or not \*(Me is running under true SMP.
+
+ .TP 7
+-\ \ \'\fB1\fR\' :\fIToggle_Single/Separate_Cpu_States\fR \*(EM On/Off
++\ \ \<\fB1\fR\> :\fIToggle_Single/Separate_Cpu_States\fR \*(EM On/Off
+ This command affects how the 't' command's Cpu States portion is shown.
+ Although this toggle exists primarily to serve massively-parallel SMP machines,
+ it is not restricted to solely SMP environments.
+@@ -750,12 +748,12 @@
+ .in
+
+ .TP 7
+-\ \ \'\fBb\fR\' :\fIBold/Reverse_toggle\fR
++\ \ \<\fBb\fR\> :\fIBold/Reverse_toggle\fR
+ This command will impact how the 'x' and 'y' toggles are displayed.
+ Further, it will only be available when at least one of those toggles is \*O.
+
+ .TP 7
+-\ \ \'\fBx\fR\' :\fIColumn_Highlight_toggle\fR
++\ \ \<\fBx\fR\> :\fIColumn_Highlight_toggle\fR
+ Changes highlighting for the current sort field.
+ You probably don't need a constant visual reminder of the sort field and
+ \*(Me hopes that you always run with 'column highlight' \*F, due to the cost
+@@ -765,7 +763,7 @@
+ visual reminder.
+
+ .TP 7
+-\ \ \'\fBy\fR\' :\fIRow_Highlight_toggle\fR
++\ \ \<\fBy\fR\> :\fIRow_Highlight_toggle\fR
+ Changes highlighting for "running" tasks.
+ For additional insight into this task state, \*(Xt 2a. DESCRIPTIONS of Fields,
+ Process Status.
+@@ -774,7 +772,7 @@
+ The only costs will be a few additional tty escape sequences.
+
+ .TP 7
+-\ \ \'\fBz\fR\' :\fIColor/Monochrome_toggle\fR
++\ \ \<\fBz\fR\> :\fIColor/Monochrome_toggle\fR
+ Switches the \*(CW between your last used color scheme and the older form
+ of black-on-white or white-on-black.
+ This command will alter\fB both\fR the \*(SA and \*(TA but does not affect the
+@@ -785,20 +783,20 @@
+ .B CONTENT\fR of \*(TW
+ .PD 0
+ .TP 7
+-\ \ \'\fBc\fR\' :\fICommand_Line/Program_Name_toggle\fR
++\ \ \<\fBc\fR\> :\fICommand_Line/Program_Name_toggle\fR
+ This command will be honored whether or not the 'Command' column
+ is currently visible.
+ Later, should that field come into view, the change you applied will be seen.
+
+ .TP 7
+-\ \ \'\fBf\fR\' and \'\fBo\fR\' :\fIFields_select\fR or \fIOrder_fields\fR
++\ \ \<\fBf\fR\> and \<\fBo\fR\> :\fIFields_select\fR or \fIOrder_fields\fR
+ These keys display separate screens where you can change which
+ fields are displayed and their order.
+ For additional information on these \*(CIs
+ \*(Xt 2b. SELECTING and ORDERING Columns.
+
+ .TP 7
+-\ \ \'\fBH\fR\' :\fIThreads_toggle\fR
++\ \ \<\fBS\fR\> :\fICumulative_Time_Mode_toggle\fR
+ When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process.
+
+ .TP 7
+@@ -818,7 +816,7 @@
+ effect, simply ask for help and view the window summary on the second line.
+
+ .TP 7
+-\ \ \'\fBu\fR\' :\fIShow_Specific_User_Only\fR
++\ \ \<\fBu\fR\> :\fIShow_Specific_User_Only\fR
+ You will be prompted to enter the name of the user to display.
+ Thereafter, in that \*(TW only matching User ID's will be shown, or possibly
+ no tasks will be shown.
+@@ -831,7 +829,7 @@
+ .B SIZE\fR of \*(TW
+ .PD 0
+ .TP 7
+-\ \ \'\fBi\fR\' :\fIIdle_Processes_toggle\fR
++\ \ \<\fBi\fR\> :\fIIdle_Processes_toggle\fR
+ Displays all tasks or just active tasks.
+ When this toggle is \*F, idled or zombied processes will not be displayed.
+
+@@ -839,7 +837,7 @@
+ affect the window's size, as all prior \*(TDs will have already been painted.
+
+ .TP 7
+-\ \ \'\fBn\fR\' or \'#\' :\fISet_Maximum_Tasks\fR
++\ \ \<\fBn\fR\> or \<\fB#\fR\> :\fISet_Maximum_Tasks\fR
+ You will be prompted to enter the number of tasks to display.
+ The lessor of your number and available screen rows will be used.
+
+@@ -878,12 +876,12 @@
+ .in
+
+ .TP 7
+-\ \ \'\fB<\fR\' :\fIMove_Sort_Field_Left\fR
++\ \ \<\fB<\fR\> :\fIMove_Sort_Field_Left\fR
+ Moves the sort column to the left unless the current sort field is
+ the first field being displayed.
+
+ .TP 7
+-\ \ \'\fB>\fR\' :\fIMove_Sort_Field_Right\fR
++\ \ \<\fB>\fR\> :\fIMove_Sort_Field_Right\fR
+ Moves the sort column to the right unless the current sort field is
+ the last field being displayed.
+
+@@ -894,7 +892,7 @@
+ .in
+
+ .TP 7
+-\ \ \'\fBF\fR\' or \'\fBO\fR\' :\fISelect_Sort_Field\fR
++\ \ \<\fBF\fR\> or \<\fBO\fR\> :\fISelect_Sort_Field\fR
+ These keys display a separate screen where you can change which field
+ is used as the sort column.
+
+@@ -907,7 +905,7 @@
+ when running \*(Me with column highlighting turned \*F.
+
+ .TP 7
+-\ \ \'\fBR\fR\' :\fIReverse/Normal_Sort_Field_toggle\fR
++\ \ \<\fBR\fR\> :\fIReverse/Normal_Sort_Field_toggle\fR
+ Using this \*(CI you can alternate between high-to-low and low-to-high sorts.
+
+ .PP
+@@ -984,7 +982,7 @@
+ .\" ......................................................................
+ .SS 4b. COMMANDS for Windows
+ .TP 7
+-\ \ \'\fB-\fR\' and \'\fB_\fR\' :\fIShow/Hide_Window(s)_toggles\fR
++\ \ \<\fB-\fR\> and \<\fB_\fR\> :\fIShow/Hide_Window(s)_toggles\fR
+ The '-' key turns the \*(CW's \*(TD \*O and \*F.
+ When \*O, that \*(TA will show a minimum of the columns header you've
+ established with the 'f' and 'o' commands.
+@@ -998,7 +996,7 @@
+ as the only display element.
+
+ .TP 7
+-*\ \'\fB=\fR\' and \'\fB+\fR\' :\fIEqualize_(re-balance)_Window(s)\fR
++*\ \<\fB=\fR\> and \<\fB+\fR\> :\fIEqualize_(re-balance)_Window(s)\fR
+ The '=' key forces the \*(CW's \*(TD to be visible.
+ It also reverses any 'i' (idle tasks) and 'n' (max tasks) commands that might
+ be active.
+@@ -1009,7 +1007,7 @@
+ except for the 'i' (idle tasks) and 'n' (max tasks) commands.
+
+ .TP 7
+-*\ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR
++*\ \<\fBA\fR\> :\fIAlternate_Display_Mode_toggle\fR
+ This command will switch between \*(FM and \*(AM.
+
+ The first time you issue this command, all four \*(TDs will be shown.
+@@ -1017,7 +1015,7 @@
+ chosen to make visible.
+
+ .TP 7
+-*\ \'\fBa\fR\' and \'\fBw\fR\' :\fINext_Window_Forward/Backward\fR
++*\ \<\fBa\fR\> and \<\fBw\fR\> :\fINext_Window_Forward/Backward\fR
+ This will change the \*(CW, which in turn changes the window to which
+ commands are directed.
+ These keys act in a circular fashion so you can reach any desired \*(CW
+@@ -1028,7 +1026,7 @@
+ the \*(TD is \*F and many commands will be restricted.
+
+ .TP 7
+-*\ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR
++*\ \<\fBG\fR\> :\fIChoose_Another_Window/Field_Group\fR
+ You will be prompted to enter a number between 1 and 4 designating the
+ window/field group which should be made the \*(CW.
+
+@@ -1037,15 +1035,15 @@
+ commands.
+
+ .TP 7
+-\ \ \'\fBg\fR\' :\fIChange_Window/Field_Group_Name\fR
++\ \ \<\fBg\fR\> :\fIChange_Window/Field_Group_Name\fR
+ You will be prompted for a new name to be applied to the \*(CW.
+ It does not require that the window name be visible
+ (the 'l' toggle to be \*O).
+
+ .IP "*" 3
+ The \*(CIs shown with an \*(AS have use beyond \*(AM.
+- \'=', 'A', 'G' are always available
+- \'a', 'w' act the same when color mapping
++ '=', 'A', 'G' are always available
++ 'a', 'w' act the same when color mapping
+
+
+ .\" ----------------------------------------------------------------------
diff --git a/smartt-top/debian/patches/top.1_cpustates.patch b/smartt-top/debian/patches/top.1_cpustates.patch
new file mode 100644
index 0000000..8dbc499
--- /dev/null
+++ b/smartt-top/debian/patches/top.1_cpustates.patch
@@ -0,0 +1,50 @@
+Description: top CPU state descriptions
+Author: Craig Small <csmall@debian.org>
+Bug-Debian: http://bugs.debian.org/312157
+Index: b/top.1
+===================================================================
+--- a/top.1 2009-11-24 21:00:38.000000000 +1100
++++ b/top.1 2009-11-24 21:00:40.000000000 +1100
+@@ -537,6 +537,42 @@
+ upper case\fR letter and to the\fB right\fR with the\fB lower case\fR
+ letter.
+
++.\" ......................................................................
++.SS 2c. CPU States
++.\" ----------------------------------------------------------------------
++The CPU states are shown in the Summary Area. They are always shown as a
++percentage and are for the time between now and the last refresh.
++
++.TP 3
++\fB us\fR \*(EM User CPU time
++The time the CPU has spent running users' processes that are not
++niced.
++
++.TP 3
++\fB sy\fR \*(EM System CPU time
++The time the CPU has spent running the kernel and its processes.
++
++.TP 3
++\fB ni\fR \*(EM Nice CPU time
++The time the CPU has spent running users' proccess that have been
++niced.
++
++.TP 3
++\fB wa\fR \*(EM iowait
++Amount of time the CPU has been waiting for I/O to complete.
++
++.TP 3
++\fB hi\fR \*(EM Hardware IRQ
++The amount of time the CPU has been servicing hardware interrupts.
++
++.TP 3
++\fB si\fR \*(EM Software Interrupts
++The amount of time the CPU has been servicing software interrupts.
++
++.TP 3
++\fB st\fR \*(EM Steal Time
++The amount of CPU 'stolen' from this virtual machine by the hypervisor
++for other tasks (such as running another virtual machine).
+
+ .\" ----------------------------------------------------------------------
+ .SH 3. INTERACTIVE Commands
diff --git a/smartt-top/debian/patches/top_1_swap.patch b/smartt-top/debian/patches/top_1_swap.patch
new file mode 100644
index 0000000..946eead
--- /dev/null
+++ b/smartt-top/debian/patches/top_1_swap.patch
@@ -0,0 +1,23 @@
+Description: Explain what SWAP means in top
+Bug-Debian: http://bugs.debian.org/217841
+Author: Craig Small <csmall@debian.org>
+--- a/top.1
++++ b/top.1
+@@ -401,13 +401,13 @@
+ o:\fB VIRT\fR \*(EM Virtual Image (kb)
+ The total amount of \*(MV used by the task.
+ It includes all code, data and shared libraries plus pages that have been
+-swapped out.
+-
+-VIRT = SWAP + RES.
++swapped out and pages that have been mapped but not used.
+
+ .TP 3
+ p:\fB SWAP\fR \*(EM Swapped size (kb)
+-The swapped out portion of a task's total \*(MV image.
++Memory that is not resident but is present in a task. This is memory that
++has been swapped out but could include additional non-resident memory.
++This column is calculated by subtracting \*(MP from \*(MV.
+
+ .TP 3
+ q:\fB RES\fR \*(EM Resident size (kb)
diff --git a/smartt-top/debian/patches/top_c_resize.patch b/smartt-top/debian/patches/top_c_resize.patch
new file mode 100644
index 0000000..027bb65
--- /dev/null
+++ b/smartt-top/debian/patches/top_c_resize.patch
@@ -0,0 +1,75 @@
+Author: <hesso@pool.math.tu-berlin.de>
+Description: Prevent top from segfaulting when the display is shrinked to only
+a few (read: 3 or less) lines.
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 21:00:35.000000000 +1100
++++ b/top.c 2009-11-24 21:00:39.000000000 +1100
+@@ -140,7 +140,7 @@
+ are exploited in a macro and represent 90% of our optimization.
+ The Stdout_buf is transparent to our code and regardless of whose
+ buffer is used, stdout is flushed at frame end or if interactive. */
+-static char *Pseudo_scrn;
++static PSEUDO_SCREEN_t Pseudo_scrn;
+ static int Pseudo_row, Pseudo_cols, Pseudo_size;
+ #ifndef STDOUT_IOLBF
+ // less than stdout's normal buffer but with luck mostly '\n' anyway
+@@ -2431,7 +2431,10 @@
+ Pseudo_cols = Screen_cols + CLRBUFSIZ + 1;
+ if (Batch) Pseudo_size = ROWBUFSIZ + 1;
+ else Pseudo_size = Pseudo_cols * Screen_rows;
+- Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size);
++ if( Pseudo_scrn.buf == NULL || Pseudo_size > Pseudo_scrn.mem_size ) {
++ Pseudo_scrn.buf = alloc_r(Pseudo_scrn.buf, Pseudo_size);
++ Pseudo_scrn.mem_size = Pseudo_size;
++ }
+
+ // force rebuild of column headers AND libproc/readproc requirements
+ Frames_libflags = 0;
+@@ -3315,7 +3318,7 @@
+ // reframewins(), who also builds each window's column headers
+ if (!Frames_libflags) {
+ reframewins();
+- memset(Pseudo_scrn, '\0', Pseudo_size);
++ memset(Pseudo_scrn.buf, '\0', Pseudo_size);
+ }
+ Pseudo_row = Msg_row = scrlins = 0;
+ ppt = summary_show();
+Index: b/top.h
+===================================================================
+--- a/top.h 2009-11-24 20:53:04.000000000 +1100
++++ b/top.h 2009-11-24 21:00:39.000000000 +1100
+@@ -135,7 +135,7 @@
+ int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \
+ putp ( Batch ? _str : \
+ ({ \
+- char *restrict const _pse = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \
++ char *restrict const _pse = &Pseudo_scrn.buf[Pseudo_row++ * Pseudo_cols]; \
+ memcmp(_pse, _str, _len) ? memcpy(_pse, _str, _len) : "\n"; \
+ }) \
+ ); \
+@@ -149,7 +149,11 @@
+ int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \
+ if (Batch) _ptr = _str; \
+ else { \
+- _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \
++ if (Pseudo_row * Pseudo_cols + _len > Pseudo_size) { \
++ Pseudo_scrn.buf = realloc(Pseudo_scrn.buf, Pseudo_row * Pseudo_cols + _len); \
++ Pseudo_scrn.mem_size = Pseudo_size = Pseudo_row * Pseudo_cols + _len; \
++ } \
++ _ptr = &Pseudo_scrn.buf[Pseudo_row++ * Pseudo_cols]; \
+ if (memcmp(_ptr, _str, _len)) { \
+ memcpy(_ptr, _str, _len); \
+ } else { \
+@@ -237,6 +241,11 @@
+ RCW_t win [4]; // a 'WIN_t.rc' for each of the 4 windows
+ } RCF_t;
+
++typedef struct PSEUDO_SCREEN_t {
++ char *buf;
++ int mem_size;
++} PSEUDO_SCREEN_t;
++
+ // The scaling 'type' used with scale_num() -- this is how
+ // the passed number is interpreted should scaling be necessary
+ enum scale_num {
diff --git a/smartt-top/debian/patches/top_highlight.patch b/smartt-top/debian/patches/top_highlight.patch
new file mode 100644
index 0000000..4b32177
--- /dev/null
+++ b/smartt-top/debian/patches/top_highlight.patch
@@ -0,0 +1,19 @@
+Author: Paolo Pantaleo <paolopantaleo@gmail.com>
+Description: Fix highlighting problem in top
+Bug-Debian: http://bugs.debian.org/351065
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 21:00:48.000000000 +1100
++++ b/top.c 2009-11-24 21:00:50.000000000 +1100
+@@ -3054,9 +3054,10 @@
+ snprintf(_z, sizeof(_z), f, ## va); \
+ snprintf(cbuf, sizeof(cbuf), "%s%s%s", \
+ q->capclr_rowhigh, \
+- _z, \
++ _z+advance, \
+ !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \
+ ); \
++ advance=0; \
+ pad += q->len_rowhigh; \
+ if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \
+ } \
diff --git a/smartt-top/debian/patches/top_irix.patch b/smartt-top/debian/patches/top_irix.patch
new file mode 100644
index 0000000..6a37289
--- /dev/null
+++ b/smartt-top/debian/patches/top_irix.patch
@@ -0,0 +1,44 @@
+Description: Change formal of IRix mode when showing threads
+Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Bug-Debian: http://bugs.debian.org/459890
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 20:53:05.000000000 +1100
++++ b/top.c 2009-11-24 21:00:33.000000000 +1100
+@@ -1783,7 +1783,8 @@
+ confighlp(Winstk[i].rc.fieldscur);
+ }
+
+- if(Rc.mode_irixps && smp_num_cpus>1){
++ if(Rc.mode_irixps && smp_num_cpus>1 &&
++ !(CHKw(Curwin, Show_THREADS))) {
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+ Fieldstab[P_CPU].fmts = " %4.0f";
+@@ -2570,6 +2571,15 @@
+ case 'H':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_THREADS);
++ if(Rc.mode_irixps && smp_num_cpus>1 &&
++ !(CHKw(Curwin, Show_THREADS))){
++ // good for 100 CPUs per process
++ pcpu_max_value = 9999.0;
++ Fieldstab[P_CPU].fmts = " %4.0f";
++ } else {
++ pcpu_max_value = 99.9;
++ Fieldstab[P_CPU].fmts = " %#4.1f";
++ }
+ show_msg(fmtmk("Show threads %s"
+ , CHKw(Curwin, Show_THREADS) ? "On" : "Off"));
+ }
+@@ -2626,7 +2636,8 @@
+ Rc.mode_irixps = !Rc.mode_irixps;
+ show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
+ #endif
+- if(Rc.mode_irixps && smp_num_cpus>1){
++ if(Rc.mode_irixps && smp_num_cpus>1 &&
++ !(CHKw(Curwin, Show_THREADS))){
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+ Fieldstab[P_CPU].fmts = " %4.0f";
diff --git a/smartt-top/debian/patches/top_mintime.patch b/smartt-top/debian/patches/top_mintime.patch
new file mode 100644
index 0000000..fe61d69
--- /dev/null
+++ b/smartt-top/debian/patches/top_mintime.patch
@@ -0,0 +1,16 @@
+Author: <csmall@debian.org>
+Description: Checks minimum time is valid
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 21:00:39.000000000 +1100
++++ b/top.c 2009-11-24 21:00:48.000000000 +1100
+@@ -2552,7 +2552,8 @@
+ else {
+ float tmp =
+ get_float(fmtmk("Change delay from %.1f to", Rc.delay_time));
+- if (tmp > -1) Rc.delay_time = tmp;
++ if (tmp == 0.0) show_msg("\aNot valid");
++ else if (tmp > 0) Rc.delay_time = tmp;
+ }
+ break;
+
diff --git a/smartt-top/debian/patches/top_no_openproc.patch b/smartt-top/debian/patches/top_no_openproc.patch
new file mode 100644
index 0000000..905b3ce
--- /dev/null
+++ b/smartt-top/debian/patches/top_no_openproc.patch
@@ -0,0 +1,21 @@
+Description: Exit top if cannot openproc
+Author: Justin Pryzby <justinpryzby@users.sourceforge.net>
+Bug-Debian: http://bugs.debian.org/378695
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 21:00:33.000000000 +1100
++++ b/top.c 2009-11-24 21:00:34.000000000 +1100
+@@ -1106,6 +1106,12 @@
+ else
+ PT = openproc(flags);
+
++ if (PT==NULL) {
++ std_err(strerror(errno));
++ exit(1);
++ }
++
++
+ // i) Allocated Chunks: *Existing* table; refresh + reuse
+ if (!(CHKw(Curwin, Show_THREADS))) {
+ while (curmax < savmax) {
diff --git a/smartt-top/debian/patches/top_nohz.patch b/smartt-top/debian/patches/top_nohz.patch
new file mode 100644
index 0000000..1e41fcd
--- /dev/null
+++ b/smartt-top/debian/patches/top_nohz.patch
@@ -0,0 +1,14 @@
+Author: <fabbione@sunfire.int.fabbione.net>
+Description: Handle idle time calculations correctly when running with NOHZ.
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 21:00:50.000000000 +1100
++++ b/top.c 2009-11-24 21:00:50.000000000 +1100
+@@ -2916,6 +2916,7 @@
+ s_frme = cpu->s - cpu->s_sav;
+ n_frme = cpu->n - cpu->n_sav;
+ i_frme = TRIMz(cpu->i - cpu->i_sav);
++ if ((u_frme == 0) && (i_frme == 0)) i_frme = 100.0;
+ w_frme = cpu->w - cpu->w_sav;
+ x_frme = cpu->x - cpu->x_sav;
+ y_frme = cpu->y - cpu->y_sav;
diff --git a/smartt-top/debian/patches/top_numeric_args.patch b/smartt-top/debian/patches/top_numeric_args.patch
new file mode 100644
index 0000000..841cf6a
--- /dev/null
+++ b/smartt-top/debian/patches/top_numeric_args.patch
@@ -0,0 +1,98 @@
+Description: Better top numeric argument handling
+Bug-Debian: http://bugs.debian.org/358724
+Author: Justin Pryzby <justinpryzby@users.sourceforge.net>
+--- a/top.c
++++ b/top.c
+@@ -32,6 +32,7 @@
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <math.h>
+ #include <string.h>
+
+ // Foul POS defines all sorts of stuff...
+@@ -1825,8 +1826,12 @@
+
+ while (*args) {
+ const char *cp = *(args++);
++ if (*cp!='-') {
++ std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s" , *cp, Myname, usage));
++ }
+
+- while (*cp) {
++ for(++cp; *cp; cp++) {
++ char *end;
+ switch (*cp) {
+ case '\0':
+ case '-':
+@@ -1842,8 +1847,15 @@
+ else if (*args) cp = *args++;
+ else std_err("-d requires argument");
+ /* a negative delay will be dealt with shortly... */
+- if (sscanf(cp, "%f", &tmp_delay) != 1)
+- std_err(fmtmk("bad delay '%s'", cp));
++ errno=0;
++ if (( (fabs(tmp_delay=strtod(cp, &end))==HUGE_VAL &&
++ errno==ERANGE) ||
++ (tmp_delay==0 && errno!=0) ||
++ end==cp ||
++ end!=cp+strlen(cp))) {
++ std_err(fmtmk("bad delay '%s'", cp));
++ }
++ cp=-1+end;
+ break;
+ case 'H':
+ TOGw(Curwin, Show_THREADS);
+@@ -1859,8 +1871,15 @@
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-n requires argument");
+- if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1)
+- std_err(fmtmk("bad iterations arg '%s'", cp));
++ errno=0;
++ if ((((Loops=strtol(cp, &end, 0))==LONG_MIN ||
++ Loops==LONG_MAX) &&
++ errno==ERANGE) ||
++ end==cp ||
++ end!=cp+strlen(cp) || Loops<1) {
++ std_err(fmtmk("bad iterations arg '%s'", cp));
++ }
++ cp=-1+end;
+ break;
+ case 'p':
+ do {
+@@ -1868,11 +1887,19 @@
+ selection_type = 'p';
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+- else std_err("-p argument missing");
++ else std_err("-p requires argument");
+ if (Monpidsidx >= MONPIDMAX)
+ std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
+- if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0)
+- std_err(fmtmk("bad pid '%s'", cp));
++ errno=0;
++ if ((((Monpids[Monpidsidx]=strtol(cp, &end, 0))==LONG_MIN ||
++ Monpids[Monpidsidx]==LONG_MAX) &&
++ errno==ERANGE) ||
++ end==cp ||
++ Monpids[Monpidsidx]<1) {
++ std_err(fmtmk("bad pid '%s'", cp));
++ }
++ cp=-1+end;
++
+ if (!Monpids[Monpidsidx])
+ Monpids[Monpidsidx] = getpid();
+ Monpidsidx++;
+@@ -1918,10 +1945,7 @@
+ , *cp, Myname, usage));
+
+ } /* end: switch (*cp) */
+-
+- /* advance cp and jump over any numerical args used above */
+- if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1;
+- } /* end: while (*cp) */
++ } /* end: for (; *cp) */
+ } /* end: while (*args) */
+
+ /* fixup delay time, maybe... */
diff --git a/smartt-top/debian/patches/top_stdin_eof.patch b/smartt-top/debian/patches/top_stdin_eof.patch
new file mode 100644
index 0000000..888bf85
--- /dev/null
+++ b/smartt-top/debian/patches/top_stdin_eof.patch
@@ -0,0 +1,20 @@
+Description: Check for stdin eof if term
+Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Bug-Debian: http://bugs.debian.org/458986
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 21:00:34.000000000 +1100
++++ b/top.c 2009-11-24 21:00:35.000000000 +1100
+@@ -3408,9 +3408,8 @@
+ // check 1st, in case tv zeroed (by sig handler) before it got set
+ rc = chin(0, &c, 1);
+ if (rc <= 0) {
+- // EOF is pretty much a "can't happen" except for a kernel bug.
+- // We should quickly die via SIGHUP, and thus not spin here.
+- // if (rc == 0) end_pgm(0); /* EOF from terminal */
++ if (rc == 0) end_pgm(0); /* EOF from terminal, may happen if top
++ * erroneously gets detached from it. */
+ fcntl(STDIN_FILENO, F_SETFL, file_flags);
+ select(1, &fs, NULL, NULL, &tv);
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
diff --git a/smartt-top/debian/patches/top_uid_length.patch b/smartt-top/debian/patches/top_uid_length.patch
new file mode 100644
index 0000000..634cbff
--- /dev/null
+++ b/smartt-top/debian/patches/top_uid_length.patch
@@ -0,0 +1,16 @@
+Description: Make the uid length 5 not 4
+Author: <csmall@debian.org>
+Bug-Debian: http://bugs.debian.org/426782
+Index: b/top.c
+===================================================================
+--- a/top.c 2009-11-24 21:00:35.000000000 +1100
++++ b/top.c 2009-11-24 21:00:35.000000000 +1100
+@@ -1237,7 +1237,7 @@
+ { "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE },
+ { "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER },
+ { "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER },
+- { "DdCc", " UID", " %4u", -1, -1, SF(UID), "User Id", L_NONE },
++ { "DdCc", " UID", " %5u", -1, -1, SF(UID), "User Id", L_NONE },
+ { "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER },
+ { "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP },
+ { "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat },
diff --git a/smartt-top/debian/patches/top_username_parse.patch b/smartt-top/debian/patches/top_username_parse.patch
new file mode 100644
index 0000000..47fceff
--- /dev/null
+++ b/smartt-top/debian/patches/top_username_parse.patch
@@ -0,0 +1,15 @@
+Description: Fixes off-by-one error so top -u username works
+Bug-Debian: http://bugs.debian.org/571790
+Author: Chuan-kai Lin <cklin@debian.org>
+Last-Update: 2010-03-01
+--- a/top.c
++++ b/top.c
+@@ -1924,7 +1924,7 @@
+ errmsg = parse_uid(cp, &selection_uid);
+ if (errmsg) std_err(errmsg);
+ selection_type = 'u';
+- cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
++ cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp)-1; // FIXME: junk
+ } while(0);
+ break;
+ case 'U':
diff --git a/smartt-top/debian/patches/uptime.1.patch b/smartt-top/debian/patches/uptime.1.patch
new file mode 100644
index 0000000..e5946f8
--- /dev/null
+++ b/smartt-top/debian/patches/uptime.1.patch
@@ -0,0 +1,48 @@
+Description: Cleanup uptime man page #282168
+Author: Brendan O'Dea <bod@debian.org>
+Bug-Debian: http://bugs.debian.org/282168
+Reviewed-by: Craig Small<csmall@debian.org>
+Index: b/uptime.1
+===================================================================
+--- a/uptime.1 2009-11-24 20:53:05.000000000 +1100
++++ b/uptime.1 2009-11-24 21:00:35.000000000 +1100
+@@ -6,7 +6,8 @@
+ .SH SYNOPSIS
+ .B uptime
+ .br
+-.BR uptime " [" "\-V" ]
++.B uptime
++.RB [ \-V ]
+ .SH DESCRIPTION
+ .B uptime
+ gives a one line display of the following information.
+@@ -14,13 +15,25 @@
+ how long the system has been running,
+ how many users are currently logged on,
+ and the system load averages for the past 1, 5, and 15 minutes.
+-.sp
++
+ This is the same information contained in the header line displayed by
+ .BR w (1).
++.sp
++System load averages is the average number of processes that are either
++in a runnable or uninterruptable state. A process in a runnable state is
++either using the CPU or waiting to use the CPU. A process in
++uninterruptable state is waiting for some I/O access, eg waiting for
++disk. The averages are taken over the three time intervals.
++Load averages are not normalized for the number of CPUs in a system, so
++a load average of 1 means a single CPU system is loaded all the time
++while on a 4 CPU system it means it was idle 75% of the time.
+ .SH FILES
+-.IR /var/run/utmp " information about who is currently logged on"
+-.br
+-.IR /proc " process information"
++.TP
++.I /var/run/utmp
++information about who is currently logged on
++.TP
++.I /proc
++process information
+ .SH AUTHORS
+ .B uptime
+ was written by Larry Greenfield <greenfie@gauss.rutgers.edu> and
diff --git a/smartt-top/debian/patches/vmstat.8.patch b/smartt-top/debian/patches/vmstat.8.patch
new file mode 100644
index 0000000..4963e4c
--- /dev/null
+++ b/smartt-top/debian/patches/vmstat.8.patch
@@ -0,0 +1,109 @@
+Author: <csmall@debian.org>
+Description: Fixes groff problems
+Removes pslab as it doesnt appear
+Index: b/vmstat.8
+===================================================================
+--- a/vmstat.8 2009-11-24 20:53:04.000000000 +1100
++++ b/vmstat.8 2009-11-24 21:00:36.000000000 +1100
+@@ -1,6 +1,6 @@
+ .\" This page Copyright (C) 1994 Henry Ware <al172@yfn.ysu.edu>
+ .\" Distributed under the GPL, Copyleft 1994.
+-.TH VMSTAT 8 "27 July 1994 " "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual"
++.TH VMSTAT 8 "2009 Jan 9" "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual"
+ .SH NAME
+ vmstat \- Report virtual memory statistics
+ .SH SYNOPSIS
+@@ -22,32 +22,35 @@
+ .RB [ "\-d"]
+ .br
+ .B vmstat
++.RB [ "\-D"]
++.br
++.B vmstat
+ .RB [ "\-p disk partition"]
+ .br
+ .B vmstat
+ .RB [ "\-V" ]
+ .SH DESCRIPTION
+ \fBvmstat\fP reports information about processes, memory, paging,
+-block IO, traps, and cpu activity.
++block IO, traps, disks and cpu activity.
+
+ The first report produced gives averages since the last reboot. Additional
+ reports give information on a sampling period of length \fIdelay\fP.
+ The process and memory reports are instantaneous in either case.
+
+ .SS Options
+-The \fB-a\fP switch displays active/inactive memory, given a 2.5.41 kernel or better.
++The \fB\-a\fP switch displays active/inactive memory, given a 2.5.41 kernel or better.
+ .PP
+-The \fB-f\fP switch displays the number of forks since boot.
++The \fB\-f\fP switch displays the number of forks since boot.
+ This includes the fork, vfork, and clone system calls, and is
+ equivalent to the total number of tasks created. Each process
+ is represented by one or more tasks, depending on thread usage.
+ This display does not repeat.
+ .PP
+-The \fB-m\fP displays slabinfo.
++The \fB\-m\fP displays slabinfo.
+ .PP
+-The \fB-n\fP switch causes the header to be displayed only once rather than periodically.
++The \fB\-n\fP switch causes the header to be displayed only once rather than periodically.
+ .PP
+-The \fB-s\fP switch displays a table of various event counters
++The \fB\-s\fP switch displays a table of various event counters
+ and memory statistics. This display does not repeat.
+ .PP
+ .I delay
+@@ -58,13 +61,15 @@
+ is the number of updates. If no count is specified and delay is
+ defined, \fIcount\fP defaults to infinity.
+ .PP
+-The \fB-d\fP reports disk statistics (2.5.70 or above required)
++The \fB\-d\fP reports disk statistics (2.5.70 or above required)
++.PP
++The \fB-D\fP reports some summary statistics about disk activity.
+ .PP
+-The \fB-p\fP followed by some partition name for detailed statistics (2.5.70 or above required)
++The \fB\-p\fP followed by some partition name for detailed statistics (2.5.70 or above required)
+ .PP
+-The \fB-S\fP followed by k or K or m or M switches outputs between 1000, 1024, 1000000, or 1048576 bytes
++The \fB\-S\fP followed by k or K or m or M switches outputs between 1000, 1024, 1000000, or 1048576 bytes
+ .PP
+-The \fB-V\fP switch results in displaying version information.
++The \fB\-V\fP switch results in displaying version information.
+ .PP
+ .SH FIELD DESCRIPTION FOR VM MODE
+ .SS
+@@ -81,8 +86,8 @@
+ free: the amount of idle memory.
+ buff: the amount of memory used as buffers.
+ cache: the amount of memory used as cache.
+-inact: the amount of inactive memory. (-a option)
+-active: the amount of active memory. (-a option)
++inact: the amount of inactive memory. (\-a option)
++active: the amount of active memory. (\-a option)
+ .fi
+ .PP
+ .SS
+@@ -110,9 +115,9 @@
+ .B "CPU "
+ These are percentages of total CPU time.
+ .nf
+-us: Time spent running non-kernel code. (user time, including nice time)
++us: Time spent running non\-kernel code. (user time, including nice time)
+ sy: Time spent running kernel code. (system time)
+-id: Time spent idle. Prior to Linux 2.5.41, this includes IO-wait time.
++id: Time spent idle. Prior to Linux 2.5.41, this includes IO\-wait time.
+ wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle.
+ st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown.
+
+@@ -161,8 +166,6 @@
+ total: Total number of available objects
+ size: Size of each object
+ pages: Number of pages with at least one active object
+-totpages: Total number of allocated pages
+-pslab: Number of pages per slab
+ .fi
+
+ .SH NOTES
diff --git a/smartt-top/debian/patches/vmstat_headers.patch b/smartt-top/debian/patches/vmstat_headers.patch
new file mode 100644
index 0000000..d9ede29
--- /dev/null
+++ b/smartt-top/debian/patches/vmstat_headers.patch
@@ -0,0 +1,148 @@
+Description: Headers for disk and slab info
+getopt replacement
+Author: Liu Xing <liuxing@cn.fujitsu.com>
+Author: Michael Tokarev <mjt@corpit.ru>
+Bug-Debian: http://bugs.debian.org/436805
+Bug-Debian: http://bugs.debian.org/408088
+Index: b/vmstat.c
+===================================================================
+--- a/vmstat.c 2009-11-24 20:53:00.000000000 +1100
++++ b/vmstat.c 2009-11-24 21:00:49.000000000 +1100
+@@ -29,14 +29,15 @@
+ #include "proc/sysinfo.h"
+ #include "proc/version.h"
+
+-static unsigned long dataUnit=1024;
+-static char szDataUnit [16];
+ #define UNIT_B 1
+ #define UNIT_k 1000
+ #define UNIT_K 1024
+ #define UNIT_m 1000000
+ #define UNIT_M 1048576
+
++static unsigned long dataUnit=UNIT_K;
++static char szDataUnit[3] = "K";
++
+ #define VMSTAT 0
+ #define DISKSTAT 0x00000001
+ #define VMSUMSTAT 0x00000002
+@@ -354,6 +355,7 @@
+ if ((fDiskstat=fopen("/proc/diskstats", "rb"))){
+ fclose(fDiskstat);
+ ndisks=getdiskstat(&disks,&partitions);
++ if (!moreheaders) diskheader();
+ for(k=0; k<ndisks; k++){
+ if (moreheaders && ((k%height)==0)) diskheader();
+ printf(format,
+@@ -424,6 +426,7 @@
+ return;
+ }
+
++ if (!moreheaders) slabheader();
+ nSlab = getslabinfo(&slabs);
+ for(k=0; k<nSlab; k++){
+ if (moreheaders && ((k%height)==0)) slabheader();
+@@ -582,12 +585,10 @@
+ ////////////////////////////////////////////////////////////////////////////
+
+ int main(int argc, char *argv[]) {
+- char partition[16];
+- argc=0; /* redefined as number of integer arguments */
+- for (argv++;*argv;argv++) {
+- if ('-' ==(**argv)) {
+- switch (*(++(*argv))) {
+-
++ char *partition = NULL;
++ int c;
++
++ while((c = getopt(argc, argv, "VdafmDnp:S:s")) != EOF) switch(c) {
+ case 'V':
+ display_version();
+ exit(0);
+@@ -603,7 +604,7 @@
+ fork_format();
+ exit(0);
+ case 'm':
+- statMode |= SLABSTAT;
++ statMode |= SLABSTAT;
+ break;
+ case 'D':
+ statMode |= DISKSUMSTAT;
+@@ -614,53 +615,40 @@
+ break;
+ case 'p':
+ statMode |= PARTITIONSTAT;
+- if (argv[1]){
+- char *cp = *++argv;
+- if(!memcmp(cp,"/dev/",5)) cp += 5;
+- snprintf(partition, sizeof partition, "%s", cp);
+- }else{
+- fprintf(stderr, "-p requires an argument\n");
+- exit(EXIT_FAILURE);
+- }
++ partition = optarg;
++ if (memcmp(partition, "/dev/", 5) == 0) partition += 5;
+ break;
+ case 'S':
+- if (argv[1]){
+- ++argv;
+- if (!strcmp(*argv, "k")) dataUnit=UNIT_k;
+- else if (!strcmp(*argv, "K")) dataUnit=UNIT_K;
+- else if (!strcmp(*argv, "m")) dataUnit=UNIT_m;
+- else if (!strcmp(*argv, "M")) dataUnit=UNIT_M;
+- else {fprintf(stderr, "-S requires k, K, m or M (default is kb)\n");
+- exit(EXIT_FAILURE);
+- }
+- strcpy(szDataUnit, *argv);
+- }else {fprintf(stderr, "-S requires an argument\n");
+- exit(EXIT_FAILURE);
+- }
++ switch(optarg[0]) {
++ case 'b': case 'B': dataUnit = UNIT_B; break;
++ case 'k': dataUnit = UNIT_k; break;
++ case 'K': dataUnit = UNIT_K; break;
++ case 'm': dataUnit = UNIT_m; break;
++ case 'M': dataUnit = UNIT_M; break;
++ default:
++ fprintf(stderr, "-S requires k, K, m or M (default is kb)\n");
++ exit(EXIT_FAILURE);
++ }
++ szDataUnit[0] = optarg[0];
+ break;
+ case 's':
+- statMode |= VMSUMSTAT;
++ statMode |= VMSUMSTAT;
+ break;
+ default:
+ /* no other aguments defined yet. */
+ usage();
+- }
+- }else{
+- argc++;
+- switch (argc) {
+- case 1:
+- if ((sleep_time = atoi(*argv)) == 0)
++ }
++
++ if (optind < argc) {
++ if ((sleep_time = atoi(argv[optind++])) == 0)
+ usage();
+- num_updates = ULONG_MAX;
+- break;
+- case 2:
+- num_updates = atol(*argv);
+- break;
+- default:
+- usage();
+- } /* switch */
++ num_updates = ULONG_MAX;
+ }
+-}
++ if (optind < argc)
++ num_updates = atol(argv[optind++]);
++ if (optind < argc)
++ usage();
++
+ if (moreheaders) {
+ int tmp=winhi()-3;
+ height=((tmp>0)?tmp:22);
diff --git a/smartt-top/debian/patches/vmstat_part_format.patch b/smartt-top/debian/patches/vmstat_part_format.patch
new file mode 100644
index 0000000..eb1b59a
--- /dev/null
+++ b/smartt-top/debian/patches/vmstat_part_format.patch
@@ -0,0 +1,77 @@
+Author: Daniel Novotny
+Description: The contents of /proc/diskstats have changed since 2.6.25
+Bug-Redhat: https://bugzilla.redhat.com/show_bug.cgi?id=485243
+--- a/proc/sysinfo.c
++++ b/proc/sysinfo.c
+@@ -796,6 +796,18 @@
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
++static int is_disk(char *dev)
++{
++ char syspath[PATH_MAX];
++ char *slash;
++
++ while ((slash = strchr(dev, '/')))
++ *slash = '!';
++ snprintf(syspath, sizeof(syspath), "/sys/block/%s", dev);
++ return !(access(syspath, F_OK));
++}
++
++/////////////////////////////////////////////////////////////////////////////
+
+ unsigned int getdiskstat(struct disk_stat **disks, struct partition_stat **partitions){
+ FILE* fd;
+@@ -803,6 +815,7 @@
+ int cPartition = 0;
+ int fields;
+ unsigned dummy;
++ char devname[PATH_MAX];
+
+ *disks = NULL;
+ *partitions = NULL;
+@@ -815,8 +828,9 @@
+ fclose(fd);
+ break;
+ }
+- fields = sscanf(buff, " %*d %*d %*s %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u", &dummy);
+- if (fields == 1){
++ fields = sscanf(buff, " %*d %*d %15s %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u",
++ &devname, &dummy);
++ if (fields == 2 && is_disk(devname)){
+ (*disks) = realloc(*disks, (cDisk+1)*sizeof(struct disk_stat));
+ sscanf(buff, " %*d %*d %15s %u %u %llu %u %u %u %llu %u %u %u %u",
+ //&disk_major,
+@@ -839,7 +853,9 @@
+ }else{
+ (*partitions) = realloc(*partitions, (cPartition+1)*sizeof(struct partition_stat));
+ fflush(stdout);
+- sscanf(buff, " %*d %*d %15s %u %llu %u %u",
++ sscanf(buff, (fields == 2)
++ ? " %*d %*d %15s %u %*u %llu %*u %u %*u %llu %*u %*u %*u %*u"
++ : " %*d %*d %15s %u %llu %u %llu",
+ //&part_major,
+ //&part_minor,
+ (*partitions)[cPartition].partition_name,
+--- a/proc/sysinfo.h
++++ b/proc/sysinfo.h
+@@ -113,7 +113,7 @@
+ unsigned parent_disk; // index into a struct disk_stat array
+ unsigned reads;
+ unsigned writes;
+- unsigned requested_writes;
++ unsigned long long requested_writes;
+ }partition_stat;
+
+ extern unsigned int getpartitions_num(struct disk_stat *disks, int ndisks);
+--- a/vmstat.c
++++ b/vmstat.c
+@@ -286,7 +286,7 @@
+ struct disk_stat *disks;
+ struct partition_stat *partitions, *current_partition=NULL;
+ unsigned long ndisks, j, k, npartitions;
+- const char format[] = "%20u %10llu %10u %10u\n";
++ const char format[] = "%20u %10llu %10u %10llu\n";
+
+ fDiskstat=fopen("/proc/diskstats","rb");
+ if(!fDiskstat){
diff --git a/smartt-top/debian/patches/vmstat_units b/smartt-top/debian/patches/vmstat_units
new file mode 100644
index 0000000..f2e8032
--- /dev/null
+++ b/smartt-top/debian/patches/vmstat_units
@@ -0,0 +1,41 @@
+Author: Michael Tokarev <mjt@tls.msk.ru>
+Description: vmstat shouldn't scale si/so just like it doesn't scale
+bi/bo.
+Use strtoull insteadof strtoul for some counters.
+Bug-Debian: http://bugs.debian.org/558361
+Bug-Debian: http://bugs.debian.org/558134
+--- a/proc/sysinfo.c
++++ b/proc/sysinfo.c
+@@ -606,7 +606,7 @@
+ );
+ head = tail+1;
+ if(!found) goto nextline;
+- *(found->slot) = strtoul(head,&tail,10);
++ *(found->slot) = (unsigned long)strtoull(head,&tail,10);
+ nextline:
+ tail = strchr(head, '\n');
+ if(!tail) break;
+--- a/vmstat.c
++++ b/vmstat.c
+@@ -209,8 +209,8 @@
+ unitConvert(kb_swap_used), unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+- (unsigned)( (*pswpin * kb_per_page * hz + divo2) / Div ),
+- (unsigned)( (*pswpout * kb_per_page * hz + divo2) / Div ),
++ (unsigned)( (*pswpin * unitConvert(kb_per_page) * hz + divo2) / Div ),
++ (unsigned)( (*pswpout * unitConvert(kb_per_page) * hz + divo2) / Div ),
+ (unsigned)( (*pgpgin * hz + divo2) / Div ),
+ (unsigned)( (*pgpgout * hz + divo2) / Div ),
+ (unsigned)( (*intr * hz + divo2) / Div ),
+@@ -258,8 +258,8 @@
+ unitConvert(kb_swap_used),unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+- (unsigned)( ( (pswpin [tog] - pswpin [!tog])*kb_per_page+sleep_half )/sleep_time ), /*si*/
+- (unsigned)( ( (pswpout[tog] - pswpout[!tog])*kb_per_page+sleep_half )/sleep_time ), /*so*/
++ (unsigned)( ( (pswpin [tog] - pswpin [!tog])*unitConvert(kb_per_page)+sleep_half )/sleep_time ), /*si*/
++ (unsigned)( ( (pswpout[tog] - pswpout[!tog])*unitConvert(kb_per_page)+sleep_half )/sleep_time ), /*so*/
+ (unsigned)( ( pgpgin [tog] - pgpgin [!tog] +sleep_half )/sleep_time ), /*bi*/
+ (unsigned)( ( pgpgout[tog] - pgpgout[!tog] +sleep_half )/sleep_time ), /*bo*/
+ (unsigned)( ( intr [tog] - intr [!tog] +sleep_half )/sleep_time ), /*in*/
diff --git a/smartt-top/debian/patches/vmstat_units.patch b/smartt-top/debian/patches/vmstat_units.patch
new file mode 100644
index 0000000..3345463
--- /dev/null
+++ b/smartt-top/debian/patches/vmstat_units.patch
@@ -0,0 +1,52 @@
+Author: Michael Tokarev <mjt@tls.msk.ru>
+Description: For vmstat only change units where it makes sense and try
+to stop some overflows of some kernel variables
+Bug-Debian: http://bugs.debian.org/558134
+Bug-Debian: http://bugs.debian.org/558361
+--- a/proc/sysinfo.c
++++ b/proc/sysinfo.c
+@@ -606,7 +606,7 @@
+ );
+ head = tail+1;
+ if(!found) goto nextline;
+- *(found->slot) = strtoul(head,&tail,10);
++ *(found->slot) = (unsigned long)strtoull(head,&tail,10);
+ nextline:
+ tail = strchr(head, '\n');
+ if(!tail) break;
+--- a/vmstat.c
++++ b/vmstat.c
+@@ -209,8 +209,8 @@
+ unitConvert(kb_swap_used), unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+- (unsigned)( (*pswpin * unitConvert(kb_per_page) * hz + divo2) / Div ),
+- (unsigned)( (*pswpout * unitConvert(kb_per_page) * hz + divo2) / Div ),
++ (unsigned)( (*pswpin * kb_per_page * hz + divo2) / Div ),
++ (unsigned)( (*pswpout * kb_per_page * hz + divo2) / Div ),
+ (unsigned)( (*pgpgin * hz + divo2) / Div ),
+ (unsigned)( (*pgpgout * hz + divo2) / Div ),
+ (unsigned)( (*intr * hz + divo2) / Div ),
+@@ -258,8 +258,8 @@
+ unitConvert(kb_swap_used),unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+- (unsigned)( ( (pswpin [tog] - pswpin [!tog])*unitConvert(kb_per_page)+sleep_half )/sleep_time ), /*si*/
+- (unsigned)( ( (pswpout[tog] - pswpout[!tog])*unitConvert(kb_per_page)+sleep_half )/sleep_time ), /*so*/
++ (unsigned)( ( (pswpin [tog] - pswpin [!tog])*kb_per_page+sleep_half )/sleep_time ), /*si*/
++ (unsigned)( ( (pswpout[tog] - pswpout[!tog])*kb_per_page+sleep_half )/sleep_time ), /*so*/
+ (unsigned)( ( pgpgin [tog] - pgpgin [!tog] +sleep_half )/sleep_time ), /*bi*/
+ (unsigned)( ( pgpgout[tog] - pgpgout[!tog] +sleep_half )/sleep_time ), /*bo*/
+ (unsigned)( ( intr [tog] - intr [!tog] +sleep_half )/sleep_time ), /*in*/
+--- a/vmstat.8
++++ b/vmstat.8
+@@ -67,7 +67,8 @@
+ .PP
+ The \fB\-p\fP followed by some partition name for detailed statistics (2.5.70 or above required)
+ .PP
+-The \fB\-S\fP followed by k or K or m or M switches outputs between 1000, 1024, 1000000, or 1048576 bytes
++The \fB\-S\fP followed by k or K or m or M switches changes the units of
++ouput from bytes to outputs between 1000, 1024, 1000000, or 1048576 bytes. Note this does not change the swap (si/so) or block (bi/bo) fields.
+ .PP
+ The \fB\-V\fP switch results in displaying version information.
+ .PP
diff --git a/smartt-top/debian/patches/w-bassman.patch b/smartt-top/debian/patches/w-bassman.patch
new file mode 100644
index 0000000..6e8657d
--- /dev/null
+++ b/smartt-top/debian/patches/w-bassman.patch
@@ -0,0 +1,88 @@
+Author: <csmall@debian.org>
+Description: w-bassman emulation with -o flag
+Bug-Debian: http://bugs.debian.org/414906
+Index: b/w.1
+===================================================================
+--- a/w.1 2009-11-24 20:53:04.000000000 +1100
++++ b/w.1 2009-11-24 21:00:39.000000000 +1100
+@@ -5,7 +5,7 @@
+ w \- Show who is logged on and what they are doing.
+ .SH SYNOPSIS
+ .B w \-
+-.RB [ husfV ]
++.RB [ husfVo ]
+ .RI [ user ]
+ .SH DESCRIPTION
+ .B "w "
+@@ -55,6 +55,9 @@
+ .B "\-V "
+ Display version information.
+ .TP 0.5i
++.B "\-o "
++Old style output. Prints blank space for idle times less than one minute.
++.TP 0.5i
+ .B "user "
+ Show information about the specified user only.
+
+Index: b/w.c
+===================================================================
+--- a/w.c 2009-11-24 21:00:37.000000000 +1100
++++ b/w.c 2009-11-24 21:00:39.000000000 +1100
+@@ -30,6 +30,7 @@
+ #include <termios.h>
+
+ static int ignoreuser = 0; /* for '-u' */
++static int oldstyle = 0; /* for '-o' */
+ static proc_t **procs; /* our snapshot of the process table */
+
+ typedef struct utmp utmp_t;
+@@ -76,6 +77,16 @@
+ printf(" ? ");
+ return;
+ }
++ if (oldstyle) {
++ if (t >= 48*60*60) /* > 2 days */
++ fprintf(fout, " %2ludays", t/(24*60*60));
++ else if (t >= 60*60) /* > 1 hour */
++ fprintf(fout, " %2lu:%02u ", t/(60*60), (unsigned) ((t/60)%60));
++ else if (t > 60) /* > 1 minute */
++ fprintf(fout, " %2lu:%02um", t/60, (unsigned) t%60);
++ else
++ fprintf(fout, " ");
++ } else {
+ if (t >= 48*60*60) /* > 2 days */
+ fprintf(fout, " %2ludays", t/(24*60*60));
+ else if (t >= 60*60) /* > 1 hour */
+@@ -84,6 +95,7 @@
+ fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
+ else
+ fprintf(fout, " %2lu.%02us", t, centi_sec);
++ }
+ }
+
+ /**** stat the device file to get an idle time */
+@@ -239,7 +251,7 @@
+ #endif
+
+ setlocale(LC_ALL, "");
+- for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++)
++ for (args=0; (ch = getopt(argc, argv, "hlusfVo")) != EOF; args++)
+ switch (ch) {
+ case 'h': header = 0; break;
+ case 'l': longform = 1; break;
+@@ -247,6 +259,7 @@
+ case 'f': from = !from; break;
+ case 'V': display_version(); exit(0);
+ case 'u': ignoreuser = 1; break;
++ case 'o': oldstyle = 1; break;
+ default:
+ printf("usage: w -hlsufV [user]\n"
+ " -h skip header\n"
+@@ -254,6 +267,7 @@
+ " -s short listing\n"
+ " -u ignore uid of processes\n"
+ " -f toggle FROM field (default %s)\n"
++ " -o old-style output\n"
+ " -V display version\n", FROM_STRING);
+ exit(1);
+ }
diff --git a/smartt-top/debian/patches/w.1.patch b/smartt-top/debian/patches/w.1.patch
new file mode 100644
index 0000000..ebb38e6
--- /dev/null
+++ b/smartt-top/debian/patches/w.1.patch
@@ -0,0 +1,70 @@
+Author: <csmall@debian.org>
+Description: Cleaned up the w man page
+Make note of the time formats
+Bug-Debian: http://bugs.debian.org/414906
+Index: b/w.1
+===================================================================
+--- a/w.1 2009-11-24 21:00:39.000000000 +1100
++++ b/w.1 2009-11-24 21:00:42.000000000 +1100
+@@ -4,26 +4,26 @@
+ .SH NAME
+ w \- Show who is logged on and what they are doing.
+ .SH SYNOPSIS
+-.B w \-
+-.RB [ husfVo ]
++.B w
++.RB [ \-husfVo ]
+ .RI [ user ]
+ .SH DESCRIPTION
+-.B "w "
++.B w
+ displays information about the users currently on the machine,
+ and their processes.
+ The header shows, in this order, the current time,
+ how long the system has been running,
+ how many users are currently logged on,
+ and the system load averages for the past 1, 5, and 15 minutes.
+-.sp
++
+ The following entries are displayed for each user:
+ login name, the tty name, the remote host, login time, idle time, JCPU, PCPU,
+ and the command line of their current process.
+-.sp
++
+ The JCPU time is the time used by all processes attached to the tty. It
+ does not include past background jobs, but does include currently
+ running background jobs.
+-.sp
++
+ The PCPU time is the time used by the current process, named in the "what"
+ field.
+
+@@ -35,7 +35,7 @@
+ .TP 0.5i
+ .B "\-u "
+ Ignores the username while figuring out the current process and cpu
+-times. To demonstrate this, do a "su" and do a "w" and a "w -u".
++times. To demonstrate this, do a "su" and do a "w" and a "w \-u".
+ .TP 0.5i
+ .B "\-s "
+ Use the short format.
+@@ -70,6 +70,19 @@
+ process information
+ .PP
+
++.SH NOTES
++The output for Idle, JCPU and PCPU times vaires depending on if you use
++the \-o (old style) option or not. These formats can be confusing if you
++switch between the old style and standard. In the following paragraphs
++days are DD, hours HH, minutes MM, seconds SS and 100ths of seconds CC.
++
++The standard format is DDdays, HH:MMm, MM:SS or SS.CC if the times are
++greater than 2 days, 1hour, or 1 minute respectively.
++
++For the \-o option, the output will be either DDdays, HH:MM, MM:SSm or
++blank if the times are greater than 2 days, 1 hour or 1 minute
++respectively.
++
+ .SH "SEE ALSO"
+ .BR free (1),
+ .BR ps (1),
diff --git a/smartt-top/debian/patches/w_columns.patch b/smartt-top/debian/patches/w_columns.patch
new file mode 100644
index 0000000..92424f8
--- /dev/null
+++ b/smartt-top/debian/patches/w_columns.patch
@@ -0,0 +1,30 @@
+Description: use COLUMNS environment if TIOCGWINSZ fails
+Author: Craig Small <csmall@debian.org>
+Index: b/w.c
+===================================================================
+--- a/w.c 2009-11-24 21:00:39.000000000 +1100
++++ b/w.c 2009-11-24 21:00:43.000000000 +1100
+@@ -241,10 +241,10 @@
+
+ /***** main */
+ int main(int argc, char **argv) {
+- char *user = NULL;
++ char *user = NULL, *p;
+ utmp_t *u;
+ struct winsize win;
+- int header=1, longform=1, from=1, args, maxcmd=80, ch;
++ int header=1, longform=1, from=1, args, maxcmd, ch;
+
+ #ifndef W_SHOWFROM
+ from = 0;
+@@ -277,6 +277,10 @@
+
+ if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
+ maxcmd = win.ws_col;
++ else if (p = getenv("COLUMNS"))
++ maxcmd = atoi(p);
++ else
++ maxcmd = 80;
+ if (maxcmd < 71) {
+ fprintf(stderr, "%d column window is too narrow\n", maxcmd);
+ exit(1);
diff --git a/smartt-top/debian/patches/w_envlength.patch b/smartt-top/debian/patches/w_envlength.patch
new file mode 100644
index 0000000..8ca1dcf
--- /dev/null
+++ b/smartt-top/debian/patches/w_envlength.patch
@@ -0,0 +1,173 @@
+Description: Use environment to set user and from/host column widths
+Author: Craig Small <csmall@debian.org>
+Bug-Debian: http://bugs.debian.org/396423
+Bug-Debian: http://bugs.debian.org/341439
+Index: b/w.1
+===================================================================
+--- a/w.1 2009-11-24 21:00:42.000000000 +1100
++++ b/w.1 2009-11-24 21:00:44.000000000 +1100
+@@ -1,6 +1,6 @@
+ .\" -*-Nroff-*-
+ .\"
+-.TH W 1 "8 Dec 1993 " " " "Linux User's Manual"
++.TH W 1 "5 October 2009 " " " "Linux User's Manual"
+ .SH NAME
+ w \- Show who is logged on and what they are doing.
+ .SH SYNOPSIS
+@@ -61,6 +61,14 @@
+ .B "user "
+ Show information about the specified user only.
+
++.SH ENVIRONMENT
++.TP
++PROCPS_USERLEN
++Override the default width of the username column. Defaults to 8.
++.TP
++PROCPS_FROMLEN
++Override the default width of the from column. Defaults to 16.
++
+ .SH FILES
+ .TP
+ .I /var/run/utmp
+Index: b/w.c
+===================================================================
+--- a/w.c 2009-11-24 21:00:43.000000000 +1100
++++ b/w.c 2009-11-24 21:00:44.000000000 +1100
+@@ -44,20 +44,19 @@
+ /* Uh... same thing as UT_NAMESIZE */
+ #define USERSZ (sizeof u->ut_user)
+
++/* Arbitary setting, not too big for the screen, max host size */
++#define HOSTSZ 40
++
+
+ /* This routine is careful since some programs leave utmp strings
+- * unprintable. Always outputs at least 16 chars padded with spaces
++ * unprintable. Always outputs at least fromlen chars padded with spaces
+ * on the right if necessary.
+ */
+-static void print_host(const char *restrict host, int len) {
++static void print_host(const char *restrict host, int len, const int fromlen) {
+ const char *last;
+ int width = 0;
+
+- /* FIXME: there should really be a way to configure this... */
+- /* for now, we'll just limit it to the 16 that the libc5 version
+- * of utmp uses.
+- */
+- if (len > 16) len = 16;
++ if (len > fromlen) len = fromlen;
+ last = host + len;
+ for ( ; host < last ; host++){
+ if (isprint(*host) && *host != ' ') {
+@@ -68,7 +67,8 @@
+ }
+ }
+ // space-fill, and a '-' too if needed to ensure the column exists
+- if(width < 16) fputs("- "+width, stdout);
++ while(width++ < fromlen)
++ fputc(' ',stdout);
+ }
+
+ /***** compact 7 char format for time intervals (belongs in libproc?) */
+@@ -180,7 +180,7 @@
+
+
+ /***** showinfo */
+-static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) {
++static void showinfo(utmp_t *u, int formtype, int maxcmd, int from, const int userlen, const int fromlen) {
+ unsigned long long jcpu;
+ int ut_pid_found;
+ unsigned i;
+@@ -205,9 +205,9 @@
+
+ strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */
+ if (formtype) {
+- printf("%-9.8s%-9.8s", uname, u->ut_line);
++ printf("%-*.*s%-9.8s", userlen+1, userlen, uname, u->ut_line);
+ if (from)
+- print_host(u->ut_host, sizeof u->ut_host);
++ print_host(u->ut_host, sizeof u->ut_host, fromlen);
+ print_logintime(u->ut_time, stdout);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+@@ -220,9 +220,9 @@
+ } else
+ printf(" ? ");
+ } else {
+- printf("%-9.8s%-9.8s", u->ut_user, u->ut_line);
++ printf("%-*.*s%-9.8s", userlen+1, userlen, u->ut_user, u->ut_line);
+ if (from)
+- print_host(u->ut_host, sizeof u->ut_host);
++ print_host(u->ut_host, sizeof u->ut_host, fromlen);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+ else
+@@ -245,6 +245,9 @@
+ utmp_t *u;
+ struct winsize win;
+ int header=1, longform=1, from=1, args, maxcmd, ch;
++ int userlen = 8;
++ int fromlen = 16;
++ char *env_var;
+
+ #ifndef W_SHOWFROM
+ from = 0;
+@@ -275,6 +278,22 @@
+ if ((argv[optind]))
+ user = (argv[optind]);
+
++ /* Get user field length from environment */
++ if ( (env_var = getenv("PROCPS_USERLEN")) != NULL) {
++ userlen = atoi(env_var);
++ if (userlen < 8 || userlen > USERSZ) {
++ fprintf(stderr, "User length environment PROCPS_USERLEN must be between 8 and %d, ignoring.\n", USERSZ);
++ userlen=8;
++ }
++ }
++ /* Get from field length from environment */
++ if ( (env_var = getenv("PROCPS_FROMLEN")) != NULL) {
++ fromlen = atoi(env_var);
++ if (fromlen < 8 || fromlen > HOSTSZ) {
++ fprintf(stderr, "From length environment PROCPS_FROMLEN must be between 8 and %d, ignoring.\n", HOSTSZ);
++ fromlen=16;
++ }
++ }
+ if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
+ maxcmd = win.ws_col;
+ else if (p = getenv("COLUMNS"))
+@@ -285,7 +304,7 @@
+ fprintf(stderr, "%d column window is too narrow\n", maxcmd);
+ exit(1);
+ }
+- maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0);
++ maxcmd -= 21 + userlen + (from ? fromlen : 0) + (longform ? 20 : 0);
+ if (maxcmd < 3)
+ fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
+
+@@ -293,7 +312,7 @@
+
+ if (header) { /* print uptime and headers */
+ print_uptime();
+- printf("USER TTY ");
++ printf("%-*s TTY ",userlen,"USER");
+ if (from)
+ printf("FROM ");
+ if (longform)
+@@ -309,14 +328,14 @@
+ u = getutent();
+ if (unlikely(!u)) break;
+ if (u->ut_type != USER_PROCESS) continue;
+- if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from);
++ if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from, userlen, fromlen);
+ }
+ } else {
+ for (;;) {
+ u = getutent();
+ if (unlikely(!u)) break;
+ if (u->ut_type != USER_PROCESS) continue;
+- if (*u->ut_user) showinfo(u, longform, maxcmd, from);
++ if (*u->ut_user) showinfo(u, longform, maxcmd, from, userlen, fromlen);
+ }
+ }
+ endutent();
diff --git a/smartt-top/debian/patches/w_time.patch b/smartt-top/debian/patches/w_time.patch
new file mode 100644
index 0000000..48f3884
--- /dev/null
+++ b/smartt-top/debian/patches/w_time.patch
@@ -0,0 +1,16 @@
+Description: Let fprintf print locale-friendly seconds
+Author: Craig Small <csmall@debian.org>
+Bug-Debian: http://bugs.debian.org/252575
+Index: b/w.c
+===================================================================
+--- a/w.c 2009-11-24 20:53:04.000000000 +1100
++++ b/w.c 2009-11-24 21:00:37.000000000 +1100
+@@ -83,7 +83,7 @@
+ else if (t > 60) /* > 1 minute */
+ fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
+ else
+- fprintf(fout, " %2lu.%02us", t, centi_sec);
++ fprintf(fout, " %2lu.%02us", t, centi_sec);
+ }
+
+ /**** stat the device file to get an idle time */
diff --git a/smartt-top/debian/patches/w_userproc.patch b/smartt-top/debian/patches/w_userproc.patch
new file mode 100644
index 0000000..8224acd
--- /dev/null
+++ b/smartt-top/debian/patches/w_userproc.patch
@@ -0,0 +1,15 @@
+Description: Make w make better guesses for user process
+ Based on suggestion by Herbert Xu <herbert@gondor.apana.org.au>
+Bug-Debian: http://bugs.debian.org/187808
+Author: Craig Small <csmall@debian.org>
+--- a/w.c
++++ b/w.c
+@@ -171,7 +171,7 @@
+ secondbest = tmp;
+ }
+ if(!ignoreuser && uid != tmp->euid && uid != tmp->ruid) continue;
+- if(tmp->tgid != tmp->tpgid) continue;
++ if(tmp->pgrp != tmp->tpgid) continue;
+ if(best && tmp->start_time <= best->start_time) continue;
+ best = tmp;
+ }
diff --git a/smartt-top/debian/patches/watch.1.patch b/smartt-top/debian/patches/watch.1.patch
new file mode 100644
index 0000000..3ff182c
--- /dev/null
+++ b/smartt-top/debian/patches/watch.1.patch
@@ -0,0 +1,103 @@
+Description: Cleanup of manual page and cumulative is a parameter not option
+Author: jidanni@jidanni.org
+Bug-Debian: http://bugs.debian.org/527193
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/watch.1
+===================================================================
+--- a/watch.1 2009-11-24 20:53:04.000000000 +1100
++++ b/watch.1 2009-11-24 21:00:36.000000000 +1100
+@@ -1,45 +1,54 @@
+-.TH WATCH 1 "1999 Apr 3" " " "Linux User's Manual"
++.TH WATCH 1 "2009 May 11" " " "Linux User's Manual"
+ .SH NAME
+ watch \- execute a program periodically, showing output fullscreen
+ .SH SYNOPSIS
++.na
+ .B watch
+-.I [\-dhvt] [\-n <seconds>] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=<seconds>] [\-\-no\-title] [\-\-version] <command>
++.RB [ \-dhvt ]
++.RB [ \-n
++.IR seconds ]
++.RB [ \-\-differences[=\fIcumulative\fP]]
++.RB [ \-\-help ]
++.RB [ \-\-interval=\fIseconds\fP]
++.RB [ \-\-no\-title ]
++.RB [ \-\-version ]
++.I command
+ .SH DESCRIPTION
+-.BR watch
++.B watch
+ runs
+ .I command
+ repeatedly, displaying its output (the first screenfull). This allows you to
+ watch the program output change over time. By default, the program is run
+ every 2 seconds; use
+-.I -n
++.B \-n
+ or
+-.I --interval
++.B \-\-interval
+ to specify a different interval.
+ .PP
+ The
+-.I -d
++.B \-d
+ or
+-.I --differences
+-flag will highlight the differences between successive updates. The
+-.I --cumulative
+-option makes highlighting "sticky", presenting a running display of all
++.B \-\-differences
++flag will highlight the differences between successive updates. Using
++.B \-\-differences=\fIcumulative\fP
++makes highlighting "sticky", presenting a running display of all
+ positions that have ever changed. The
+-.I -t
++.B \-t
+ or
+-.I --no-title
++.B \-\-no\-title
+ option turns off the header showing the interval, command, and current
+ time at the top of the display, as well as the following blank line.
+ .PP
+-.BR watch
++.B watch
+ will run until interrupted.
+ .SH NOTE
+ Note that
+ .I command
+-is given to "sh -c"
++is given to "sh \-c"
+ which means that you may need to use extra quoting to get the desired effect.
+ .PP
+ Note that POSIX option processing is used (i.e., option processing stops at
+-the first non-option argument). This means that flags after
++the first non\-option argument). This means that flags after
+ .I command
+ don't get interpreted by
+ .BR watch
+@@ -61,20 +70,20 @@
+ To see the effects of quoting, try these out
+ .IP
+ watch echo $$
+-.IP
++.br
+ watch echo '$$'
+-.IP
++.br
+ watch echo "'"'$$'"'"
+ .PP
+ You can watch for your administrator to install the latest kernel with
+ .IP
+-watch uname -r
++watch uname \-r
+ .PP
+ (Just kidding.)
+ .SH BUGS
+ Upon terminal resize, the screen will not be correctly repainted until the
+ next scheduled update. All
+-.I --differences
++.B \-\-differences
+ highlighting is lost on that update as well.
+ .PP
+ Non-printing characters are stripped from program output. Use "cat -v" as
diff --git a/smartt-top/debian/patches/watch_8bitchar.patch b/smartt-top/debian/patches/watch_8bitchar.patch
new file mode 100644
index 0000000..3c226f3
--- /dev/null
+++ b/smartt-top/debian/patches/watch_8bitchar.patch
@@ -0,0 +1,15 @@
+Author: <csmall@debian.org>
+Description: Make watch unsigned chars and 8bit clean
+Index: b/watch.c
+===================================================================
+--- a/watch.c 2009-11-24 20:53:03.000000000 +1100
++++ b/watch.c 2009-11-24 21:00:40.000000000 +1100
+@@ -297,7 +297,7 @@
+ move(y, x);
+ if (option_differences) {
+ chtype oldch = inch();
+- char oldc = oldch & A_CHARTEXT;
++ unsigned char oldc = oldch & A_CHARTEXT;
+ attr = !first_screen
+ && ((char)c != oldc
+ ||
diff --git a/smartt-top/debian/patches/watch_ansi_colour.patch b/smartt-top/debian/patches/watch_ansi_colour.patch
new file mode 100644
index 0000000..077e135
--- /dev/null
+++ b/smartt-top/debian/patches/watch_ansi_colour.patch
@@ -0,0 +1,180 @@
+Description: Interprets ANSI color code sequences
+Bug-Debian: http://bugs.debian.org/129334
+Author: Craig Small <csmall@debian.org>
+Last-Update: 2010-03-01
+--- a/watch.c
++++ b/watch.c
+@@ -37,6 +37,7 @@
+ #endif
+
+ static struct option longopts[] = {
++ {"color", no_argument, 0, 'c' },
+ {"differences", optional_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {"interval", required_argument, 0, 'n'},
+@@ -50,7 +51,7 @@
+ };
+
+ static char usage[] =
+- "Usage: %s [-bdhnptvx] [--beep] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
++ "Usage: %s [-bcdhnptvx] [--beep] [--color] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
+
+ static char *progname;
+
+@@ -62,6 +63,74 @@
+ static int precise_timekeeping = 0;
+
+ #define min(x,y) ((x) > (y) ? (y) : (x))
++#define MAX_ANSIBUF 10
++
++static void init_ansi_colors(void)
++{
++ int i;
++ short ncurses_colors[] = {
++ COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE,
++ COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE };
++
++ for (i=0; i< 8; i++)
++ init_pair(i+1, ncurses_colors[i], -1);
++}
++
++static void set_ansi_attribute(const int attrib)
++{
++ switch (attrib)
++ {
++ case -1:
++ return;
++ case 0:
++ standend();
++ return;
++ case 1:
++ attrset(A_BOLD);
++ return;
++ }
++ if (attrib >= 30 && attrib <= 37) {
++ color_set(attrib-29,NULL);
++ return;
++ }
++}
++
++static void process_ansi(FILE *fp)
++{
++ int i,c, num1, num2;
++ char buf[MAX_ANSIBUF];
++ char *nextnum;
++
++
++ c= getc(fp);
++ if (c != '[') {
++ ungetc(c, fp);
++ return;
++ }
++ for(i=0; i<MAX_ANSIBUF; i++)
++ {
++ c = getc(fp);
++ if (c == 'm') //COLOUR SEQUENCE ENDS in 'm'
++ {
++ buf[i] = '\0';
++ break;
++ }
++ if (c < '0' && c > '9' && c != ';')
++ {
++ while(--i >= 0)
++ ungetc(buf[i],fp);
++ return;
++ }
++ buf[i] = (char)c;
++ }
++ num1 = strtol(buf, &nextnum, 10);
++ if (nextnum != buf && nextnum[0] != '\0')
++ num2 = strtol(nextnum+1, NULL, 10);
++ else
++ num2 = -1;
++ set_ansi_attribute(num1);
++ set_ansi_attribute(num2);
++}
+
+ static void do_usage(void) NORETURN;
+ static void do_usage(void)
+@@ -187,6 +256,7 @@
+ option_differences_cumulative = 0,
+ option_exec = 0,
+ option_beep = 0,
++ option_color = 0,
+ option_errexit = 0,
+ option_help = 0, option_version = 0;
+ double interval = 2;
+@@ -205,12 +275,15 @@
+ setlocale(LC_ALL, "");
+ progname = argv[0];
+
+- while ((optc = getopt_long(argc, argv, "+bed::hn:pvtx", longopts, (int *) 0))
++ while ((optc = getopt_long(argc, argv, "+bced::hn:pvtx", longopts, (int *) 0))
+ != EOF) {
+ switch (optc) {
+ case 'b':
+ option_beep = 1;
+ break;
++ case 'c':
++ option_color = 1;
++ break;
+ case 'd':
+ option_differences = 1;
+ if (optarg)
+@@ -319,6 +392,14 @@
+ /* Set up tty for curses use. */
+ curses_started = 1;
+ initscr();
++ if (option_color) {
++ if (has_colors()) {
++ start_color();
++ use_default_colors();
++ init_ansi_colors();
++ } else
++ option_color = 0;
++ }
+ nonl();
+ noecho();
+ cbreak();
+@@ -460,7 +541,13 @@
+ }while (c != WEOF && !isprint(c) && c<128
+ && wcwidth(c) == 0
+ && c != L'\n'
+- && c != L'\t');
++ && c != L'\t'
++ && (c != L'\033' || option_color != 1));
++ if (c == L'\033' && option_color == 1) {
++ x--;
++ process_ansi(p);
++ continue;
++ }
+ if (c == L'\n')
+ if (!oldeolseen && x == 0) {
+ x = -1;
+--- a/watch.1
++++ b/watch.1
+@@ -1,4 +1,4 @@
+-.TH WATCH 1 "2009 May 11" " " "Linux User's Manual"
++.TH WATCH 1 "2010 Mar 01" " " "Linux User's Manual"
+ .SH NAME
+ watch \- execute a program periodically, showing output fullscreen
+ .SH SYNOPSIS
+@@ -8,6 +8,7 @@
+ .RB [ \-n
+ .IR seconds ]
+ .RB [ \-\-beep ]
++.RB [ \-\-color ]
+ .RB [ \-\-differences[=\fIcumulative\fP]]
+ .RB [ \-\-errexit ]
+ .RB [ \-\-exec ]
+@@ -75,6 +76,10 @@
+ options, which will cause
+ .B watch
+ to exit if the return value from the program is non-zero.
++.PP
++By default \fBwatch\fR will normally not pass escape characters, however
++if you use the \fI\-\-c\fR or \fI\-\-color\fR option, then
++\fBwatch\fR will interpret ANSI color sequences for the foreground.
+
+ .SH NOTE
+ Note that
diff --git a/smartt-top/debian/patches/watch_exec_beep.patch b/smartt-top/debian/patches/watch_exec_beep.patch
new file mode 100644
index 0000000..33de698
--- /dev/null
+++ b/smartt-top/debian/patches/watch_exec_beep.patch
@@ -0,0 +1,259 @@
+Description: Adds -exec and -beep flags and has better quoting #410967
+Adds -errexit flag now too #183346
+Author: Mordechai T. Abzug <morty@frakir.org>
+Bug-Debian: http://bugs.debian.org/410967
+Bug-Debian: http://bugs.debian.org/183346
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/watch.1
+===================================================================
+--- a/watch.1 2009-11-24 21:00:36.000000000 +1100
++++ b/watch.1 2009-11-24 21:00:43.000000000 +1100
+@@ -4,10 +4,13 @@
+ .SH SYNOPSIS
+ .na
+ .B watch
+-.RB [ \-dhvt ]
++.RB [ \-bdehvtx ]
+ .RB [ \-n
+ .IR seconds ]
++.RB [ \-\-beep ]
+ .RB [ \-\-differences[=\fIcumulative\fP]]
++.RB [ \-\-errexit ]
++.RB [ \-\-exec ]
+ .RB [ \-\-help ]
+ .RB [ \-\-interval=\fIseconds\fP]
+ .RB [ \-\-no\-title ]
+@@ -17,7 +20,8 @@
+ .B watch
+ runs
+ .I command
+-repeatedly, displaying its output (the first screenfull). This allows you to
++repeatedly, displaying its output and errors (the first screenfull). This
++allows you to
+ watch the program output change over time. By default, the program is run
+ every 2 seconds; use
+ .B \-n
+@@ -37,15 +41,33 @@
+ or
+ .B \-\-no\-title
+ option turns off the header showing the interval, command, and current
+-time at the top of the display, as well as the following blank line.
++time at the top of the display, as well as the following blank line. The
++.I \-b
++or
++.I \-\-beep
++option causes the command to beep if it has a non-zero exit.
+ .PP
+ .B watch
+-will run until interrupted.
++will normally run until interrupted. If you want
++.B watch
++to exit on an error from the program running use the
++.I \-e
++or
++.I \-\-errexit
++options, which will cause
++.B watch
++to exit if the return value from the program is non-zero.
++
+ .SH NOTE
+ Note that
+ .I command
+ is given to "sh \-c"
+ which means that you may need to use extra quoting to get the desired effect.
++You can disable this with the
++.I -x
++or
++.I --exec
++option, which passes the command to exec(2) instead.
+ .PP
+ Note that POSIX option processing is used (i.e., option processing stops at
+ the first non\-option argument). This means that flags after
+@@ -93,4 +115,5 @@
+ .B watch
+ was written by Tony Rems <rembo@unisoft.com> in 1991, with mods and
+ corrections by Francois Pinard. It was reworked and new features added by
+-Mike Coleman <mkc@acm.org> in 1999.
++Mike Coleman <mkc@acm.org> in 1999. The beep, exec, and error handling
++features were added by Morty Abzug <morty@frakir.org> in 2008.
+Index: b/watch.c
+===================================================================
+--- a/watch.c 2009-11-24 21:00:40.000000000 +1100
++++ b/watch.c 2009-11-24 21:00:43.000000000 +1100
+@@ -8,6 +8,7 @@
+ * Mike Coleman <mkc@acm.org>.
+ *
+ * Changes by Albert Cahalan, 2002-2003.
++ * stderr handling, exec, and beep option added by Morty Abzug, 2008
+ */
+
+ #define VERSION "0.2.0"
+@@ -35,13 +36,16 @@
+ {"differences", optional_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {"interval", required_argument, 0, 'n'},
++ {"beep", no_argument, 0, 'b'},
++ {"errexit", no_argument, 0, 'e'},
++ {"exec", no_argument, 0, 'x'},
+ {"no-title", no_argument, 0, 't'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+ };
+
+ static char usage[] =
+- "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
++ "Usage: %s [-bdhntvx] [--beep] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
+
+ static char *progname;
+
+@@ -140,28 +144,44 @@
+ int optc;
+ int option_differences = 0,
+ option_differences_cumulative = 0,
++ option_exec = 0,
++ option_beep = 0,
++ option_errexit = 0,
+ option_help = 0, option_version = 0;
+ double interval = 2;
+ char *command;
++ char **command_argv;
+ int command_length = 0; /* not including final \0 */
++ int pipefd[2];
++ int status;
++ pid_t child;
+
+ setlocale(LC_ALL, "");
+ progname = argv[0];
+
+- while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
++ while ((optc = getopt_long(argc, argv, "+bed::hn:vtx", longopts, (int *) 0))
+ != EOF) {
+ switch (optc) {
++ case 'b':
++ option_beep = 1;
++ break;
+ case 'd':
+ option_differences = 1;
+ if (optarg)
+ option_differences_cumulative = 1;
+ break;
++ case 'e':
++ option_errexit = 1;
++ break;
+ case 'h':
+ option_help = 1;
+ break;
+ case 't':
+ show_title = 0;
+ break;
++ case 'x':
++ option_exec = 1;
++ break;
+ case 'n':
+ {
+ char *str;
+@@ -191,18 +211,23 @@
+
+ if (option_help) {
+ fprintf(stderr, usage, progname);
++ fputs(" -b, --beep\t\t\t\tbeep if the command has a non-zero exit\n", stderr);
+ fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
+ fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
++ fputs(" -e, --errexit\t\t\t\texit watch if the command has a non-zero exit\n", stderr);
+ fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
+ fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
+ fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
+ fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr);
++ fputs(" -x, --exec\t\t\t\tpass command to exec instead of sh\n", stderr);
+ exit(0);
+ }
+
+ if (optind >= argc)
+ do_usage();
+
++ command_argv=&(argv[optind]); /* save for later */
++
+ command = strdup(argv[optind++]);
+ command_length = strlen(command);
+ for (; optind < argc; optind++) {
+@@ -261,11 +286,57 @@
+ free(header);
+ }
+
+- if (!(p = popen(command, "r"))) {
+- perror("popen");
++ /* allocate pipes */
++ if (pipe(pipefd)<0) {
++ perror("pipe");
++ do_exit(7);
++ }
++
++ /* flush stdout and stderr, since we're about to do fd stuff */
++ fflush(stdout);
++ fflush(stderr);
++
++ /* fork to prepare to run command */
++ child=fork();
++
++ if (child<0) { /* fork error */
++ perror("fork");
+ do_exit(2);
++ } else if (child==0) { /* in child */
++ close (pipefd[0]); /* child doesn't need read side of pipe */
++ close (1); /* prepare to replace stdout with pipe */
++ if (dup2 (pipefd[1], 1)<0) { /* replace stdout with write side of pipe */
++ perror("dup2");
++ exit(3);
++ }
++ dup2(1, 2); /* stderr should default to stdout */
++
++ if (option_exec) { /* pass command to exec instead of system */
++ if (execvp(command_argv[0], command_argv)==-1) {
++ perror("exec");
++ exit(4);
++ }
++ } else {
++ status=system(command); /* watch manpage promises sh quoting */
++
++ /* propagate command exit status as child exit status */
++ if (!WIFEXITED(status)) { /* child exits nonzero if command does */
++ exit(1);
++ } else {
++ exit(WEXITSTATUS(status));
++ }
++ }
++
++ }
++
++ /* otherwise, we're in parent */
++ close(pipefd[1]); /* close write side of pipe */
++ if ((p=fdopen(pipefd[0], "r"))==NULL) {
++ perror("fdopen");
++ do_exit(5);
+ }
+
++
+ for (y = show_title; y < height; y++) {
+ int eolseen = 0, tabpending = 0;
+ for (x = 0; x < width; x++) {
+@@ -313,7 +384,19 @@
+ oldeolseen = eolseen;
+ }
+
+- pclose(p);
++ fclose(p);
++
++ /* harvest child process and get status, propagated from command */
++ if (waitpid(child, &status, 0)<0) {
++ perror("waitpid");
++ do_exit(8);
++ };
++
++ /* if child process exited in error, beep if option_beep is set */
++ if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
++ if (option_beep) beep();
++ if (option_errexit) do_exit(8);
++ }
+
+ first_screen = 0;
+ refresh();
diff --git a/smartt-top/debian/patches/watch_precision_time.patch b/smartt-top/debian/patches/watch_precision_time.patch
new file mode 100644
index 0000000..8ed5095
--- /dev/null
+++ b/smartt-top/debian/patches/watch_precision_time.patch
@@ -0,0 +1,216 @@
+Description: Add precision wait time option -p
+Author: Anthony DeRobertis <asd@suespammers.org>
+Bug-Debian: http://bugs.debian.org/183486
+Reviewed-by: Craig Small <csmall@debian.org>
+Index: b/watch.1
+===================================================================
+--- a/watch.1 2009-11-24 21:00:43.000000000 +1100
++++ b/watch.1 2009-11-24 21:00:46.000000000 +1100
+@@ -4,7 +4,7 @@
+ .SH SYNOPSIS
+ .na
+ .B watch
+-.RB [ \-bdehvtx ]
++.RB [ \-bdehpvtx ]
+ .RB [ \-n
+ .IR seconds ]
+ .RB [ \-\-beep ]
+@@ -14,6 +14,7 @@
+ .RB [ \-\-help ]
+ .RB [ \-\-interval=\fIseconds\fP]
+ .RB [ \-\-no\-title ]
++.RB [ \-\-precise ]
+ .RB [ \-\-version ]
+ .I command
+ .SH DESCRIPTION
+@@ -27,7 +28,24 @@
+ .B \-n
+ or
+ .B \-\-interval
+-to specify a different interval.
++to specify a different interval. Normally, this interval is interpreted
++as the amout of time between the completion of one run of
++.I command
++and the beginning of the next run. However, with the
++.I \-p
++or
++.I \-\-precise
++option, you can make
++.BR watch
++attempt to run
++.I command
++every
++.I interval
++seconds. Try it with
++.B ntptime
++and notice how the fractional seconds stays
++(nearly) the same, as opposed to normal mode where they continuously
++increase.
+ .PP
+ The
+ .B \-d
+@@ -97,11 +115,21 @@
+ .br
+ watch echo "'"'$$'"'"
+ .PP
++To see the effect of precision time keeping, try adding
++.I \-p
++to
++.IP
++watch \-n 10 sleep 1
++.PP
+ You can watch for your administrator to install the latest kernel with
+ .IP
+ watch uname \-r
+ .PP
+-(Just kidding.)
++(Note that
++.I \-p
++isn't guaranteed to work across reboots, especially in the face of
++.B ntpdate
++or other bootup time-changing mechanisms)
+ .SH BUGS
+ Upon terminal resize, the screen will not be correctly repainted until the
+ next scheduled update. All
+@@ -110,6 +138,22 @@
+ .PP
+ Non-printing characters are stripped from program output. Use "cat -v" as
+ part of the command pipeline if you want to see them.
++.PP
++.I \-\-precise
++mode doesn't yet have advanced temporal distortion technology to
++compensate for a
++.I command
++that takes more than
++.I interval
++seconds to execute.
++.B watch
++also can get into a state where it rapid-fires as many executions of
++.I command
++as it can to catch up from a previous executions running longer than
++.I interval
++(for example,
++.B netstat
++taking ages on a DNS lookup).
+ .SH AUTHORS
+ The original
+ .B watch
+@@ -117,3 +161,7 @@
+ corrections by Francois Pinard. It was reworked and new features added by
+ Mike Coleman <mkc@acm.org> in 1999. The beep, exec, and error handling
+ features were added by Morty Abzug <morty@frakir.org> in 2008.
++On a not so dark and stormy morning
++in March of 2003, Anthony DeRobertis <asd@suespammers.org> got sick of
++his watches that should update every minute eventually updating many
++seconds after the minute started, and added microsecond precision.
+Index: b/watch.c
+===================================================================
+--- a/watch.c 2009-11-24 21:00:43.000000000 +1100
++++ b/watch.c 2009-11-24 21:00:46.000000000 +1100
+@@ -21,6 +21,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/ioctl.h>
++#include <sys/time.h>
+ #include <time.h>
+ #include <unistd.h>
+ #include <termios.h>
+@@ -39,13 +40,14 @@
+ {"beep", no_argument, 0, 'b'},
+ {"errexit", no_argument, 0, 'e'},
+ {"exec", no_argument, 0, 'x'},
++ {"precise", no_argument, 0, 'p'},
+ {"no-title", no_argument, 0, 't'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+ };
+
+ static char usage[] =
+- "Usage: %s [-bdhntvx] [--beep] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
++ "Usage: %s [-bdhnptvx] [--beep] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
+
+ static char *progname;
+
+@@ -54,6 +56,7 @@
+ static int screen_size_changed = 0;
+ static int first_screen = 1;
+ static int show_title = 2; // number of lines used, 2 or 0
++static int precise_timekeeping = 0;
+
+ #define min(x,y) ((x) > (y) ? (y) : (x))
+
+@@ -138,6 +141,15 @@
+ }
+ }
+
++/* get current time in usec */
++typedef unsigned long long watch_usec_t;
++#define USECS_PER_SEC (1000000ull)
++watch_usec_t get_time_usec() {
++ struct timeval now;
++ gettimeofday(&now, NULL);
++ return USECS_PER_SEC*now.tv_sec + now.tv_usec;
++}
++
+ int
+ main(int argc, char *argv[])
+ {
+@@ -152,6 +164,8 @@
+ char *command;
+ char **command_argv;
+ int command_length = 0; /* not including final \0 */
++ watch_usec_t next_loop; /* next loop time in us, used for precise time
++ keeping only */
+ int pipefd[2];
+ int status;
+ pid_t child;
+@@ -159,7 +173,7 @@
+ setlocale(LC_ALL, "");
+ progname = argv[0];
+
+- while ((optc = getopt_long(argc, argv, "+bed::hn:vtx", longopts, (int *) 0))
++ while ((optc = getopt_long(argc, argv, "+bed::hn:pvtx", longopts, (int *) 0))
+ != EOF) {
+ switch (optc) {
+ case 'b':
+@@ -194,6 +208,9 @@
+ interval = ~0u/1000000;
+ }
+ break;
++ case 'p':
++ precise_timekeeping = 1;
++ break;
+ case 'v':
+ option_version = 1;
+ break;
+@@ -217,6 +234,7 @@
+ fputs(" -e, --errexit\t\t\t\texit watch if the command has a non-zero exit\n", stderr);
+ fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
+ fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
++ fputs(" -p, --precise\t\t\t\tprecise timing, ignore command run time\n", stderr);
+ fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
+ fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr);
+ fputs(" -x, --exec\t\t\t\tpass command to exec instead of sh\n", stderr);
+@@ -256,6 +274,9 @@
+ noecho();
+ cbreak();
+
++ if (precise_timekeeping)
++ next_loop = get_time_usec();
++
+ for (;;) {
+ time_t t = time(NULL);
+ char *ts = ctime(&t);
+@@ -400,6 +421,12 @@
+
+ first_screen = 0;
+ refresh();
++ if (precise_timekeeping) {
++ watch_usec_t cur_time = get_time_usec();
++ next_loop += USECS_PER_SEC*interval;
++ if (cur_time < next_loop)
++ usleep(next_loop - cur_time);
++ } else
+ usleep(interval * 1000000);
+ }
+
diff --git a/smartt-top/debian/patches/watch_unicode.patch b/smartt-top/debian/patches/watch_unicode.patch
new file mode 100644
index 0000000..1012271
--- /dev/null
+++ b/smartt-top/debian/patches/watch_unicode.patch
@@ -0,0 +1,292 @@
+Description: Unicode/8bit for watch
+Bug-Debian: http://bugs.debian.org/240989
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/procps/+bug/318221
+Author: Jarrod Lowe <ubuntu@rrod.net>
+--- a/AUTHORS
++++ b/AUTHORS
+@@ -47,4 +47,5 @@
+ watch:
+ Tony Rems <rembo@unisoft.com>
+ Mike Coleman <mkc@acm.org>
++Jarrod Lowe <procps@rrod.net>
+
+--- a/Makefile
++++ b/Makefile
+@@ -68,6 +68,7 @@
+ _TARFILES := Makefile
+
+ CURSES := -lncurses
++CURSESW := -lncursesw
+
+ # This seems about right for the dynamic library stuff.
+ # Something like this is probably needed to make the SE Linux
+@@ -119,7 +120,7 @@
+ # Unlike the kernel one, this check_gcc goes all the way to
+ # producing an executable. There might be a -m64 that works
+ # until you go looking for a 64-bit curses library.
+-check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o /dev/null $(CURSES) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
++check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o /dev/null $(CURSES) $(CURSESW) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+ # Be 64-bit if at all possible. In a cross-compiling situation, one may
+ # do "make m64=-m32 lib64=lib" to produce 32-bit executables. DO NOT
+@@ -250,7 +251,7 @@
+ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES)
+
+ watch: % : %.o
+- $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES)
++ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSESW)
+
+ ############ progX --> progY
+
+--- a/watch.1
++++ b/watch.1
+@@ -139,6 +139,17 @@
+ Non-printing characters are stripped from program output. Use "cat -v" as
+ part of the command pipeline if you want to see them.
+ .PP
++Combining Characters that are supposed to display on the character at the
++last column on the screen may display one column early, or they may not
++display at all.
++.PP
++Combining Characters never count as different in
++.I \-\-differences
++mode. Only the base character counts.
++.PP
++Blank lines directly after a line which ends in the last column do not
++display.
++.PP
+ .I \-\-precise
+ mode doesn't yet have advanced temporal distortion technology to
+ compensate for a
+@@ -165,3 +176,4 @@
+ in March of 2003, Anthony DeRobertis <asd@suespammers.org> got sick of
+ his watches that should update every minute eventually updating many
+ seconds after the minute started, and added microsecond precision.
++Unicode support was added in 2009 by Jarrod Lowe <procps@rrod.net>.
+--- a/watch.c
++++ b/watch.c
+@@ -9,14 +9,16 @@
+ *
+ * Changes by Albert Cahalan, 2002-2003.
+ * stderr handling, exec, and beep option added by Morty Abzug, 2008
++ * Unicode Support added by Jarrod Lowe <procps@rrod.net> in 2009.
+ */
+
+-#define VERSION "0.2.0"
++#define VERSION "0.3.0"
+
++#include <wchar.h>
+ #include <ctype.h>
+ #include <getopt.h>
+ #include <signal.h>
+-#include <ncurses.h>
++#include <ncursesw/ncurses.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -27,6 +29,7 @@
+ #include <termios.h>
+ #include <locale.h>
+ #include "proc/procps.h"
++#include <errno.h>
+
+ #ifdef FORCE_8BIT
+ #undef isprint
+@@ -150,6 +153,32 @@
+ return USECS_PER_SEC*now.tv_sec + now.tv_usec;
+ }
+
++// read a wide character from a popen'd stream
++#define MAX_ENC_BYTES 16
++wint_t my_getwc(FILE *s);
++wint_t my_getwc(FILE *s) {
++ char i[MAX_ENC_BYTES]; //assuming no encoding ever consumes more than 16 bytes
++ int byte = 0;
++ int convert;
++ int x;
++ wchar_t rval;
++ while(1) {
++ i[byte] = getc(s);
++ if (i[byte]==EOF) { return WEOF; }
++ byte++;
++ errno = 0;
++ mbtowc(NULL, NULL, 0);
++ convert = mbtowc(&rval, i, byte);
++ x = errno;
++ if(convert > 0) { return rval; } //legal conversion
++ if(byte == MAX_ENC_BYTES) {
++ while(byte > 1) { ungetc(i[--byte], s); } //at least *try* to fix up
++ errno = -EILSEQ;
++ return WEOF;
++ }
++ }
++}
++
+ int
+ main(int argc, char *argv[])
+ {
+@@ -162,8 +191,11 @@
+ option_help = 0, option_version = 0;
+ double interval = 2;
+ char *command;
++ wchar_t *wcommand = NULL;
+ char **command_argv;
+ int command_length = 0; /* not including final \0 */
++ int wcommand_columns = 0; /* not including final \0 */
++ int wcommand_characters = 0; /* not including final \0 */
+ watch_usec_t next_loop; /* next loop time in us, used for precise time
+ keeping only */
+ int pipefd[2];
+@@ -259,6 +291,23 @@
+ command[command_length] = '\0';
+ }
+
++ // convert to wide for printing purposes
++ //mbstowcs(NULL, NULL, 0);
++ wcommand_characters = mbstowcs(NULL, command, 0);
++ if(wcommand_characters < 0) {
++ fprintf(stderr, "Unicode Handling Error\n");
++ exit(1);
++ }
++ wcommand = (wchar_t*)malloc((wcommand_characters+1) * sizeof(wcommand));
++ if(wcommand == NULL) {
++ fprintf(stderr, "Unicode Handling Error (malloc)\n");
++ exit(1);
++ }
++ mbstowcs(wcommand, command, wcommand_characters+1);
++ wcommand_columns = wcswidth(wcommand, -1);
++
++
++
+ get_terminal_size();
+
+ /* Catch keyboard interrupts so we can put tty back in a sane state. */
+@@ -298,12 +347,44 @@
+ if (show_title) {
+ // left justify interval and command,
+ // right justify time, clipping all to fit window width
+- asprintf(&header, "Every %.1fs: %.*s",
+- interval, min(width - 1, command_length), command);
+- mvaddstr(0, 0, header);
+- if (strlen(header) > (size_t) (width - tsl - 1))
+- mvaddstr(0, width - tsl - 4, "... ");
+- mvaddstr(0, width - tsl + 1, ts);
++
++ int hlen = asprintf(&header, "Every %.1fs: ", interval);
++
++ // the rules:
++ // width < tsl : print nothing
++ // width < tsl + hlen + 1: print ts
++ // width = tsl + hlen + 1: print header, ts
++ // width < tsl + hlen + 4: print header, ..., ts
++ // width < tsl + hlen + wcommand_columns: print header, truncated wcommand, ..., ts
++ // width > "": print header, wcomand, ts
++ // this is slightly different from how it used to be
++ if(width >= tsl) {
++ if(width >= tsl + hlen + 1) {
++ mvaddstr(0, 0, header);
++ if(width >= tsl + hlen + 2) {
++ if(width < tsl + hlen + 4) {
++ mvaddstr(0, width - tsl - 4, "... ");
++ }else{
++ if(width < tsl + hlen + wcommand_columns) {
++ // print truncated
++ int avail_columns = width - tsl - hlen;
++ int using_columns = wcommand_columns;
++ int using_characters = wcommand_characters;
++ while(using_columns > avail_columns - 4) {
++ using_characters--;
++ using_columns = wcswidth(wcommand, using_characters);
++ }
++ mvaddnwstr(0, hlen, wcommand, using_characters);
++ mvaddstr(0, width - tsl - 4, "... ");
++ }else{
++ mvaddwstr(0, hlen, wcommand);
++ }
++ }
++ }
++ }
++ mvaddstr(0, width - tsl + 1, ts);
++ }
++
+ free(header);
+ }
+
+@@ -360,47 +441,62 @@
+
+ for (y = show_title; y < height; y++) {
+ int eolseen = 0, tabpending = 0;
++ wint_t carry = WEOF;
+ for (x = 0; x < width; x++) {
+- int c = ' ';
++ wint_t c = L' ';
+ int attr = 0;
+
+ if (!eolseen) {
+ /* if there is a tab pending, just spit spaces until the
+ next stop instead of reading characters */
+ if (!tabpending)
+- do
+- c = getc(p);
+- while (c != EOF && !isprint(c)
+- && c != '\n'
+- && c != '\t');
+- if (c == '\n')
++ do {
++ if(carry == WEOF) {
++ c = my_getwc(p);
++ }else{
++ c = carry;
++ carry = WEOF;
++ }
++ }while (c != WEOF && !isprint(c) && c<128
++ && wcwidth(c) == 0
++ && c != L'\n'
++ && c != L'\t');
++ if (c == L'\n')
+ if (!oldeolseen && x == 0) {
+ x = -1;
+ continue;
+ } else
+ eolseen = 1;
+- else if (c == '\t')
++ else if (c == L'\t')
+ tabpending = 1;
+- if (c == EOF || c == '\n' || c == '\t')
+- c = ' ';
++ if (x==width-1 && wcwidth(c)==2) {
++ y++;
++ x = -1; //process this double-width
++ carry = c; //character on the next line
++ continue; //because it won't fit here
++ }
++ if (c == WEOF || c == L'\n' || c == L'\t')
++ c = L' ';
+ if (tabpending && (((x + 1) % 8) == 0))
+ tabpending = 0;
+ }
+ move(y, x);
+ if (option_differences) {
+- chtype oldch = inch();
+- unsigned char oldc = oldch & A_CHARTEXT;
++ cchar_t oldc;
++ in_wch(&oldc);
+ attr = !first_screen
+- && ((char)c != oldc
++ && ((wchar_t)c != oldc.chars[0]
+ ||
+ (option_differences_cumulative
+- && (oldch & A_ATTRIBUTES)));
++ && (oldc.attr & A_ATTRIBUTES)));
+ }
+ if (attr)
+ standout();
+- addch(c);
++ addnwstr((wchar_t*)&c,1);
+ if (attr)
+ standend();
++ if(wcwidth(c) == 0) { x--; }
++ if(wcwidth(c) == 2) { x++; }
+ }
+ oldeolseen = eolseen;
+ }
diff --git a/smartt-top/debian/postinst b/smartt-top/debian/postinst
new file mode 100644
index 0000000..05c58c8
--- /dev/null
+++ b/smartt-top/debian/postinst
@@ -0,0 +1,104 @@
+#!/bin/sh
+# postinst script for procps
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+#
+# quoting from the policy:
+# Any necessary prompting should almost always be confined to the
+# post-installation script, and should be protected with a conditional
+# so that unnecessary prompting doesn't happen if a package's
+# installation fails and the `postinst' is called with `abort-upgrade',
+# `abort-remove' or `abort-deconfigure'.
+
+# Move a conffile without triggering a dpkg question
+mv_conffile() {
+ OLDCONFFILE="$1"
+ NEWCONFFILE="$2"
+
+ if [ -e "$OLDCONFFILE" ]; then
+ echo "Preserving user changes to $NEWCONFFILE ..."
+ mv -f "$NEWCONFFILE" "$NEWCONFFILE".dpkg-new
+ mv -f "$OLDCONFFILE" "$NEWCONFFILE"
+ fi
+}
+
+# update alternative, if it exists
+check_alternatives() {
+ BINNAME="$1"
+ BINPATH="$2"
+ MANSEC="$3"
+ if [ -e "$BINPATH"/"$BINNAME".procps ] ; then
+ update-alternatives --install "$BINPATH"/"$BINNAME" "$BINNAME" \
+ "$BINPATH"/"$BINNAME".procps 50 \
+ --slave /usr/share/man/man"$MANSEC"/"$BINNAME"."$MANSEC".gz "$BINNAME"."$MANSEC".gz \
+ /usr/share/man/man"$MANSEC"/"$BINNAME".procps."$MANSEC".gz
+ fi
+}
+case "$1" in
+ configure|abort-remove|abort-deconfigure)
+ if [ -e /etc/psdevtab ] ; then
+ rm -f /etc/psdevtab
+ fi
+ if [ -e /etc/psdatabase ]
+ then
+ rm -f /etc/psdatabase
+ fi
+ # Remove old procps init.d script, if it exists Closes: #55137
+ if [ -L /etc/rcS.d/S30procps.sh ]
+ then
+ update-rc.d -f procps.sh remove >/dev/null
+ fi
+ # and if that didn't work Closes: #92184 (#234306 with -L )
+ if [ -L /etc/rcS.d/S30procps.sh ]
+ then
+ rm -f /etc/rcS.d/S30procps.sh
+ fi
+ # Remove moved procps.sh file, if it is there
+ if dpkg --compare-versions "$2" le "1:3.2.7-3"; then
+ mv_conffile "/etc/init.d/procps.sh" "/etc/init.d/procps"
+ fi
+
+ #
+ # Now to do the alternatives for w
+ update-alternatives --install /usr/bin/w w /usr/bin/w.procps 50 \
+ --slave /usr/share/man/man1/w.1.gz w.1.gz /usr/share/man/man1/w.procps.1.gz
+ # Do alternatives for uptime kill vmstat and ps, if required
+ check_alternatives "uptime" "/usr/bin" "1"
+ check_alternatives "kill" "/usr/bin" "1"
+ check_alternatives "vmstat" "/usr/bin" "8"
+ check_alternatives "ps" "/bin" "1"
+
+ start procps
+ ;;
+
+ abort-upgrade)
+ # Nothing to do
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/smartt-top/debian/postrm b/smartt-top/debian/postrm
new file mode 100644
index 0000000..47743f2
--- /dev/null
+++ b/smartt-top/debian/postrm
@@ -0,0 +1,51 @@
+#!/bin/sh
+# postrm script for procps
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+restoreoldconffile()
+{
+ v=$1
+ f=/etc/init.d/procps
+ c=1:3.2.7-5
+ [ ! -e "$f" ] && return
+ dpkg --compare-versions "$1" ge "$c" || return
+ mv "$f" "$f.sh"
+}
+
+case "$1" in
+ abort-upgrade)
+ restoreoldconffile
+ ;;
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/smartt-top/debian/preinst b/smartt-top/debian/preinst
new file mode 100644
index 0000000..f29824b
--- /dev/null
+++ b/smartt-top/debian/preinst
@@ -0,0 +1,72 @@
+#!/bin/sh
+# preinst script for procps
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+prep_mv_conffile()
+{
+ PKGNAME=$1
+ CONFFILE=$2
+
+ if [ -e "$CONFFILE" ]; then
+ md5sum="`md5sum \"$CONFFILE\" | sed -e \"s/ .*//\"`"
+ old_md5sum="`dpkg-query -W -f='${Conffiles}' $PKGNAME | sed -n -e \"\\\\' $CONFFILE'{s/ obsolete$//;s/.* //p}\"`"
+ if [ "$md5sum" = "$old_md5sum" ]; then
+ rm -f "$CONFFILE"
+ fi
+ fi
+}
+
+upgradeoldconffile()
+{
+ f=/etc/init.d/procps.sh
+ [ ! -e "$f" ] && return
+
+ curmd5=`md5sum "$f" |awk '{print $1}'`
+ oldmd5=`dpkg-query -W -f='${Conffiles}' procps | sed "{\\'^ $f ' ! d; s///}"`
+ [ "$curmd5" = "$oldmd5" ] && {
+ # The admin has not modified $f
+ echo "Preparing to remove obsolete, unmodified conffile: $f"
+ mv -fv "$f" "$f.from-preinst"
+ return
+ } >&2
+
+ # The admim modified $f; cause a deliberate conffile prompt
+ echo "Moving obsolete conffile to new pathname:"
+ mv -fv "$f" "${f%.sh}"
+}
+
+
+case "$1" in
+ install)
+ ;;
+ upgrade)
+ prep_mv_conffile procps "/etc/init.d/procps.sh"
+ ;;
+ abort-upgrade)
+ ;;
+
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/smartt-top/debian/prerm b/smartt-top/debian/prerm
new file mode 100644
index 0000000..cf65fc3
--- /dev/null
+++ b/smartt-top/debian/prerm
@@ -0,0 +1,15 @@
+#!/bin/sh
+set -e
+
+case "$1" in
+ remove|deconfigure)
+ update-alternatives --remove w /usr/bin/w.procps
+ ;;
+ upgrade|failed-upgrade)
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
diff --git a/smartt-top/debian/procps.bug-presubj b/smartt-top/debian/procps.bug-presubj
new file mode 100644
index 0000000..c736edf
--- /dev/null
+++ b/smartt-top/debian/procps.bug-presubj
@@ -0,0 +1,5 @@
+If you are reporting on the kill program please make sure you are running
+/bin/kill and not the shell built-in kill program (which is usually the
+default one used).
+
+ps WILL print UIDs instead of usernames if they are longer than 8 characters.
diff --git a/smartt-top/debian/procps.init.linux b/smartt-top/debian/procps.init.linux
new file mode 100644
index 0000000..f20bd8c
--- /dev/null
+++ b/smartt-top/debian/procps.init.linux
@@ -0,0 +1,58 @@
+#! /bin/sh
+# /etc/init.d/procps: Set kernel variables from /etc/sysctl.conf
+#
+# written by Elrond <Elrond@Wunder-Nett.org>
+
+### BEGIN INIT INFO
+# Provides: procps
+# Required-Start: mountkernfs $local_fs
+# Required-Stop:
+# Should-Start: udev module-init-tools
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Configure kernel parameters at boottime
+# Description: Loads kernel parameters that are specified in /etc/sysctl.conf
+### END INIT INFO
+
+PATH=/sbin:/bin
+
+SYSCTL=/sbin/sysctl
+
+test -x $SYSCTL || exit 0
+
+. /lib/lsb/init-functions
+
+# Comment this out for sysctl to print every item changed
+QUIET_SYSCTL="-q"
+
+# Check for existance of the default file and exit if not there,
+# Closes #52839 for the boot-floppy people
+if [ -f /etc/default/rcS ] ; then
+ . /etc/default/rcS
+fi
+
+set -e
+
+case "$1" in
+ start|restart|force-reload)
+ log_action_begin_msg "Setting kernel variables "
+ STATUS=0
+ for file in /etc/sysctl.conf /etc/sysctl.d/*.conf ; do
+ if [ -r "$file" ] ; then
+ if [ "$VERBOSE" = "yes" ] ; then
+ log_action_cont_msg " $file"
+ fi
+ $SYSCTL $QUIET_SYSCTL -p "$file" || STATUS=$?
+ fi
+ done
+ log_action_end_msg $STATUS
+ ;;
+ stop)
+ ;;
+ *)
+ echo "Usage: /etc/init.d/procps {start|stop|restart|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+exit 0
diff --git a/smartt-top/debian/procps.install b/smartt-top/debian/procps.install
new file mode 100644
index 0000000..6e1ede6
--- /dev/null
+++ b/smartt-top/debian/procps.install
@@ -0,0 +1,8 @@
+# Files to install for non-kfreebsd and non-hurd systems
+# I think that just means linux
+debian/sysctl.conf etc
+etc/sysctl.d/*
+proc/libproc-3.2.8.so lib
+bin/*
+sbin/sysctl
+usr/bin/*
diff --git a/smartt-top/debian/procps.install.hurd b/smartt-top/debian/procps.install.hurd
new file mode 100644
index 0000000..0bcdeda
--- /dev/null
+++ b/smartt-top/debian/procps.install.hurd
@@ -0,0 +1,5 @@
+# Files to install for non-kfreebsd and non-hurd systems
+# I think that just means linux
+proc/libproc-3.2.8.so lib
+bin/*
+usr/bin/*
diff --git a/smartt-top/debian/procps.install.kfreebsd b/smartt-top/debian/procps.install.kfreebsd
new file mode 100644
index 0000000..0bcdeda
--- /dev/null
+++ b/smartt-top/debian/procps.install.kfreebsd
@@ -0,0 +1,5 @@
+# Files to install for non-kfreebsd and non-hurd systems
+# I think that just means linux
+proc/libproc-3.2.8.so lib
+bin/*
+usr/bin/*
diff --git a/smartt-top/debian/procps.links b/smartt-top/debian/procps.links
new file mode 100644
index 0000000..839ded8
--- /dev/null
+++ b/smartt-top/debian/procps.links
@@ -0,0 +1,2 @@
+usr/bin/pgrep usr/bin/pkill
+usr/bin/skill usr/bin/snice
diff --git a/smartt-top/debian/procps.lintian-overrides b/smartt-top/debian/procps.lintian-overrides
new file mode 100644
index 0000000..95641b2
--- /dev/null
+++ b/smartt-top/debian/procps.lintian-overrides
@@ -0,0 +1 @@
+procps: package-name-doesnt-match-sonames
diff --git a/smartt-top/debian/procps.manpages b/smartt-top/debian/procps.manpages
new file mode 100644
index 0000000..d1182c5
--- /dev/null
+++ b/smartt-top/debian/procps.manpages
@@ -0,0 +1,2 @@
+debian/tmp/usr/share/man/man?/*
+debian/w.de.1
diff --git a/smartt-top/debian/procps.menu b/smartt-top/debian/procps.menu
new file mode 100644
index 0000000..ea7844d
--- /dev/null
+++ b/smartt-top/debian/procps.menu
@@ -0,0 +1,4 @@
+?package(procps):needs="text" section="Applications/System/Monitoring" \
+ title="Top" \
+ description="Display Linux tasks" \
+ command="/usr/bin/top"
diff --git a/smartt-top/debian/rules b/smartt-top/debian/rules
new file mode 100755
index 0000000..02cf158
--- /dev/null
+++ b/smartt-top/debian/rules
@@ -0,0 +1,109 @@
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+PACKAGE="procps"
+DEBROOT=$(CURDIR)/debian/tmp
+
+DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
+DEB_HOST_ARCH=$(shell dpkg-architecture -qDEB_HOST_ARCH)
+DEB_HOST_GNU_TYPE=$(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE=$(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE))
+CROSS=CC=$(DEB_HOST_GNU_TYPE)-gcc
+else
+CROSS=
+endif
+
+PROCLIB = $(shell basename proc/libproc-*.so)
+CFLAGS=-g
+
+%:
+ dh $@
+
+override_dh_auto_clean:
+ dh_auto_clean
+ if [ -d static ] ; then rm -rf static ; fi
+
+override_dh_auto_build:
+ dh_testdir
+
+ [ -d static ] || mkdir static
+ $(MAKE) $(CROSS) lib64=lib m64= SHARED=0 CFLAGS="$(CFLAGS)" proc/libproc.a
+ mv proc/libproc.a static
+ $(MAKE) clean
+ $(MAKE) $(CROSS) W_SHOWFROM=\-DW_SHOWFROM lib64=lib m64= CFLAGS="$(CFLAGS)"
+ ( cd static && ln -s /lib/$(PROCLIB) libproc.so )
+
+ touch build-stamp
+
+override_dh_auto_install:
+ $(MAKE) $(CROSS) lib64=lib ln_f="ln -sf" ldconfig=echo DESTDIR=$(DEBROOT) install
+
+ # Build up sysctl.d
+ install -d $(DEBROOT)/etc/sysctl.d/
+ install --mode 644 -o root -g root debian/sysctl.d/*.conf debian/sysctl.d/README $(DEBROOT)/etc/sysctl.d/
+ifneq (,$(wildcard debian/sysctl.d/*.conf.$(DEB_HOST_ARCH)))
+ # If a non-arch-specific default exists, install the arch-specific
+ # version of the conf in place of it, otherwise, build up a general
+ # 10-arch-specific.conf file.
+ for archconf in debian/sysctl.d/*.conf.$(DEB_HOST_ARCH); do \
+ conf=$$(basename $$archconf .$(DEB_HOST_ARCH)); \
+ if [ -f debian/sysctl.d/$$conf ]; then \
+ install --mode 644 -o root -g root $$archconf $(DEBROOT)/etc/sysctl.d/$$conf; \
+ else \
+ cat $$archconf >> $(DEBROOT)/etc/sysctl.d/10-arch-specific.conf; \
+ fi; \
+ done
+endif
+
+ # Rename w as there are two of them
+ (cd $(DEBROOT)/usr/bin && mv w w.procps )
+ (cd $(DEBROOT)/usr/share/man/man1 && mv w.1 w.procps.1 )
+
+ifneq ($(DEB_HOST_ARCH_OS), linux)
+ rm -f \
+ $(DEBROOT)/usr/bin/slabtop \
+ $(DEBROOT)/usr/share/man/man1/slabtop.1 \
+ $(DEBROOT)/sbin/sysctl \
+ $(DEBROOT)/usr/share/man/man8/sysctl.8 \
+ $(DEBROOT)/usr/share/man/man5/sysctl.conf.5 \
+ $(NULL)
+endif
+ifeq ($(DEB_HOST_ARCH_OS), kfreebsd)
+ rm -f \
+ $(DEBROOT)/bin/kill \
+ $(DEBROOT)/usr/share/man/man1/kill.1 \
+ $(NULL)
+endif
+ifeq ($(DEB_HOST_ARCH_OS), hurd)
+ rm -f \
+ $(DEBROOT)/usr/bin/pmap \
+ $(DEBROOT)/usr/share/man/man1/pmap.1 \
+ $(NULL)
+ # Rename kill as there are two of them
+ (cd $(DEBROOT)/bin && mv kill kill.procps )
+ (cd $(DEBROOT)/usr/share/man/man1 && mv kill.1 kill.procps.1 )
+
+ # Rename vmstat as there are two of them
+ (cd $(DEBROOT)/usr/bin && mv vmstat vmstat.procps )
+ (cd $(DEBROOT)/usr/share/man/man8 && mv vmstat.8 vmstat.procps.8 )
+
+ # Rename uptime as there are two of them
+ (cd $(DEBROOT)/usr/bin && mv uptime uptime.procps )
+ (cd $(DEBROOT)/usr/share/man/man1 && mv uptime.1 uptime.procps.1 )
+
+ # Rename ps as there are two of them
+ (cd $(DEBROOT)/bin && mv ps ps.procps )
+ (cd $(DEBROOT)/usr/share/man/man1 && mv ps.1 ps.procps.1 )
+endif
+
+override_dh_installchangelogs:
+ dh_installchangelogs NEWS
+
+override_dh_installinit:
+ifeq ($(DEB_HOST_ARCH_OS), linux)
+ dh_installinit --no-start --update-rcd-params='start 17 S .'
+endif
+
diff --git a/smartt-top/debian/source/format b/smartt-top/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/smartt-top/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/smartt-top/debian/sysctl.conf b/smartt-top/debian/sysctl.conf
new file mode 100644
index 0000000..d9acd92
--- /dev/null
+++ b/smartt-top/debian/sysctl.conf
@@ -0,0 +1,60 @@
+#
+# /etc/sysctl.conf - Configuration file for setting system variables
+# See /etc/sysctl.d/ for additional system variables.
+# See sysctl.conf (5) for information.
+#
+
+#kernel.domainname = example.com
+
+# Uncomment the following to stop low-level messages on console
+#kernel.printk = 3 4 1 3
+
+##############################################################3
+# Functions previously found in netbase
+#
+
+# Uncomment the next two lines to enable Spoof protection (reverse-path filter)
+# Turn on Source Address Verification in all interfaces to
+# prevent some spoofing attacks
+#net.ipv4.conf.default.rp_filter=1
+#net.ipv4.conf.all.rp_filter=1
+
+# Uncomment the next line to enable TCP/IP SYN cookies
+# See http://lwn.net/Articles/277146/
+# Note: This may impact IPv6 TCP sessions too
+#net.ipv4.tcp_syncookies=1
+
+# Uncomment the next line to enable packet forwarding for IPv4
+#net.ipv4.ip_forward=1
+
+# Uncomment the next line to enable packet forwarding for IPv6
+# Enabling this option disables Stateless Address Autoconfiguration
+# based on Router Advertisements for this host
+#net.ipv6.conf.all.forwarding=1
+
+
+###################################################################
+# Additional settings - these settings can improve the network
+# security of the host and prevent against some network attacks
+# including spoofing attacks and man in the middle attacks through
+# redirection. Some network environments, however, require that these
+# settings are disabled so review and enable them as needed.
+#
+# Do not accept ICMP redirects (prevent MITM attacks)
+#net.ipv4.conf.all.accept_redirects = 0
+#net.ipv6.conf.all.accept_redirects = 0
+# _or_
+# Accept ICMP redirects only for gateways listed in our default
+# gateway list (enabled by default)
+# net.ipv4.conf.all.secure_redirects = 1
+#
+# Do not send ICMP redirects (we are not a router)
+#net.ipv4.conf.all.send_redirects = 0
+#
+# Do not accept IP source route packets (we are not a router)
+#net.ipv4.conf.all.accept_source_route = 0
+#net.ipv6.conf.all.accept_source_route = 0
+#
+# Log Martian Packets
+#net.ipv4.conf.all.log_martians = 1
+#
diff --git a/smartt-top/debian/sysctl.d/10-console-messages.conf b/smartt-top/debian/sysctl.d/10-console-messages.conf
new file mode 100644
index 0000000..8402cf4
--- /dev/null
+++ b/smartt-top/debian/sysctl.d/10-console-messages.conf
@@ -0,0 +1,3 @@
+
+# the following stops low-level messages on console
+kernel.printk = 4 4 1 7
diff --git a/smartt-top/debian/sysctl.d/10-keyboard.conf.powerpc b/smartt-top/debian/sysctl.d/10-keyboard.conf.powerpc
new file mode 100644
index 0000000..fad6361
--- /dev/null
+++ b/smartt-top/debian/sysctl.d/10-keyboard.conf.powerpc
@@ -0,0 +1,6 @@
+
+# PowerPC:
+# Emulate the middle mouse button with F11 and the right with F12.
+dev.mac_hid.mouse_button_emulation = 1
+dev.mac_hid.mouse_button2_keycode = 87
+dev.mac_hid.mouse_button3_keycode = 88
diff --git a/smartt-top/debian/sysctl.d/10-network-security.conf b/smartt-top/debian/sysctl.d/10-network-security.conf
new file mode 100644
index 0000000..40bb134
--- /dev/null
+++ b/smartt-top/debian/sysctl.d/10-network-security.conf
@@ -0,0 +1,12 @@
+
+# Turn on Source Address Verification in all interfaces to
+# prevent some spoofing attacks.
+net.ipv4.conf.default.rp_filter=1
+net.ipv4.conf.all.rp_filter=1
+
+# Turn on SYN-flood protections. Starting with 2.6.26, there is no loss
+# of TCP functionality/features under normal conditions. When flood
+# protections kick in under high unanswered-SYN load, the system
+# should remain more stable, with a trade off of some loss of TCP
+# functionality/features (e.g. TCP Window scaling).
+net.ipv4.tcp_syncookies=1
diff --git a/smartt-top/debian/sysctl.d/10-ptrace.conf b/smartt-top/debian/sysctl.d/10-ptrace.conf
new file mode 100644
index 0000000..bb39c6b
--- /dev/null
+++ b/smartt-top/debian/sysctl.d/10-ptrace.conf
@@ -0,0 +1,22 @@
+# The PTRACE system is used for debugging. With it, a single user process
+# can attach to any other dumpable process owned by the same user. In the
+# case of malicious software, it is possible to use PTRACE to access
+# credentials that exist in memory (re-using existing SSH connections,
+# extracting GPG agent information, etc).
+#
+# A PTRACE scope of "0" is the more permissive mode. A scope of "1" limits
+# PTRACE only to direct child processes (e.g. "gdb name-of-program" and
+# "strace -f name-of-program" work, but gdb's "attach" and "strace -fp $PID"
+# do not). The PTRACE scope is ignored when a user has CAP_SYS_PTRACE, so
+# "sudo strace -fp $PID" will work as before. For more details see:
+# https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace
+#
+# For applications launching crash handlers that need PTRACE, exceptions can
+# be registered by the debugee by declaring in the segfault handler
+# specifically which process will be using PTRACE on the debugee:
+# prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);
+#
+# In general, PTRACE is not needed for the average running Ubuntu system.
+# To that end, the default is to set the PTRACE scope to "1". This value
+# may not be appropriate for developers or servers with only admin accounts.
+kernel.yama.ptrace_scope = 1
diff --git a/smartt-top/debian/sysctl.d/10-zeropage.conf b/smartt-top/debian/sysctl.d/10-zeropage.conf
new file mode 100644
index 0000000..d2faee6
--- /dev/null
+++ b/smartt-top/debian/sysctl.d/10-zeropage.conf
@@ -0,0 +1,9 @@
+# Protect the zero page of memory from userspace mmap to prevent kernel
+# NULL-dereference attacks against potential future kernel security
+# vulnerabilities. (Added in kernel 2.6.23.)
+#
+# While this default is built into the Ubuntu kernel, there is no way to
+# restore the kernel default if the value is changed during runtime; for
+# example via package removal (e.g. wine, dosemu). Therefore, this value
+# is reset to the secure default each time the sysctl values are loaded.
+vm.mmap_min_addr = 65536
diff --git a/smartt-top/debian/sysctl.d/10-zeropage.conf.armel b/smartt-top/debian/sysctl.d/10-zeropage.conf.armel
new file mode 100644
index 0000000..f8020e2
--- /dev/null
+++ b/smartt-top/debian/sysctl.d/10-zeropage.conf.armel
@@ -0,0 +1,11 @@
+# Protect the zero page of memory from userspace mmap to prevent kernel
+# NULL-dereference attacks against potential future kernel security
+# vulnerabilities. (Added in kernel 2.6.23.)
+#
+# While this default is built into the Ubuntu kernel, there is no way to
+# restore the kernel default if the value is changed during runtime; for
+# example via package removal (e.g. wine, dosemu). Therefore, this value
+# is reset to the secure default each time the sysctl values are loaded.
+#
+# ARM-specific default:
+vm.mmap_min_addr = 32768
diff --git a/smartt-top/debian/sysctl.d/README b/smartt-top/debian/sysctl.d/README
new file mode 100644
index 0000000..8bf221d
--- /dev/null
+++ b/smartt-top/debian/sysctl.d/README
@@ -0,0 +1,8 @@
+This directory contains settings similar to those found in /etc/sysctl.conf.
+In general, files in the 10-*.conf range come from the procps package and
+serve as system defaults. Other packages install their files in the
+30-*.conf range, to override system defaults. End-users can use 60-*.conf
+and above, or use /etc/sysctl.conf directly, which overrides anything in
+this directory.
+
+After making any changes, please run "start procps".
diff --git a/smartt-top/debian/upstart b/smartt-top/debian/upstart
new file mode 100644
index 0000000..a13e97e
--- /dev/null
+++ b/smartt-top/debian/upstart
@@ -0,0 +1,13 @@
+# procps - set sysctls from /etc/sysctl.conf
+#
+# This task sets kernel sysctl variables from /etc/sysctl.conf and
+# /etc/sysctl.d
+
+description "set sysctls from /etc/sysctl.conf"
+
+start on virtual-filesystems
+
+task
+script
+ cat /etc/sysctl.d/*.conf /etc/sysctl.conf | sysctl -e -p -
+end script
diff --git a/smartt-top/debian/w.de.1 b/smartt-top/debian/w.de.1
new file mode 100644
index 0000000..ff1811b
--- /dev/null
+++ b/smartt-top/debian/w.de.1
@@ -0,0 +1,73 @@
+.\" -*-Nroff-*-
+.\"
+.TH W 1 "8 Dez 1993 " " " "Linux Benutzerhandbuch"
+.SH NAME
+w \- Zeigt an, wer am System angemeldet ist und was sie/er gerade tut.
+.SH SYNTAX
+.B w
+.RB [ \-husfVo ]
+.RI [ Benutzer ]
+.SH BESCHREIBUNG
+.B w
+zeigt Informationen über die gerade eingeloggten Benutzer und ihre Prozesse an. Die Kopfzeile enthält die aktuelle Zeit, die Zeit, seit der das System läuft, wie viele Benutzer gerade angemeldet sind und die durchschnittliche Systemlast der letzten 1, 5 und 15 Minuten.
+
+Die folgenden Daten werden für jeden Benutzer angezeigt:
+Der Login-Name, der tty-Name, der entfernte Host, die Login-Zeit, die Leerlaufzeit, JCPU , PCPU und die Kommandozeile des laufenden Prozesses.
+
+Die JCPU-Zeit ist die Zeit, die von allen Prozessen genutzt wurde, die an dem jeweiligen Terminal angemeldet sind. Sie enthält keine abgeschlossenen Hintergrund-Aufträge, jedoch die derzeit laufenden Hintergrund-Aufträge.
+
+Die PCPU-Zeit ist die Zeit, die vom derzeit laufenden Prozess bisher genutzt wurde.
+
+.PP
+.SH "OPTIONEN"
+.TP 0.5i
+.B "\-h "
+Die Kopfzeile nicht ausgegeben.
+.TP 0.5i
+.B "\-u "
+Ignoriert den Benutzernamen während der aktuelle Prozess und die CPU-Zeiten ermittelt werden. Probieren Sie die Option aus, indem Sie zunächst "su", danach "w" und "w \-u" eingeben.
+.TP 0.5i
+.B "\-s "
+Das kurze Ausgabeformat verwenden.
+Den Anmeldenamen, die JCPU und PCPU nicht ausgeben.
+.TP 0.5i
+.B "\-f "
+Umschalten des
+.B from
+(remote host) Feldes. Die Standardeinstellung ist, das
+.B from
+Feld nicht auszugeben. Evtl. hat ihr Systemadministrator oder Paketbetreuer jedoch auch eine Version kompiliert, in der das
+.B from
+Feld standardmäßig angezeigt wird.
+.TP 0.5i
+.B "\-V "
+Versionsinformation anzeigen.
+.TP 0.5i
+.B "\-o "
+Altes Ausgabeformat. Druckt Leerzeichen für Leerlaufzeiten unter einer Minute.
+.TP 0.5i
+.B "Benutzer "
+Zeigt nur Informationen über den angegebenen Benutzer an.
+
+.SH DATEIEN
+.TP
+.I /var/run/utmp
+Informationen darüber, wer gerade angemeldet ist
+.TP
+.I /proc
+Prozessinformationen
+.PP
+
+.SH "SIEHE AUCH"
+.BR free (1),
+.BR ps (1),
+.BR top (1),
+.BR uptime (1),
+.BR utmp (5),
+.BR who (1)
+
+.SH AUTOREN
+.B w
+ist von Charles Blake fast vollständig neu geschrieben worden. Die ursprüngliche Version wurde von Larry Greenfield <greenfie@gauss.rutgers.edu> und Michael K. Johnson <johnsonm@redhat.com> geschrieben.
+
+Berichten Sie Fehler an <albert@users.sf.net> \ No newline at end of file
diff --git a/smartt-top/debian/watch b/smartt-top/debian/watch
new file mode 100644
index 0000000..26cbf05
--- /dev/null
+++ b/smartt-top/debian/watch
@@ -0,0 +1,6 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# Site Directory Pattern Version Script
+version=3
+http://procps.sourceforge.net/download.html procps-(.*)\.tar\.gz
diff --git a/smartt-top/dummy.c b/smartt-top/dummy.c
new file mode 100644
index 0000000..95e7824
--- /dev/null
+++ b/smartt-top/dummy.c
@@ -0,0 +1,31 @@
+// This is to test the compiler.
+
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Foul POS defines all sorts of stuff...
+#include <term.h>
+#undef tab
+
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <values.h>
+
+int main(int argc, char *argv[]){
+ (void)argc;
+ (void)argv;
+ return 0;
+}
diff --git a/smartt-top/free.1 b/smartt-top/free.1
new file mode 100644
index 0000000..cae1241
--- /dev/null
+++ b/smartt-top/free.1
@@ -0,0 +1,67 @@
+.\" -*-Nroff-*-
+.\" This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu.
+.\" Freely distributable under the terms of the GPL
+.TH FREE 1 "5 Oct 2009 " "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+free \- Display amount of free and used memory in the system
+.SH SYNOPSIS
+.B free
+.RB [ \-b | \-k | \-m | \-g ]
+.RB [ \-c
+.IR count ]
+.RB [ \-l ]
+.RB [ \-o ]
+.RB [ \-t ]
+.RB [ \-s
+.IR delay ]
+.RB [ \-V ]
+.SH DESCRIPTION
+\fBfree\fP displays the total amount of free and used physical and swap
+memory in the system, as well as the buffers used by the kernel.
+The shared memory column should be ignored; it is obsolete.
+.SS OPTIONS
+.TP
+\fB\-b\fR
+Display the amount of memory in bytes.
+.TP
+\fB\-c\fR \fIcount\fR
+Display the result \fIcount\fR times. Requires the \fB\-s\fR option.
+.TP
+\fB\-g\fR
+Display the amount of memory in gigabytes.
+.TP
+\fB\-k\fR
+Display the amount of memory in kilobytes. This is the default.
+.TP
+\fB\-l\fR
+Show detailed low and high memory statistics.
+.TP
+\fB\-m\fR
+Display the amount of memory in megabytes.
+.TP
+\fB\-o\fR
+Display the output in old format, the only difference being this option
+will disable the display of the "buffer adjusted" line.
+.TP
+\fB\-s\fR
+Continuously display the result \fIdelay\fP seconds apart. You
+may actually specify any floating point number for \fIdelay\fP,
+.BR usleep (3)
+is used for microsecond resolution delay times.
+.TP
+\fB\-t\fR
+Display a line showing the column totals.
+.TP
+\fB\-V\fR
+Display version information.
+.SH FILES
+.nf
+/proc/meminfo memory information
+.fi
+.SH AUTHORS
+Written by Brian Edmonds.
+
+Send bug reports to <albert@users.sf.net>
+.SH "SEE ALSO"
+.BR ps "(1), " slabtop "(1), " top "(1), " vmstat (8).
+.\"{{{}}}
diff --git a/smartt-top/free.c b/smartt-top/free.c
new file mode 100644
index 0000000..bd78f02
--- /dev/null
+++ b/smartt-top/free.c
@@ -0,0 +1,122 @@
+// free.c - free(1)
+// procps utility to display free memory information
+//
+// All new, Robert Love <rml@tech9.net> 18 Nov 2002
+// Original by Brian Edmonds and Rafal Maszkowski 14 Dec 1992
+//
+// This program is licensed under the GNU Library General Public License, v2
+//
+// Copyright 2003 Robert Love
+// Copyright 2004 Albert Cahalan
+
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+//#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define S(X) ( ((unsigned long long)(X) << 10) >> shift)
+
+const char help_message[] =
+"usage: free [-b|-k|-m|-g] [-l] [-o] [-t] [-s delay] [-c count] [-V]\n"
+" -b,-k,-m,-g show output in bytes, KB, MB, or GB\n"
+" -l show detailed low and high memory statistics\n"
+" -o use old format (no -/+buffers/cache line)\n"
+" -t display total for RAM + swap\n"
+" -s update every [delay] seconds\n"
+" -c update [count] times\n"
+" -V display version information and exit\n"
+;
+
+int main(int argc, char *argv[]){
+ int i;
+ int count = 0;
+ int shift = 10;
+ int pause_length = 0;
+ int show_high = 0;
+ int show_total = 0;
+ int old_fmt = 0;
+
+ /* check startup flags */
+ while( (i = getopt(argc, argv, "bkmglotc:s:V") ) != -1 )
+ switch (i) {
+ case 'b': shift = 0; break;
+ case 'k': shift = 10; break;
+ case 'm': shift = 20; break;
+ case 'g': shift = 30; break;
+ case 'l': show_high = 1; break;
+ case 'o': old_fmt = 1; break;
+ case 't': show_total = 1; break;
+ case 's': pause_length = 1000000 * atof(optarg); break;
+ case 'c': count = strtoul(optarg, NULL, 10); break;
+ case 'V': display_version(); exit(0);
+ default:
+ fwrite(help_message,1,strlen(help_message),stderr);
+ return 1;
+ }
+
+ do {
+ meminfo();
+ printf(" total used free shared buffers cached\n");
+ printf(
+ "%-7s %10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n", "Mem:",
+ S(kb_main_total),
+ S(kb_main_used),
+ S(kb_main_free),
+ S(kb_main_shared),
+ S(kb_main_buffers),
+ S(kb_main_cached)
+ );
+ // Print low vs. high information, if the user requested it.
+ // Note we check if low_total==0: if so, then this kernel does
+ // not export the low and high stats. Note we still want to
+ // print the high info, even if it is zero.
+ if (show_high) {
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "Low:",
+ S(kb_low_total),
+ S(kb_low_total - kb_low_free),
+ S(kb_low_free)
+ );
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "High:",
+ S(kb_high_total),
+ S(kb_high_total - kb_high_free),
+ S(kb_high_free)
+ );
+ }
+ if(!old_fmt){
+ unsigned KLONG buffers_plus_cached = kb_main_buffers + kb_main_cached;
+ printf(
+ "-/+ buffers/cache: %10Lu %10Lu\n",
+ S(kb_main_used - buffers_plus_cached),
+ S(kb_main_free + buffers_plus_cached)
+ );
+ }
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "Swap:",
+ S(kb_swap_total),
+ S(kb_swap_used),
+ S(kb_swap_free)
+ );
+ if(show_total){
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "Total:",
+ S(kb_main_total + kb_swap_total),
+ S(kb_main_used + kb_swap_used),
+ S(kb_main_free + kb_swap_free)
+ );
+ }
+ if(pause_length){
+ fputc('\n', stdout);
+ fflush(stdout);
+ if (count != 1) usleep(pause_length);
+ }
+ } while(pause_length && --count);
+
+ return 0;
+}
diff --git a/smartt-top/kill.1 b/smartt-top/kill.1
new file mode 100644
index 0000000..df1bdd7
--- /dev/null
+++ b/smartt-top/kill.1
@@ -0,0 +1,106 @@
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for kill.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan; converted to a man page by
+.\" Michael K. Johnson
+.TH KILL 1 "November 21, 1999" "Linux" "Linux User's Manual"
+.SH NAME
+kill \- send a signal to a process
+
+.SH SYNOPSIS
+\fBkill\fR [ \-\fBsignal\fR | \-s \fBsignal\fR ] \fBpid\fR ...
+.br
+\fBkill\fR [ \-L | -V, \-\-version ]
+.br
+\fBkill\fR \-l [ \fBsignal\fR ]
+
+.SH DESCRIPTION
+The default signal for kill is TERM. Use \-l or \-L to list available signals.
+Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+Alternate signals may be specified in three ways: \-9 \-SIGKILL \-KILL.
+Negative PID values may be used to choose whole process groups; see the
+PGID column in ps command output. A PID of \-1 is special; it indicates
+all processes except the kill process itself and init.
+
+.SH SIGNALS
+The signals listed below may be available for use with kill.
+When known constant, numbers and default behavior are shown.
+
+.TS
+lB rB lB lB
+lfCW r l l.
+Name Num Action Description
+0 0 n/a exit code indicates if a signal may be sent
+ALRM 14 exit
+HUP 1 exit
+INT 2 exit
+KILL 9 exit cannot be blocked
+PIPE 13 exit
+POLL exit
+PROF exit
+TERM 15 exit
+USR1 exit
+USR2 exit
+VTALRM exit
+STKFLT exit might not be implemented
+PWR ignore might exit on some systems
+WINCH ignore
+CHLD ignore
+URG ignore
+TSTP stop might interact with the shell
+TTIN stop might interact with the shell
+TTOU stop might interact with the shell
+STOP stop cannot be blocked
+CONT restart continue if stopped, otherwise ignore
+ABRT 6 core
+FPE 8 core
+ILL 4 core
+QUIT 3 core
+SEGV 11 core
+TRAP 5 core
+SYS core might not be implemented
+EMT core might not be implemented
+BUS core core dump might fail
+XCPU core core dump might fail
+XFSZ core core dump might fail
+.TE
+
+.SH NOTES
+Your shell (command line interpreter) may have a built-in kill command.
+You may need to run the command described here as /bin/kill to solve
+the conflict.
+
+.SH EXAMPLES
+.TP
+.B kill \-9 \-1
+Kill all processes you can kill.
+.TP
+.B kill \-l 11
+Translate number 11 into a signal name.
+.TP
+.B kill -L
+List the available signal choices in a nice table.
+.TP
+.B kill 123 543 2341 3453
+Send the default signal, SIGTERM, to all those processes.
+
+.SH "SEE ALSO"
+.BR pkill (1),
+.BR skill (1),
+.BR kill (2),
+.BR renice (1),
+.BR nice (1),
+.BR signal (7),
+.BR killall (1).
+
+.SH STANDARDS
+This command meets appropriate standards. The \-L flag is Linux-specific.
+
+.SH AUTHOR
+Albert Cahalan <albert@users.sf.net> wrote kill in 1999 to replace a
+bsdutils one that was not standards compliant. The util-linux one might
+also work correctly.
+
+Please send bug reports to <procps-feedback@lists.sf.net>
diff --git a/smartt-top/minimal.c b/smartt-top/minimal.c
new file mode 100644
index 0000000..a38e4c7
--- /dev/null
+++ b/smartt-top/minimal.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright 1998,2004 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/* This is a minimal /bin/ps, designed to be smaller than the old ps
+ * while still supporting some of the more important features of the
+ * new ps. (for total size, note that this ps does not need libproc)
+ * It is suitable for Linux-on-a-floppy systems only.
+ *
+ * Maintainers: do not compile or install for normal systems.
+ * Anyone needing this will want to tweak their compiler anyway.
+ */
+
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+
+#define DEV_ENCODE(M,m) ( \
+ ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \
+)
+
+///////////////////////////////////////////////////////
+#ifdef __sun__
+#include <sys/mkdev.h>
+#define _STRUCTURED_PROC 1
+#include <sys/procfs.h>
+#define NO_TTY_VALUE DEV_ENCODE(-1,-1)
+#define HZ 1 // only bother with seconds
+#endif
+
+///////////////////////////////////////////////////////
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#define NO_TTY_VALUE DEV_ENCODE(-1,-1)
+#define HZ 1 // only bother with seconds
+#endif
+
+///////////////////////////////////////////////////////
+#ifdef __linux__
+#include <asm/param.h> /* HZ */
+#include <asm/page.h> /* PAGE_SIZE */
+#define NO_TTY_VALUE DEV_ENCODE(0,0)
+#ifndef HZ
+#warning HZ not defined, assuming it is 100
+#define HZ 100
+#endif
+#endif
+
+///////////////////////////////////////////////////////////
+
+#ifndef PAGE_SIZE
+#warning PAGE_SIZE not defined, assuming it is 4096
+#define PAGE_SIZE 4096
+#endif
+
+
+
+static char P_tty_text[16];
+static char P_cmd[16];
+static char P_state;
+static int P_euid;
+static int P_pid;
+static int P_ppid, P_pgrp, P_session, P_tty_num, P_tpgid;
+static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime;
+static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_alarm;
+static unsigned long P_start_time, P_vsize;
+static long P_rss;
+static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip;
+static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch;
+static unsigned long P_wchan, P_nswap, P_cnswap;
+
+
+
+#if 0
+static int screen_cols = 80;
+static int w_count;
+#endif
+
+static int want_one_pid;
+static const char *want_one_command;
+static int select_notty;
+static int select_all;
+
+static int ps_format;
+static int old_h_option;
+
+/* we only pretend to support this */
+static int show_args; /* implicit with -f and all BSD options */
+static int bsd_c_option; /* this option overrides the above */
+
+static int ps_argc; /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg; /* index into ps_argv */
+static char *flagptr; /* current location in ps_argv[thisarg] */
+
+
+
+
+static void usage(void){
+ fprintf(stderr,
+ "-C select by command name (minimal ps only accepts one)\n"
+ "-p select by process ID (minimal ps only accepts one)\n"
+ "-e all processes (same as ax)\n"
+ "a all processes w/ tty, including other users\n"
+ "x processes w/o controlling ttys\n"
+ "-f full format\n"
+ "-j,j job control format\n"
+ "v virtual memory format\n"
+ "-l,l long format\n"
+ "u user-oriented format\n"
+ "-o user-defined format (limited support, only \"ps -o pid=\")\n"
+ "h no header\n"
+/*
+ "-A all processes (same as ax)\n"
+ "c true command name\n"
+ "-w,w wide output\n"
+*/
+ );
+ exit(1);
+}
+
+/*
+ * Return the next argument, or call the usage function.
+ * This handles both: -oFOO -o FOO
+ */
+static const char *get_opt_arg(void){
+ const char *ret;
+ ret = flagptr+1; /* assume argument is part of ps_argv[thisarg] */
+ if(*ret) return ret;
+ if(++thisarg >= ps_argc) usage(); /* there is nothing left */
+ /* argument is the new ps_argv[thisarg] */
+ ret = ps_argv[thisarg];
+ if(!ret || !*ret) usage();
+ return ret;
+}
+
+
+/* return the PID, or 0 if nothing good */
+static void parse_pid(const char *str){
+ char *endp;
+ int num;
+ if(!str) goto bad;
+ num = strtol(str, &endp, 0);
+ if(*endp != '\0') goto bad;
+ if(num<1) goto bad;
+ if(want_one_pid) goto bad;
+ want_one_pid = num;
+ return;
+bad:
+ usage();
+}
+
+/***************** parse SysV options, including Unix98 *****************/
+static void parse_sysv_option(void){
+ do{
+ switch(*flagptr){
+ /**** selection ****/
+ case 'C': /* end */
+ if(want_one_command) usage();
+ want_one_command = get_opt_arg();
+ return; /* can't have any more options */
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ case 'A':
+ case 'e':
+ select_all++;
+ select_notty++;
+case 'w': /* here for now, since the real one is not used */
+ break;
+ /**** output format ****/
+ case 'f':
+ show_args = 1;
+ /* FALL THROUGH */
+ case 'j':
+ case 'l':
+ if(ps_format) usage();
+ ps_format = *flagptr;
+ break;
+ case 'o': /* end */
+ /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */
+ if(strcmp(get_opt_arg(),"pid=")) usage();
+ if(ps_format) usage();
+ ps_format = 'o';
+ old_h_option++;
+ return; /* can't have any more options */
+ /**** other stuff ****/
+#if 0
+ case 'w':
+ w_count++;
+ break;
+#endif
+ default:
+ usage();
+ } /* switch */
+ }while(*++flagptr);
+}
+
+/************************* parse BSD options **********************/
+static void parse_bsd_option(void){
+ do{
+ switch(*flagptr){
+ /**** selection ****/
+ case 'a':
+ select_all++;
+ break;
+ case 'x':
+ select_notty++;
+ break;
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ /**** output format ****/
+ case 'j':
+ case 'l':
+ case 'u':
+ case 'v':
+ if(ps_format) usage();
+ ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */
+ break;
+ /**** other stuff ****/
+ case 'c':
+ bsd_c_option++;
+#if 0
+ break;
+#endif
+ case 'w':
+#if 0
+ w_count++;
+#endif
+ break;
+ case 'h':
+ old_h_option++;
+ break;
+ default:
+ usage();
+ } /* switch */
+ }while(*++flagptr);
+}
+
+#if 0
+#include <termios.h>
+/* not used yet */
+static void choose_dimensions(void){
+ struct winsize ws;
+ char *columns;
+ /* screen_cols is 80 by default */
+ if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col;
+ columns = getenv("COLUMNS");
+ if(columns && *columns){
+ long t;
+ char *endptr;
+ t = strtol(columns, &endptr, 0);
+ if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t;
+ }
+ if(w_count && (screen_cols<132)) screen_cols=132;
+ if(w_count>1) screen_cols=999999999;
+}
+#endif
+
+static void arg_parse(int argc, char *argv[]){
+ int sel = 0; /* to verify option sanity */
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+ /**** iterate over the args ****/
+ while(++thisarg < ps_argc){
+ flagptr = ps_argv[thisarg];
+ switch(*flagptr){
+ case '0' ... '9':
+ show_args = 1;
+ parse_pid(flagptr);
+ break;
+ case '-':
+ flagptr++;
+ parse_sysv_option();
+ break;
+ default:
+ show_args = 1;
+ parse_bsd_option();
+ break;
+ }
+ }
+ /**** sanity check and clean-up ****/
+ if(want_one_pid) sel++;
+ if(want_one_command) sel++;
+ if(select_notty || select_all) sel++;
+ if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage();
+ if(bsd_c_option) show_args = 0;
+}
+
+#ifdef __sun__
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid) {
+ struct psinfo p; // /proc/*/psinfo, struct psinfo, psinfo_t
+ char buf[32];
+ int num;
+ int fd;
+ int tty_maj, tty_min;
+ snprintf(buf, sizeof buf, "/proc/%d/psinfo", pid);
+ if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
+ num = read(fd, &p, sizeof p);
+ close(fd);
+ if(num != sizeof p) return 0;
+
+ num = PRFNSZ;
+ if (num >= sizeof P_cmd) num = sizeof P_cmd - 1;
+ memcpy(P_cmd, p.pr_fname, num); // p.pr_fname or p.pr_lwp.pr_name
+ P_cmd[num] = '\0';
+
+ P_pid = p.pr_pid;
+ P_ppid = p.pr_ppid;
+ P_pgrp = p.pr_pgid;
+ P_session = p.pr_sid;
+ P_euid = p.pr_euid;
+ P_rss = p.pr_rssize;
+ P_vsize = p.pr_size;
+ P_start_time = p.pr_start.tv_sec;
+ P_wchan = p.pr_lwp.pr_wchan;
+ P_state = p.pr_lwp.pr_sname;
+ P_nice = p.pr_lwp.pr_nice;
+ P_priority = p.pr_lwp.pr_pri; // or pr_oldpri
+// P_ruid = p.pr_uid;
+// P_rgid = p.pr_gid;
+// P_egid = p.pr_egid;
+
+#if 0
+ // don't support these
+ P_tpgid; P_flags,
+ P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime;
+ P_cutime, P_cstime, P_timeout, P_alarm;
+ P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip;
+ P_signal, P_blocked, P_sigignore, P_sigcatch;
+ P_nswap, P_cnswap;
+#endif
+
+ // we like it Linux-encoded :-)
+ tty_maj = major(p.pr_ttydev);
+ tty_min = minor(p.pr_ttydev);
+ P_tty_num = DEV_ENCODE(tty_maj,tty_min);
+
+ snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
+#if 1
+ if (tty_maj == 24) snprintf(P_tty_text, sizeof P_tty_text, "pts/%-3u", tty_min);
+ if (P_tty_num == NO_TTY_VALUE) memcpy(P_tty_text, " ? ", 8);
+ if (P_tty_num == DEV_ENCODE(0,0)) memcpy(P_tty_text, "console", 8);
+#endif
+
+ if(P_pid != pid) return 0;
+ return 1;
+}
+#endif
+
+#ifdef __FreeBSD__
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid) {
+ char buf[400];
+ int num;
+ int fd;
+ char* tmp;
+ int tty_maj, tty_min;
+ snprintf(buf, 32, "/proc/%d/status", pid);
+ if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
+ num = read(fd, buf, sizeof buf - 1);
+ close(fd);
+ if(num<43) return 0;
+ buf[num] = '\0';
+
+ P_state = '-';
+
+ // FreeBSD /proc/*/status is seriously fucked. Unlike the Linux
+ // files, we can't use strrchr to find the end of a command name.
+ // Spaces in command names do not get escaped. To avoid spoofing,
+ // one may skip 20 characters and then look _forward_ only to
+ // find a pattern of entries that are {with,with,without} a comma.
+ // The entry without a comma is wchan. Then count backwards!
+ //
+ // Don't bother for now. FreeBSD isn't worth the trouble.
+
+ tmp = strchr(buf,' ');
+ num = tmp - buf;
+ if (num >= sizeof P_cmd) num = sizeof P_cmd - 1;
+ memcpy(P_cmd,buf,num);
+ P_cmd[num] = '\0';
+
+ num = sscanf(tmp+1,
+ "%d %d %d %d "
+ "%d,%d "
+ "%*s "
+ "%ld,%*d "
+ "%ld,%*d "
+ "%ld,%*d "
+ "%*s "
+ "%d %d ",
+ &P_pid, &P_ppid, &P_pgrp, &P_session,
+ &tty_maj, &tty_min,
+ /* SKIP funny flags thing */
+ &P_start_time, /* SKIP microseconds */
+ &P_utime, /* SKIP microseconds */
+ &P_stime, /* SKIP microseconds */
+ /* SKIP &P_wchan, for now -- it is a string */
+ &P_euid, &P_euid // don't know which is which
+ );
+/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+
+ snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
+ P_tty_num = DEV_ENCODE(tty_maj,tty_min);
+// tty decode is 224 to 256 bytes on i386
+#if 1
+ tmp = NULL;
+ if (tty_maj == 5) tmp = " ttyp%c ";
+ if (tty_maj == 12) tmp = " ttyv%c ";
+ if (tty_maj == 28) tmp = " ttyd%c ";
+ if (P_tty_num == NO_TTY_VALUE) tmp = " ? ";
+ if (P_tty_num == DEV_ENCODE(0,0)) tmp = "console";
+ if (P_tty_num == DEV_ENCODE(12,255)) tmp = "consolectl";
+ if (tmp) {
+ snprintf(
+ P_tty_text,
+ sizeof P_tty_text,
+ tmp,
+ "0123456789abcdefghijklmnopqrstuvwxyz"[tty_min&31]
+ );
+ }
+#endif
+
+ if(num < 9) return 0;
+ if(P_pid != pid) return 0;
+ return 1;
+}
+#endif
+
+#ifdef __linux__
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid) {
+ char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
+ int num;
+ int fd;
+ char* tmp;
+ struct stat sb; /* stat() used to get EUID */
+ snprintf(buf, 32, "/proc/%d/stat", pid);
+ if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
+ num = read(fd, buf, sizeof buf - 1);
+ fstat(fd, &sb);
+ P_euid = sb.st_uid;
+ close(fd);
+ if(num<80) return 0;
+ buf[num] = '\0';
+ tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+ *tmp = '\0'; /* replace trailing ')' with NUL */
+ /* parse these two strings separately, skipping the leading "(". */
+ memset(P_cmd, 0, sizeof P_cmd); /* clear */
+ sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */
+ num = sscanf(tmp + 2, /* skip space after ')' too */
+ "%c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu %lu %lu "
+ "%ld %ld %ld %ld %ld %ld "
+ "%lu %lu "
+ "%ld "
+ "%lu %lu %lu %lu %lu %lu "
+ "%u %u %u %u " /* no use for RT signals */
+ "%lu %lu %lu",
+ &P_state,
+ &P_ppid, &P_pgrp, &P_session, &P_tty_num, &P_tpgid,
+ &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime,
+ &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_alarm,
+ &P_start_time, &P_vsize,
+ &P_rss,
+ &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip,
+ &P_signal, &P_blocked, &P_sigignore, &P_sigcatch,
+ &P_wchan, &P_nswap, &P_cnswap
+ );
+/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+ P_vsize /= 1024;
+ P_rss *= (PAGE_SIZE/1024);
+
+ memcpy(P_tty_text, " ? ", 8);
+ if (P_tty_num != NO_TTY_VALUE) {
+ int tty_maj = (P_tty_num>>8)&0xfff;
+ int tty_min = (P_tty_num&0xff) | ((P_tty_num>>12)&0xfff00);
+ snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
+ }
+
+ if(num < 30) return 0;
+ if(P_pid != pid) return 0;
+ return 1;
+}
+#endif
+
+static const char *do_time(unsigned long t){
+ int hh,mm,ss;
+ static char buf[32];
+ int cnt = 0;
+ t /= HZ;
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t);
+ snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss);
+ return buf;
+}
+
+static const char *do_user(void){
+ static char buf[32];
+ static struct passwd *p;
+ static int lastuid = -1;
+ if(P_euid != lastuid){
+ p = getpwuid(P_euid);
+ if(p) snprintf(buf, sizeof buf, "%-8.8s", p->pw_name);
+ else snprintf(buf, sizeof buf, "%5d ", P_euid);
+ }
+ return buf;
+}
+
+static const char *do_cpu(int longform){
+ static char buf[8];
+ strcpy(buf," - ");
+ if(!longform) buf[2] = '\0';
+ return buf;
+}
+
+static const char *do_mem(int longform){
+ static char buf[8];
+ strcpy(buf," - ");
+ if(!longform) buf[2] = '\0';
+ return buf;
+}
+
+static const char *do_stime(void){
+ static char buf[32];
+ strcpy(buf," - ");
+ return buf;
+}
+
+static void print_proc(void){
+ switch(ps_format){
+ case 0:
+ printf("%5d %s %s", P_pid, P_tty_text, do_time(P_utime+P_stime));
+ break;
+ case 'o':
+ printf("%d\n", P_pid);
+ return; /* don't want the command */
+ case 'l':
+ printf(
+ "0 %c %5d %5d %5d %s %3d %3d - "
+ "%5ld %06x %s %s",
+ P_state, P_euid, P_pid, P_ppid, do_cpu(0),
+ (int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024),
+ (unsigned)(P_wchan&0xffffff), P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'f':
+ printf(
+ "%8s %5d %5d %s %s %s %s",
+ do_user(), P_pid, P_ppid, do_cpu(0), do_stime(), P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'j':
+ printf(
+ "%5d %5d %5d %s %s",
+ P_pid, P_pgrp, P_session, P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'u'|0x80:
+ printf(
+ "%8s %5d %s %s %5ld %4ld %s %c %s %s",
+ do_user(), P_pid, do_cpu(1), do_mem(1), P_vsize, P_rss, P_tty_text, P_state,
+ do_stime(), do_time(P_utime+P_stime)
+ );
+ break;
+ case 'v'|0x80:
+ printf(
+ "%5d %s %c %s %6d - - %5d %s",
+ P_pid, P_tty_text, P_state, do_time(P_utime+P_stime), (int)P_maj_flt,
+ (int)P_rss, do_mem(1)
+ );
+ break;
+ case 'j'|0x80:
+ printf(
+ "%5d %5d %5d %5d %s %5d %c %5d %s",
+ P_ppid, P_pid, P_pgrp, P_session, P_tty_text, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'l'|0x80:
+ printf(
+ "0 %5d %5d %5d %3d %3d "
+ "%5ld %4ld %06x %c %s %s",
+ P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice,
+ P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ default:
+ ;
+ }
+ if(show_args) printf(" [%s]\n", P_cmd);
+ else printf(" %s\n", P_cmd);
+}
+
+
+int main(int argc, char *argv[]){
+ arg_parse(argc, argv);
+#if 0
+ choose_dimensions();
+#endif
+ if(!old_h_option){
+ const char *head;
+ switch(ps_format){
+ default: /* can't happen */
+ case 0: head = " PID TTY TIME CMD"; break;
+ case 'l': head = "F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break;
+ case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break;
+ case 'j': head = " PID PGID SID TTY TIME CMD"; break;
+ case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break;
+ case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break;
+ case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break;
+ case 'l'|0x80: head = "F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break;
+ }
+ printf("%s\n",head);
+ }
+ if(want_one_pid){
+ if(stat2proc(want_one_pid)) print_proc();
+ else exit(1);
+ }else{
+ struct dirent *ent; /* dirent handle */
+ DIR *dir;
+ int ouruid;
+ int found_a_proc;
+ found_a_proc = 0;
+ ouruid = getuid();
+ dir = opendir("/proc");
+ while(( ent = readdir(dir) )){
+ if(*ent->d_name<'0' || *ent->d_name>'9') continue;
+ if(!stat2proc(atoi(ent->d_name))) continue;
+ if(want_one_command){
+ if(strcmp(want_one_command,P_cmd)) continue;
+ }else{
+ if(!select_notty && P_tty_num==NO_TTY_VALUE) continue;
+ if(!select_all && P_euid!=ouruid) continue;
+ }
+ found_a_proc++;
+ print_proc();
+ }
+ closedir(dir);
+ exit(!found_a_proc);
+ }
+ return 0;
+}
diff --git a/smartt-top/pgrep.1 b/smartt-top/pgrep.1
new file mode 100644
index 0000000..257a6ce
--- /dev/null
+++ b/smartt-top/pgrep.1
@@ -0,0 +1,183 @@
+.\" Manual page for pgrep / pkill.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Copyright 2000 Kjetil Torgrim Homme
+.\"
+.TH PGREP 1 "October 5, 2007" "Linux" "Linux User's Manual"
+.SH NAME
+pgrep, pkill \- look up or signal processes based on name and other attributes
+
+.SH SYNOPSIS
+.na
+\fBpgrep\fR [\fB\-cflvx\fR] [\fB\-d\ \fIdelimiter\fR] [\fB\-n\fR|\fB\-o\fR] \
+[\fB\-P\ \fIppid\fR,...] [\fB\-g\ \fIpgrp\fR,...] [\fB\-s\ \fIsid\fR,...] \
+[\fB\-u\ \fIeuid\fR,...] [\fB\-U\ \fIuid\fR,...] [\fB\-G\ \fIgid\fR,...] \
+[\fB\-t\ \fIterm\fR,...] [\fIpattern\fR]
+
+.HP
+\fBpkill\fR [\fB\-\fIsignal\fR] [\fB\-fvx\fR] [\fB\-n\fR|\fB\-o\fR] \
+[\fB\-P\ \fIppid\fR,...] [\fB\-g\ \fIpgrp\fR,...] [\fB\-s\ \fIsid\fR,...] \
+[\fB\-u\ \fIeuid\fR,...] [\fB\-U\ \fIuid\fR,...] [\fB\-G\ \fIgid\fR,...] \
+[\fB\-t\ \fIterm\fR,...] [\fIpattern\fR]
+
+.SH DESCRIPTION
+\fBpgrep\fP looks through the currently running processes and lists the
+process IDs which matches the selection criteria to stdout. All
+the criteria have to match. For example,
+
+.IP
+$ pgrep \-u root sshd
+
+.PP
+will only list the processes called \fBsshd\fP AND owned by \fBroot\fP.
+On the other hand,
+
+.IP
+$ pgrep \-u root,daemon
+
+.PP
+will list the processes owned by \fBroot\fP OR \fBdaemon\fP.
+
+\fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP)
+to each process instead of listing them on stdout.
+
+.SH OPTIONS
+.TP
+\fB\-c\fR
+Suppress normal output; instead print a count of matching processes.
+.TP
+\fB\-d \fIdelimiter\fP
+Sets the string used to delimit each process ID in the output (by
+default a newline). (\fBpgrep\fP only.)
+.TP
+\fB\-f\fR
+The \fIpattern\fP is normally only matched against the process name.
+When \fB\-f\fR is set, the full command line is used.
+.TP
+\fB\-g \fIpgrp\fP,...
+Only match processes in the process group IDs listed. Process group 0
+is translated into \fBpgrep\fP's or \fBpkill\fP's own process group.
+.TP
+\-G \fIgid\fP,...
+Only match processes whose real group ID is listed. Either the
+numerical or symbolical value may be used.
+.TP
+\fB\-l\fR
+List the process name as well as the process ID. (\fBpgrep\fP only.)
+.TP
+\fB\-n\fR
+Select only the newest (most recently started) of the matching
+processes.
+.TP
+\fB\-o\fR
+Select only the oldest (least recently started) of the matching
+processes.
+.TP
+\fB\-P \fIppid\fP,...
+Only match processes whose parent process ID is listed.
+.TP
+\fB\-s \fIsid\fP,...
+Only match processes whose process session ID is listed. Session ID 0
+is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID.
+.TP
+\fB\-t \fIterm\fP,...
+Only match processes whose controlling terminal is listed. The
+terminal name should be specified without the "/dev/" prefix.
+.TP
+\fB\-u \fIeuid\fP,...
+Only match processes whose effective user ID is listed. Either the
+numerical or symbolical value may be used.
+.TP
+\fB\-U \fIuid\fP,...
+Only match processes whose real user ID is listed. Either the
+numerical or symbolical value may be used.
+.TP
+\fB\-v\fR
+Negates the matching.
+.TP
+\fB\-x\fR
+Only match processes whose name (or command line if \-f is specified)
+\fBexactly\fP match the \fIpattern\fP.
+.TP
+\-\fIsignal\fP
+Defines the signal to send to each matched process. Either the
+numeric or the symbolic signal name can be used. (\fBpkill\fP only.)
+
+.SH OPERANDS
+.TP
+\fIpattern\fP
+Specifies an Extended Regular Expression for matching against the
+process names or command lines.
+
+.SH EXAMPLES
+Example 1: Find the process ID of the \fBnamed\fP daemon:
+
+.IP
+$ pgrep \-u root named
+
+.PP
+Example 2: Make \fBsyslog\fP reread its configuration file:
+
+.IP
+$ pkill \-HUP syslogd
+
+.PP
+Example 3: Give detailed information on all \fBxterm\fP processes:
+
+.IP
+$ ps \-fp $(pgrep \-d, \-x xterm)
+
+.PP
+Example 4: Make all \fBnetscape\fP processes run nicer:
+
+.IP
+$ renice +4 `pgrep netscape`
+
+.SH "EXIT STATUS"
+.PD 0
+.TP
+.I 0
+One or more processes matched the criteria.
+.TP
+.I 1
+No processes matched.
+.TP
+.I 2
+Syntax error in the command line.
+.TP
+.I 3
+Fatal error: out of memory etc.
+
+.SH NOTES
+The process name used for matching is limited to the 15 characters
+present in the output of /proc/\fIpid\fP/stat. Use the \-f option to
+match against the complete command line, /proc/\fIpid\fP/cmdline.
+
+The running \fBpgrep\fP or \fBpkill\fP process will never report
+itself as a match.
+
+.SH BUGS
+The options \fB\-n\fP and \fB\-o\fP and \fB\-v\fP can not be combined.
+Let me know if you need to do this.
+
+Defunct processes are reported.
+
+.SH "SEE ALSO"
+.BR ps (1),
+.BR regex (7),
+.BR signal (7),
+.BR killall (1),
+.BR skill (1),
+.BR kill (1),
+.BR kill (2)
+
+.SH STANDARDS
+\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This
+implementation is fully compatible.
+
+.SH AUTHOR
+Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+
+Albert Cahalan <albert@users.sf.net> is the current maintainer of
+the procps package.
+
+Please send bug reports to <procps-feedback@lists.sf.net>
diff --git a/smartt-top/pgrep.c b/smartt-top/pgrep.c
new file mode 100644
index 0000000..36eccb6
--- /dev/null
+++ b/smartt-top/pgrep.c
@@ -0,0 +1,741 @@
+/* emacs settings: -*- c-basic-offset: 8 tab-width: 8 -*-
+ *
+ * pgrep/pkill -- utilities to filter the process table
+ *
+ * Copyright 2000 Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+ *
+ * May be distributed under the conditions of the
+ * GNU General Public License; a copy is in COPYING
+ *
+ * Changes by Albert Cahalan, 2002,2006.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+#include <errno.h>
+
+#include "proc/readproc.h"
+#include "proc/sig.h"
+#include "proc/devname.h"
+#include "proc/sysinfo.h"
+#include "proc/version.h" /* procps_version */
+
+// EXIT_SUCCESS is 0
+// EXIT_FAILURE is 1
+#define EXIT_USAGE 2
+#define EXIT_FATAL 3
+
+static int i_am_pkill = 0;
+static const char *progname = "pgrep";
+
+union el {
+ long num;
+ char * str;
+};
+
+/* User supplied arguments */
+
+static int opt_full = 0;
+static int opt_long = 0;
+static int opt_oldest = 0;
+static int opt_newest = 0;
+static int opt_negate = 0;
+static int opt_exact = 0;
+static int opt_count = 0;
+static int opt_signal = SIGTERM;
+static int opt_lock = 0;
+static int opt_case = 0;
+
+static const char *opt_delim = "\n";
+static union el *opt_pgrp = NULL;
+static union el *opt_rgid = NULL;
+static union el *opt_pid = NULL;
+static union el *opt_ppid = NULL;
+static union el *opt_sid = NULL;
+static union el *opt_term = NULL;
+static union el *opt_euid = NULL;
+static union el *opt_ruid = NULL;
+static char *opt_pattern = NULL;
+static char *opt_pidfile = NULL;
+
+static int usage (int opt) NORETURN;
+static int usage (int opt)
+{
+ int err = (opt=='?'); /* getopt() uses '?' to mark an error */
+ FILE *fp = err ? stderr : stdout;
+
+ if (i_am_pkill)
+ fprintf (fp, "Usage: pkill [-SIGNAL] [-fvx] ");
+ else
+ fprintf (fp, "Usage: pgrep [-cflvx] [-d DELIM] ");
+ fprintf (fp, "[-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n"
+ "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] "
+ "[PATTERN]\n");
+
+ exit(err ? EXIT_USAGE : EXIT_SUCCESS);
+}
+
+
+static union el *split_list (const char *restrict str, int (*convert)(const char *, union el *))
+{
+ char *copy = strdup (str);
+ char *ptr = copy;
+ char *sep_pos;
+ int i = 0;
+ int size = 0;
+ union el *list = NULL;
+
+ do {
+ if (i == size) {
+ size = size * 5 / 4 + 4;
+ // add 1 because slot zero is a count
+ list = realloc (list, 1 + size * sizeof *list);
+ if (list == NULL)
+ exit (EXIT_FATAL);
+ }
+ sep_pos = strchr (ptr, ',');
+ if (sep_pos)
+ *sep_pos = 0;
+ // Use ++i instead of i++ because slot zero is a count
+ if (!convert (ptr, &list[++i]))
+ exit (EXIT_USAGE);
+ if (sep_pos)
+ ptr = sep_pos + 1;
+ } while (sep_pos);
+
+ free (copy);
+ if (!i) {
+ free (list);
+ list = NULL;
+ } else {
+ list[0].num = i;
+ }
+ return list;
+}
+
+// strict_atol returns a Boolean: TRUE if the input string
+// contains a plain number, FALSE if there are any non-digits.
+static int strict_atol (const char *restrict str, long *restrict value)
+{
+ int res = 0;
+ int sign = 1;
+
+ if (*str == '+')
+ ++str;
+ else if (*str == '-') {
+ ++str;
+ sign = -1;
+ }
+
+ for ( ; *str; ++str) {
+ if (! isdigit (*str))
+ return (0);
+ res *= 10;
+ res += *str - '0';
+ }
+ *value = sign * res;
+ return 1;
+}
+
+#include <sys/file.h>
+
+// Seen non-BSD code do this:
+//
+//if (fcntl_lock(pid_fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1)
+// return -1;
+int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len)
+{
+ struct flock lock[1];
+
+ lock->l_type = type;
+ lock->l_whence = whence;
+ lock->l_start = start;
+ lock->l_len = len;
+
+ return fcntl(fd, cmd, lock);
+}
+
+
+// We try a read lock. The daemon should have a write lock.
+// Seen using flock: FreeBSD code
+static int has_flock(int fd)
+{
+ return flock(fd, LOCK_SH|LOCK_NB)==-1 && errno==EWOULDBLOCK;
+}
+
+// We try a read lock. The daemon should have a write lock.
+// Seen using fcntl: libslack
+static int has_fcntl(int fd)
+{
+ struct flock f; // seriously, struct flock is for a fnctl lock!
+ f.l_type = F_RDLCK;
+ f.l_whence = SEEK_SET;
+ f.l_start = 0;
+ f.l_len = 0;
+ return fcntl(fd,F_SETLK,&f)==-1 && (errno==EACCES || errno==EAGAIN);
+}
+
+static union el *read_pidfile(void)
+{
+ char buf[12];
+ int fd;
+ struct stat sbuf;
+ char *endp;
+ int pid;
+ union el *list = NULL;
+
+ fd = open(opt_pidfile, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if(fd<0)
+ goto out;
+ if(fstat(fd,&sbuf) || !S_ISREG(sbuf.st_mode) || sbuf.st_size<1)
+ goto out;
+ // type of lock, if any, is not standardized on Linux
+ if(opt_lock && !has_flock(fd) && !has_fcntl(fd))
+ goto out;
+ memset(buf,'\0',sizeof buf);
+ buf[read(fd,buf+1,sizeof buf-2)] = '\0';
+ pid = strtoul(buf+1,&endp,10);
+ if(endp<=buf+1 || pid<1 || pid>0x7fffffff)
+ goto out;
+ if(*endp && !isspace(*endp))
+ goto out;
+ list = malloc(2 * sizeof *list);
+ list[0].num = 1;
+ list[1].num = pid;
+out:
+ close(fd);
+ return list;
+}
+
+static int conv_uid (const char *restrict name, union el *restrict e)
+{
+ struct passwd *pwd;
+
+ if (strict_atol (name, &e->num))
+ return (1);
+
+ pwd = getpwnam (name);
+ if (pwd == NULL) {
+ fprintf (stderr, "%s: invalid user name: %s\n",
+ progname, name);
+ return 0;
+ }
+ e->num = pwd->pw_uid;
+ return 1;
+}
+
+
+static int conv_gid (const char *restrict name, union el *restrict e)
+{
+ struct group *grp;
+
+ if (strict_atol (name, &e->num))
+ return 1;
+
+ grp = getgrnam (name);
+ if (grp == NULL) {
+ fprintf (stderr, "%s: invalid group name: %s\n",
+ progname, name);
+ return 0;
+ }
+ e->num = grp->gr_gid;
+ return 1;
+}
+
+
+static int conv_pgrp (const char *restrict name, union el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ fprintf (stderr, "%s: invalid process group: %s\n",
+ progname, name);
+ return 0;
+ }
+ if (e->num == 0)
+ e->num = getpgrp ();
+ return 1;
+}
+
+
+static int conv_sid (const char *restrict name, union el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ fprintf (stderr, "%s: invalid session id: %s\n",
+ progname, name);
+ return 0;
+ }
+ if (e->num == 0)
+ e->num = getsid (0);
+ return 1;
+}
+
+
+static int conv_num (const char *restrict name, union el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ fprintf (stderr, "%s: not a number: %s\n",
+ progname, name);
+ return 0;
+ }
+ return 1;
+}
+
+
+static int conv_str (const char *restrict name, union el *restrict e)
+{
+ e->str = strdup (name);
+ return 1;
+}
+
+
+static int match_numlist (long value, const union el *restrict list)
+{
+ int found = 0;
+ if (list == NULL)
+ found = 0;
+ else {
+ int i;
+ for (i = list[0].num; i > 0; i--) {
+ if (list[i].num == value)
+ found = 1;
+ }
+ }
+ return found;
+}
+
+static int match_strlist (const char *restrict value, const union el *restrict list)
+{
+ int found = 0;
+ if (list == NULL)
+ found = 0;
+ else {
+ int i;
+ for (i = list[0].num; i > 0; i--) {
+ if (! strcmp (list[i].str, value))
+ found = 1;
+ }
+ }
+ return found;
+}
+
+static void output_numlist (const union el *restrict list, int num)
+{
+ int i;
+ const char *delim = opt_delim;
+ for (i = 0; i < num; i++) {
+ if(i+1==num)
+ delim = "\n";
+ printf ("%ld%s", list[i].num, delim);
+ }
+}
+
+static void output_strlist (const union el *restrict list, int num)
+{
+// FIXME: escape codes
+ int i;
+ const char *delim = opt_delim;
+ for (i = 0; i < num; i++) {
+ if(i+1==num)
+ delim = "\n";
+ printf ("%s%s", list[i].str, delim);
+ }
+}
+
+static PROCTAB *do_openproc (void)
+{
+ PROCTAB *ptp;
+ int flags = 0;
+
+ if (opt_pattern || opt_full)
+ flags |= PROC_FILLCOM;
+ if (opt_ruid || opt_rgid)
+ flags |= PROC_FILLSTATUS;
+ if (opt_oldest || opt_newest || opt_pgrp || opt_sid || opt_term)
+ flags |= PROC_FILLSTAT;
+ if (!(flags & PROC_FILLSTAT))
+ flags |= PROC_FILLSTATUS; // FIXME: need one, and PROC_FILLANY broken
+ if (opt_euid && !opt_negate) {
+ int num = opt_euid[0].num;
+ int i = num;
+ uid_t *uids = malloc (num * sizeof (uid_t));
+ if (uids == NULL)
+ exit (EXIT_FATAL);
+ while (i-- > 0) {
+ uids[i] = opt_euid[i+1].num;
+ }
+ flags |= PROC_UID;
+ ptp = openproc (flags, uids, num);
+ } else {
+ ptp = openproc (flags);
+ }
+ return ptp;
+}
+
+static regex_t * do_regcomp (void)
+{
+ regex_t *preg = NULL;
+
+ if (opt_pattern) {
+ char *re;
+ char errbuf[256];
+ int re_err;
+
+ preg = malloc (sizeof (regex_t));
+ if (preg == NULL)
+ exit (EXIT_FATAL);
+ if (opt_exact) {
+ re = malloc (strlen (opt_pattern) + 5);
+ if (re == NULL)
+ exit (EXIT_FATAL);
+ sprintf (re, "^(%s)$", opt_pattern);
+ } else {
+ re = opt_pattern;
+ }
+
+ re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB | opt_case);
+ if (re_err) {
+ regerror (re_err, preg, errbuf, sizeof(errbuf));
+ fputs(errbuf,stderr);
+ exit (EXIT_USAGE);
+ }
+ }
+ return preg;
+}
+
+static union el * select_procs (int *num)
+{
+ PROCTAB *ptp;
+ proc_t task;
+ unsigned long long saved_start_time; // for new/old support
+ pid_t saved_pid = 0; // for new/old support
+ int matches = 0;
+ int size = 0;
+ regex_t *preg;
+ pid_t myself = getpid();
+ union el *list = NULL;
+ char cmd[4096];
+
+ ptp = do_openproc();
+ preg = do_regcomp();
+
+ if (opt_newest) saved_start_time = 0ULL;
+ else
+ saved_start_time = ~0ULL;
+ if (opt_newest) saved_pid = 0;
+ if (opt_oldest) saved_pid = INT_MAX;
+
+ memset(&task, 0, sizeof (task));
+ while(readproc(ptp, &task)) {
+ int match = 1;
+
+ if (task.XXXID == myself)
+ continue;
+ else if (opt_newest && task.start_time < saved_start_time)
+ match = 0;
+ else if (opt_oldest && task.start_time > saved_start_time)
+ match = 0;
+ else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid))
+ match = 0;
+ else if (opt_pid && ! match_numlist (task.tgid, opt_pid))
+ match = 0;
+ else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp))
+ match = 0;
+ else if (opt_euid && ! match_numlist (task.euid, opt_euid))
+ match = 0;
+ else if (opt_ruid && ! match_numlist (task.ruid, opt_ruid))
+ match = 0;
+ else if (opt_rgid && ! match_numlist (task.rgid, opt_rgid))
+ match = 0;
+ else if (opt_sid && ! match_numlist (task.session, opt_sid))
+ match = 0;
+ else if (opt_term) {
+ if (task.tty == 0) {
+ match = 0;
+ } else {
+ char tty[256];
+ dev_to_tty (tty, sizeof(tty) - 1,
+ task.tty, task.XXXID, ABBREV_DEV);
+ match = match_strlist (tty, opt_term);
+ }
+ }
+ if (opt_long || (match && opt_pattern)) {
+ if (opt_full && task.cmdline) {
+ int i = 0;
+ int bytes = sizeof (cmd) - 1;
+
+ /* make sure it is always NUL-terminated */
+ cmd[bytes] = 0;
+ /* make room for SPC in loop below */
+ --bytes;
+
+ strncpy (cmd, task.cmdline[i], bytes);
+ bytes -= strlen (task.cmdline[i++]);
+ while (task.cmdline[i] && bytes > 0) {
+ strncat (cmd, " ", bytes);
+ strncat (cmd, task.cmdline[i], bytes);
+ bytes -= strlen (task.cmdline[i++]) + 1;
+ }
+ } else {
+ strcpy (cmd, task.cmd);
+ }
+ }
+
+ if (match && opt_pattern) {
+ if (regexec (preg, cmd, 0, NULL, 0) != 0)
+ match = 0;
+ }
+
+ if (match ^ opt_negate) { /* Exclusive OR is neat */
+ if (opt_newest) {
+ if (saved_start_time == task.start_time &&
+ saved_pid > task.XXXID)
+ continue;
+ saved_start_time = task.start_time;
+ saved_pid = task.XXXID;
+ matches = 0;
+ }
+ if (opt_oldest) {
+ if (saved_start_time == task.start_time &&
+ saved_pid < task.XXXID)
+ continue;
+ saved_start_time = task.start_time;
+ saved_pid = task.XXXID;
+ matches = 0;
+ }
+ if (matches == size) {
+ size = size * 5 / 4 + 4;
+ list = realloc(list, size * sizeof *list);
+ if (list == NULL)
+ exit (EXIT_FATAL);
+ }
+ if (opt_long) {
+ char buff[5096]; // FIXME
+ sprintf (buff, "%d %s", task.XXXID, cmd);
+ list[matches++].str = strdup (buff);
+ } else {
+ list[matches++].num = task.XXXID;
+ }
+ }
+
+ memset (&task, 0, sizeof (task));
+ }
+ closeproc (ptp);
+
+ *num = matches;
+ return list;
+}
+
+
+static void parse_opts (int argc, char **argv)
+{
+ char opts[32] = "";
+ int opt;
+ int criteria_count = 0;
+
+ if (strstr (argv[0], "pkill")) {
+ i_am_pkill = 1;
+ progname = "pkill";
+ /* Look for a signal name or number as first argument */
+ if (argc > 1 && argv[1][0] == '-') {
+ int sig;
+ sig = signal_name_to_number (argv[1] + 1);
+ if (sig == -1 && isdigit (argv[1][1]))
+ sig = atoi (argv[1] + 1);
+ if (sig != -1) {
+ int i;
+ for (i = 2; i < argc; i++)
+ argv[i-1] = argv[i];
+ --argc;
+ opt_signal = sig;
+ }
+ }
+ } else {
+ /* These options are for pgrep only */
+ strcat (opts, "ld:");
+ }
+
+ strcat (opts, "LF:cfnovxP:g:s:u:U:G:t:?V");
+
+ while ((opt = getopt (argc, argv, opts)) != -1) {
+ switch (opt) {
+// case 'D': // FreeBSD: print info about non-matches for debugging
+// break;
+ case 'F': // FreeBSD: the arg is a file containing a PID to match
+ opt_pidfile = strdup (optarg);
+ ++criteria_count;
+ break;
+ case 'G': // Solaris: match rgid/rgroup
+ opt_rgid = split_list (optarg, conv_gid);
+ if (opt_rgid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+// case 'I': // FreeBSD: require confirmation before killing
+// break;
+// case 'J': // Solaris: match by project ID (name or number)
+// break;
+ case 'L': // FreeBSD: fail if pidfile (see -F) not locked
+ opt_lock++;
+ break;
+// case 'M': // FreeBSD: specify core (OS crash dump) file
+// break;
+// case 'N': // FreeBSD: specify alternate namelist file (for us, System.map -- but we don't need it)
+// break;
+ case 'P': // Solaris: match by PPID
+ opt_ppid = split_list (optarg, conv_num);
+ if (opt_ppid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+// case 'S': // FreeBSD: don't ignore the built-in kernel tasks
+// break;
+// case 'T': // Solaris: match by "task ID" (probably not a Linux task)
+// break;
+ case 'U': // Solaris: match by ruid/rgroup
+ opt_ruid = split_list (optarg, conv_uid);
+ if (opt_ruid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 'V':
+ fprintf(stdout, "%s (%s)\n", progname, procps_version);
+ exit(EXIT_SUCCESS);
+// case 'c': // Solaris: match by contract ID
+// break;
+ case 'c':
+ opt_count = 1;
+ break;
+ case 'd': // Solaris: change the delimiter
+ opt_delim = strdup (optarg);
+ break;
+ case 'f': // Solaris: match full process name (as in "ps -f")
+ opt_full = 1;
+ break;
+ case 'g': // Solaris: match pgrp
+ opt_pgrp = split_list (optarg, conv_pgrp);
+ if (opt_pgrp == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+// case 'i': // FreeBSD: ignore case. OpenBSD: withdrawn. See -I. This sucks.
+// if (opt_case)
+// usage (opt);
+// opt_case = REG_ICASE;
+// break;
+// case 'j': // FreeBSD: restricted to the given jail ID
+// break;
+ case 'l': // Solaris: long output format (pgrep only) Should require -f for beyond argv[0] maybe?
+ opt_long = 1;
+ break;
+ case 'n': // Solaris: match only the newest
+ if (opt_oldest|opt_negate|opt_newest)
+ usage (opt);
+ opt_newest = 1;
+ ++criteria_count;
+ break;
+ case 'o': // Solaris: match only the oldest
+ if (opt_oldest|opt_negate|opt_newest)
+ usage (opt);
+ opt_oldest = 1;
+ ++criteria_count;
+ break;
+ case 's': // Solaris: match by session ID -- zero means self
+ opt_sid = split_list (optarg, conv_sid);
+ if (opt_sid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 't': // Solaris: match by tty
+ opt_term = split_list (optarg, conv_str);
+ if (opt_term == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 'u': // Solaris: match by euid/egroup
+ opt_euid = split_list (optarg, conv_uid);
+ if (opt_euid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 'v': // Solaris: as in grep, invert the matching (uh... applied after selection I think)
+ if (opt_oldest|opt_negate|opt_newest)
+ usage (opt);
+ opt_negate = 1;
+ break;
+ // OpenBSD -x, being broken, does a plain string
+ case 'x': // Solaris: use ^(regexp)$ in place of regexp (FreeBSD too)
+ opt_exact = 1;
+ break;
+// case 'z': // Solaris: match by zone ID
+// break;
+ case '?':
+ usage (optopt?optopt:opt);
+ break;
+ }
+ }
+
+ if(opt_lock && !opt_pidfile){
+ fprintf(stderr, "%s: -L without -F makes no sense\n",progname);
+ usage(0);
+ }
+
+ if(opt_pidfile){
+ opt_pid = read_pidfile();
+ if(!opt_pid){
+ fprintf(stderr, "%s: pidfile not valid\n",progname);
+ usage(0);
+ }
+ }
+
+ if (argc - optind == 1)
+ opt_pattern = argv[optind];
+ else if (argc - optind > 1)
+ usage (0);
+ else if (criteria_count == 0) {
+ fprintf (stderr, "%s: No matching criteria specified\n",
+ progname);
+ usage (0);
+ }
+}
+
+
+int main (int argc, char *argv[])
+{
+ union el *procs;
+ int num;
+
+ parse_opts (argc, argv);
+
+ procs = select_procs (&num);
+ if (i_am_pkill) {
+ int i;
+ for (i = 0; i < num; i++) {
+ if (kill (procs[i].num, opt_signal) != -1) continue;
+ if (errno==ESRCH) continue; // gone now, which is OK
+ fprintf (stderr, "pkill: %ld - %s\n",
+ procs[i].num, strerror (errno));
+ }
+ } else {
+ if (opt_count) {
+ fprintf(stdout, "%ld\n", num);
+ } else {
+ if (opt_long)
+ output_strlist (procs,num);
+ else
+ output_numlist (procs,num);
+ }
+ }
+ return !num; // exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE)
+}
diff --git a/smartt-top/pkill.1 b/smartt-top/pkill.1
new file mode 100644
index 0000000..0e94b52
--- /dev/null
+++ b/smartt-top/pkill.1
@@ -0,0 +1 @@
+.so man1/pgrep.1
diff --git a/smartt-top/pmap.1 b/smartt-top/pmap.1
new file mode 100644
index 0000000..de03e87
--- /dev/null
+++ b/smartt-top/pmap.1
@@ -0,0 +1,43 @@
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for pmap.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan.
+.\"
+.TH PMAP 1 "October 26, 2002" "Linux" "Linux User's Manual"
+.SH NAME
+pmap \- report memory map of a process
+
+.SH SYNOPSIS
+.B pmap
+.RB [ \-x | \-d ]
+.RB [ \-q ]
+.I pid
+\& ...
+.br
+.B pmap \-V
+
+.SH DESCRIPTION
+The pmap command reports the memory map of a process or processes.
+
+.SH "GENERAL OPTIONS"
+.TS
+lB l l.
+\-x extended Show the extended format.
+\-d device Show the device format.
+\-q quiet Do not display some header/footer lines.
+\-V show version Displays version of program.
+.TE
+
+.SH "SEE ALSO"
+.BR ps(1),
+.BR pgrep(1)
+
+.SH STANDARDS
+No standards apply, but pmap looks an awful lot like a SunOS command.
+
+.SH AUTHOR
+Albert Cahalan <albert@users.sf.net> wrote pmap in 2002, and is the current
+maintainer of the procps collection. Please send bug reports
+to <procps-feedback@lists.sf.net>.
diff --git a/smartt-top/pmap.c b/smartt-top/pmap.c
new file mode 100644
index 0000000..3dcb81e
--- /dev/null
+++ b/smartt-top/pmap.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2002 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include "proc/readproc.h"
+#include "proc/version.h"
+#include "proc/escape.h"
+
+static void usage(void) NORETURN;
+static void usage(void){
+ fprintf(stderr,
+ "Usage: pmap [-x | -d] [-q] [-A low,high] pid...\n"
+ "-x show details\n"
+ "-d show offset and device number\n"
+ "-q quiet; less header/footer info\n"
+ "-V show the version number\n"
+ "-A limit results to the given range\n"
+ );
+ exit(1);
+}
+
+
+static unsigned KLONG range_low;
+static unsigned KLONG range_high = ~0ull;
+
+static int V_option;
+static int r_option; // ignored -- for SunOS compatibility
+static int x_option;
+static int d_option;
+static int q_option;
+
+static unsigned shm_minor = ~0u;
+
+static void discover_shm_minor(void){
+ void *addr;
+ int shmid;
+ char mapbuf[256];
+
+ if(!freopen("/proc/self/maps", "r", stdin)) return;
+
+ // create
+ shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
+ if(shmid==-1) return; // failed; oh well
+ // attach
+ addr = shmat(shmid, NULL, SHM_RDONLY);
+ if(addr==(void*)-1) goto out_destroy;
+
+ while(fgets(mapbuf, sizeof mapbuf, stdin)){
+ char flags[32];
+ char *tmp; // to clean up unprintables
+ unsigned KLONG start, end;
+ unsigned long long file_offset, inode;
+ unsigned dev_major, dev_minor;
+ sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
+ tmp = strchr(mapbuf,'\n');
+ if(tmp) *tmp='\0';
+ tmp = mapbuf;
+ while(*tmp){
+ if(!isprint(*tmp)) *tmp='?';
+ tmp++;
+ }
+ if(start > (unsigned long)addr) continue;
+ if(dev_major) continue;
+ if(flags[3] != 's') continue;
+ if(strstr(mapbuf,"/SYSV")){
+ shm_minor = dev_minor;
+ break;
+ }
+ }
+
+ if(shmdt(addr)) perror("shmdt");
+
+out_destroy:
+ if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID");
+
+ return;
+}
+
+
+static const char *mapping_name(proc_t *p, unsigned KLONG addr, unsigned KLONG len, const char *mapbuf, unsigned showpath, unsigned dev_major, unsigned dev_minor, unsigned long long inode){
+ const char *cp;
+
+ if(!dev_major && dev_minor==shm_minor && strstr(mapbuf,"/SYSV")){
+ static char shmbuf[64];
+ snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode);
+ return shmbuf;
+ }
+
+ cp = strrchr(mapbuf,'/');
+ if(cp){
+ if(showpath) return strchr(mapbuf,'/');
+ return cp[1] ? cp+1 : cp;
+ }
+
+ cp = strchr(mapbuf,'/');
+ if(cp){
+ if(showpath) return cp;
+ return strrchr(cp,'/') + 1; // it WILL succeed
+ }
+
+ cp = " [ anon ]";
+ if( (p->start_stack >= addr) && (p->start_stack <= addr+len) ) cp = " [ stack ]";
+ return cp;
+}
+
+static int one_proc(proc_t *p){
+ char buf[32];
+ char mapbuf[9600];
+ char cmdbuf[512];
+ FILE *fp;
+ unsigned long total_shared = 0ul;
+ unsigned long total_private_readonly = 0ul;
+ unsigned long total_private_writeable = 0ul;
+
+ char *cp2=NULL;
+ unsigned long long rss = 0ull;
+ unsigned long long private_dirty = 0ull;
+ unsigned long long shared_dirty = 0ull;
+ unsigned long long total_rss = 0ull;
+ unsigned long long total_private_dirty = 0ull;
+ unsigned long long total_shared_dirty = 0ull;
+
+ // Overkill, but who knows what is proper? The "w" prog
+ // uses the tty width to determine this.
+ int maxcmd = 0xfffff;
+
+ sprintf(buf,"/proc/%u/maps",p->tgid);
+ if ( (fp = fopen(buf, "r")) == NULL) return 1;
+ if (x_option) {
+ sprintf(buf,"/proc/%u/smaps",p->tgid);
+ if ( (fp = freopen(buf, "r", fp)) == NULL) return 1;
+ }
+
+ escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS);
+ printf("%u: %s\n", p->tgid, cmdbuf);
+
+ if(!q_option && (x_option|d_option)){
+ if(x_option){
+ if(sizeof(KLONG)==4) printf("Address Kbytes RSS Dirty Mode Mapping\n");
+ else printf("Address Kbytes RSS Dirty Mode Mapping\n");
+ }
+ if(d_option){
+ if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n");
+ else printf("Address Kbytes Mode Offset Device Mapping\n");
+ }
+ }
+
+ while(fgets(mapbuf,sizeof mapbuf,fp)){
+ char flags[32];
+ char *tmp; // to clean up unprintables
+ unsigned KLONG start, end, diff=0;
+ unsigned long long file_offset, inode;
+ unsigned dev_major, dev_minor;
+ unsigned long long smap_value;
+ char smap_key[20];
+
+ /* hex values are lower case or numeric, keys are upper */
+ if (mapbuf[0] >= 'A' && mapbuf[0] <= 'Z') {
+ /* Its a key */
+ if (sscanf(mapbuf,"%20[^:]: %llu", smap_key, &smap_value) == 2) {
+ if (strncmp("Rss", smap_key, 3) == 0) {
+ rss = smap_value;
+ total_rss += smap_value;
+ continue;
+ }
+ if (strncmp("Shared_Dirty", smap_key, 12) == 0) {
+ shared_dirty = smap_value;
+ total_shared_dirty += smap_value;
+ continue;
+ }
+ if (strncmp("Private_Dirty", smap_key, 13) == 0) {
+ private_dirty = smap_value;
+ total_private_dirty += smap_value;
+ continue;
+ }
+ if (strncmp("Swap", smap_key, 4) == 0) { /*doesnt matter as long as last*/
+ printf(
+ (sizeof(KLONG)==8)
+ ? "%016"KLF"x %7lu %7llu %7llu %s %s\n"
+ : "%08lx %7lu %7llu %7llu %s %s\n",
+ start,
+ (unsigned long)(diff>>10),
+ rss,
+ (private_dirty + shared_dirty),
+ flags,
+ cp2
+ );
+ /* reset some counters */
+ rss = shared_dirty = private_dirty = 0ull;
+ continue;
+ }
+ /* Other keys */
+ continue;
+ }
+ }
+ sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
+
+ if(start > range_high)
+ break;
+ if(end < range_low)
+ continue;
+
+ tmp = strchr(mapbuf,'\n');
+ if(tmp) *tmp='\0';
+ tmp = mapbuf;
+ while(*tmp){
+ if(!isprint(*tmp)) *tmp='?';
+ tmp++;
+ }
+
+ diff = end-start;
+ if(flags[3]=='s') total_shared += diff;
+ if(flags[3]=='p'){
+ flags[3] = '-';
+ if(flags[1]=='w') total_private_writeable += diff;
+ else total_private_readonly += diff;
+ }
+
+ // format used by Solaris 9 and procps-3.2.0+
+ // an 'R' if swap not reserved (MAP_NORESERVE, SysV ISM shared mem, etc.)
+ flags[4] = '-';
+ flags[5] = '\0';
+
+ if(x_option){
+ cp2 = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
+ /* printed with the keys */
+ continue;
+ }
+ if(d_option){
+ const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
+ printf(
+ (sizeof(KLONG)==8)
+ ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n"
+ : "%08lx %7lu %s %016Lx %03x:%05x %s\n",
+ start,
+ (unsigned long)(diff>>10),
+ flags,
+ file_offset,
+ dev_major, dev_minor,
+ cp
+ );
+ }
+ if(!x_option && !d_option){
+ const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode);
+ printf(
+ (sizeof(KLONG)==8)
+ ? "%016"KLF"x %6luK %s %s\n"
+ : "%08lx %6luK %s %s\n",
+ start,
+ (unsigned long)(diff>>10),
+ flags,
+ cp
+ );
+ }
+
+ }
+
+
+
+
+ if(!q_option){
+ if(x_option){
+ if(sizeof(KLONG)==8){
+ printf("---------------- ------ ------ ------\n");
+ printf(
+ "total kB %15ld %7llu %7llu\n",
+ (total_shared + total_private_writeable + total_private_readonly) >> 10,
+ total_rss, (total_shared_dirty+total_private_dirty)
+
+ );
+ }else{
+ printf("-------- ------- ------- ------- -------\n");
+ printf(
+ "total kB %7ld - - -\n",
+ (total_shared + total_private_writeable + total_private_readonly) >> 10
+ );
+ }
+ }
+ if(d_option){
+ printf(
+ "mapped: %ldK writeable/private: %ldK shared: %ldK\n",
+ (total_shared + total_private_writeable + total_private_readonly) >> 10,
+ total_private_writeable >> 10,
+ total_shared >> 10
+ );
+ }
+ if(!x_option && !d_option){
+ if(sizeof(KLONG)==8) printf(" total %16ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
+ else printf(" total %8ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
+ }
+ }
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[]){
+ unsigned *pidlist;
+ unsigned count = 0;
+ PROCTAB* PT;
+ proc_t p;
+ int ret = 0;
+
+ if(argc<2) usage();
+ pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps
+
+ while(*++argv){
+ if(!strcmp("--version",*argv)){
+ V_option++;
+ continue;
+ }
+ if(**argv=='-'){
+ char *walk = *argv;
+ if(!walk[1]) usage();
+ while(*++walk){
+ switch(*walk){
+ case 'V':
+ V_option++;
+ break;
+ case 'x':
+ x_option++;
+ break;
+ case 'r':
+ r_option++;
+ break;
+ case 'd':
+ d_option++;
+ break;
+ case 'q':
+ q_option++;
+ break;
+ case 'A':{
+ char *arg1;
+ if(walk[1]){
+ arg1 = walk+1;
+ walk += strlen(walk)-1;
+ }else{
+ arg1 = *++argv;
+ if(!arg1)
+ usage();
+ }
+ char *arg2 = strchr(arg1,',');
+ if(arg2)
+ *arg2 = '\0';
+ arg2 = arg2 ? arg2++ : arg1;
+
+ if(*arg1)
+ range_low = STRTOUKL(arg1,&arg1,16);
+ if(*arg2)
+ range_high = STRTOUKL(arg2,&arg2,16);
+ if(*arg1 || *arg2)
+ usage();
+ }
+ break;
+ case 'a': // Sun prints anon/swap reservations
+ case 'F': // Sun forces hostile ptrace-like grab
+ case 'l': // Sun shows unresolved dynamic names
+ case 'L': // Sun shows lgroup info
+ case 's': // Sun shows page sizes
+ case 'S': // Sun shows swap reservations
+ default:
+ usage();
+ }
+ }
+ }else{
+ char *walk = *argv;
+ char *endp;
+ unsigned long pid;
+ if(!strncmp("/proc/",walk,6)){
+ walk += 6;
+ // user allowed to do: pmap /proc/*
+ if(*walk<'0' || *walk>'9') continue;
+ }
+ if(*walk<'0' || *walk>'9') usage();
+ pid = strtoul(walk, &endp, 0);
+ if(pid<1ul || pid>0x7ffffffful || *endp) usage();
+ pidlist[count++] = pid;
+ }
+ }
+
+ if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes
+ if(V_option){
+ if(count|x_option|r_option|d_option|q_option) usage();
+ fprintf(stdout, "pmap (%s)\n", procps_version);
+ return 0;
+ }
+ if(count<1) usage(); // no processes
+ if(d_option && x_option) usage();
+
+ discover_shm_minor();
+
+ pidlist[count] = 0; // old libproc interface is zero-terminated
+ PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist);
+ while(readproc(PT, &p)){
+ ret |= one_proc(&p);
+ if(p.cmdline) free((void*)*p.cmdline);
+ count--;
+ }
+ closeproc(PT);
+
+ if(count) ret |= 42; // didn't find all processes asked for
+ return ret;
+}
diff --git a/smartt-top/proc/COPYING b/smartt-top/proc/COPYING
new file mode 100644
index 0000000..92b8903
--- /dev/null
+++ b/smartt-top/proc/COPYING
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/smartt-top/proc/alloc.c b/smartt-top/proc/alloc.c
new file mode 100644
index 0000000..4a0aca7
--- /dev/null
+++ b/smartt-top/proc/alloc.c
@@ -0,0 +1,49 @@
+// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com
+// Copyright 2002 Albert Cahalan
+//
+// This file is placed under the conditions of the GNU Library
+// General Public License, version 2, or any later version.
+// See file COPYING for information on distribution conditions.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "alloc.h"
+
+void *xcalloc(void *pointer, int size) {
+ void * ret;
+ if (pointer)
+ free(pointer);
+ if (!(ret = calloc(1, size))) {
+ fprintf(stderr, "xcalloc: allocation error, size = %d\n", size);
+ exit(1);
+ }
+ return ret;
+}
+
+void *xmalloc(unsigned int size) {
+ void *p;
+
+ if (size == 0)
+ ++size;
+ p = malloc(size);
+ if (!p) {
+ fprintf(stderr, "xmalloc: malloc(%d) failed", size);
+ perror(NULL);
+ exit(1);
+ }
+ return(p);
+}
+
+void *xrealloc(void *oldp, unsigned int size) {
+ void *p;
+
+ if (size == 0)
+ ++size;
+ p = realloc(oldp, size);
+ if (!p) {
+ fprintf(stderr, "xrealloc: realloc(%d) failed", size);
+ perror(NULL);
+ exit(1);
+ }
+ return(p);
+}
diff --git a/smartt-top/proc/alloc.h b/smartt-top/proc/alloc.h
new file mode 100644
index 0000000..8c5016d
--- /dev/null
+++ b/smartt-top/proc/alloc.h
@@ -0,0 +1,14 @@
+#ifndef PROCPS_PROC_ALLOC_H
+#define PROCPS_PROC_ALLOC_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+extern void *xrealloc(void *oldp, unsigned int size) MALLOC;
+extern void *xmalloc(unsigned int size) MALLOC;
+extern void *xcalloc(void *pointer, int size) MALLOC;
+
+EXTERN_C_END
+
+#endif
diff --git a/smartt-top/proc/devname.c b/smartt-top/proc/devname.c
new file mode 100644
index 0000000..32ad954
--- /dev/null
+++ b/smartt-top/proc/devname.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "version.h"
+#include "devname.h"
+
+// This is the buffer size for a tty name. Any path is legal,
+// which makes PAGE_SIZE appropriate (see kernel source), but
+// that is only 99% portable and utmp only holds 32 anyway.
+// We need at least 20 for guess_name().
+#define TTY_NAME_SIZE 128
+
+/* Who uses what:
+ *
+ * tty_to_dev w (there is a fancy version in ps)
+ * dev_to_tty top, ps
+ */
+
+#if 0
+#include <sys/sysmacros.h>
+#define MAJOR_OF(d) ((unsigned)major(d))
+#define MINOR_OF(d) ((unsigned)minor(d))
+#else
+#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu )
+#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) )
+#undef major
+#undef minor
+#define major <-- do not use -->
+#define minor <-- do not use -->
+#endif
+
+typedef struct tty_map_node {
+ struct tty_map_node *next;
+ unsigned short devfs_type; // bool
+ unsigned short major_number;
+ unsigned minor_first;
+ unsigned minor_last;
+ char name[16];
+} tty_map_node;
+
+static tty_map_node *tty_map = NULL;
+
+/* Load /proc/tty/drivers for device name mapping use. */
+static void load_drivers(void){
+ char buf[10000];
+ char *p;
+ int fd;
+ int bytes;
+ fd = open("/proc/tty/drivers",O_RDONLY);
+ if(fd == -1) goto fail;
+ bytes = read(fd, buf, sizeof(buf) - 1);
+ if(bytes == -1) goto fail;
+ buf[bytes] = '\0';
+ p = buf;
+ while(( p = strstr(p, " /dev/") )){ // " /dev/" is the second column
+ tty_map_node *tmn;
+ int len;
+ char *end;
+ p += 6;
+ end = strchr(p, ' ');
+ if(!end) continue;
+ len = end - p;
+ tmn = calloc(1, sizeof(tty_map_node));
+ tmn->next = tty_map;
+ tty_map = tmn;
+ /* if we have a devfs type name such as /dev/tts/%d then strip the %d but
+ keep a flag. */
+ if(len >= 3 && !strncmp(end - 2, "%d", 2)){
+ len -= 2;
+ tmn->devfs_type = 1;
+ }
+ if(len >= sizeof tmn->name)
+ len = sizeof tmn->name - 1; // mangle it to avoid overflow
+ memcpy(tmn->name, p, len);
+ p = end; /* set p to point past the %d as well if there is one */
+ while(*p == ' ') p++;
+ tmn->major_number = atoi(p);
+ p += strspn(p, "0123456789");
+ while(*p == ' ') p++;
+ switch(sscanf(p, "%u-%u", &tmn->minor_first, &tmn->minor_last)){
+ default:
+ /* Can't finish parsing this line so we remove it from the list */
+ tty_map = tty_map->next;
+ free(tmn);
+ break;
+ case 1:
+ tmn->minor_last = tmn->minor_first;
+ break;
+ case 2:
+ break;
+ }
+ }
+fail:
+ if(fd != -1) close(fd);
+ if(!tty_map) tty_map = (tty_map_node *)-1;
+}
+
+/* Try to guess the device name from /proc/tty/drivers info. */
+static int driver_name(char *restrict const buf, unsigned maj, unsigned min){
+ struct stat sbuf;
+ tty_map_node *tmn;
+ if(!tty_map) load_drivers();
+ if(tty_map == (tty_map_node *)-1) return 0;
+ tmn = tty_map;
+ for(;;){
+ if(!tmn) return 0;
+ if(tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) break;
+ tmn = tmn->next;
+ }
+ sprintf(buf, "/dev/%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */
+ if(stat(buf, &sbuf) < 0){
+ if(tmn->devfs_type) return 0;
+ sprintf(buf, "/dev/%s", tmn->name); /* like "/dev/ttyZZ255" */
+ if(stat(buf, &sbuf) < 0) return 0;
+ }
+ if(min != MINOR_OF(sbuf.st_rdev)) return 0;
+ if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
+ return 1;
+}
+
+// major 204 is a mess -- "Low-density serial ports"
+static const char low_density_names[][6] = {
+"LU0", "LU1", "LU2", "LU3",
+"FB0",
+"SA0", "SA1", "SA2",
+"SC0", "SC1", "SC2", "SC3",
+"FW0", "FW1", "FW2", "FW3",
+"AM0", "AM1", "AM2", "AM3", "AM4", "AM5", "AM6", "AM7",
+"AM8", "AM9", "AM10", "AM11", "AM12", "AM13", "AM14", "AM15",
+"DB0", "DB1", "DB2", "DB3", "DB4", "DB5", "DB6", "DB7",
+"SG0",
+"SMX0", "SMX1", "SMX2",
+"MM0", "MM1",
+"CPM0", "CPM1", "CPM2", "CPM3", /* "CPM4", "CPM5", */ // bad allocation?
+"IOC0", "IOC1", "IOC2", "IOC3", "IOC4", "IOC5", "IOC6", "IOC7",
+"IOC8", "IOC9", "IOC10", "IOC11", "IOC12", "IOC13", "IOC14", "IOC15",
+"IOC16", "IOC17", "IOC18", "IOC19", "IOC20", "IOC21", "IOC22", "IOC23",
+"IOC24", "IOC25", "IOC26", "IOC27", "IOC28", "IOC29", "IOC30", "IOC31",
+"VR0", "VR1",
+"IOC84", "IOC85", "IOC86", "IOC87", "IOC88", "IOC89", "IOC90", "IOC91",
+"IOC92", "IOC93", "IOC94", "IOC95", "IOC96", "IOC97", "IOC98", "IOC99",
+"IOC100", "IOC101", "IOC102", "IOC103", "IOC104", "IOC105", "IOC106", "IOC107",
+"IOC108", "IOC109", "IOC110", "IOC111", "IOC112", "IOC113", "IOC114", "IOC115",
+"SIOC0", "SIOC1", "SIOC2", "SIOC3", "SIOC4", "SIOC5", "SIOC6", "SIOC7",
+"SIOC8", "SIOC9", "SIOC10", "SIOC11", "SIOC12", "SIOC13", "SIOC14", "SIOC15",
+"SIOC16", "SIOC17", "SIOC18", "SIOC19", "SIOC20", "SIOC21", "SIOC22", "SIOC23",
+"SIOC24", "SIOC25", "SIOC26", "SIOC27", "SIOC28", "SIOC29", "SIOC30", "SIOC31",
+"PSC0", "PSC1", "PSC2", "PSC3", "PSC4", "PSC5",
+"AT0", "AT1", "AT2", "AT3", "AT4", "AT5", "AT6", "AT7",
+"AT8", "AT9", "AT10", "AT11", "AT12", "AT13", "AT14", "AT15",
+"NX0", "NX1", "NX2", "NX3", "NX4", "NX5", "NX6", "NX7",
+"NX8", "NX9", "NX10", "NX11", "NX12", "NX13", "NX14", "NX15",
+"J0", // minor is 186
+"UL0","UL1","UL2","UL3",
+"xvc0", // FAIL -- "/dev/xvc0" lacks "tty" prefix
+"PZ0","PZ1","PZ2","PZ3",
+"TX0","TX1","TX2","TX3","TX4","TX5","TX6","TX7",
+"SC0","SC1","SC2","SC3",
+"MAX0","MAX1","MAX2","MAX3",
+};
+
+#if 0
+// test code
+#include <stdio.h>
+#define AS(x) (sizeof(x)/sizeof((x)[0]))
+int main(int argc, char *argv[]){
+ int i = 0;
+ while(i<AS(low_density_names)){
+ printf("%3d = /dev/tty%.*s\n",i,sizeof low_density_names[i],low_density_names[i]);
+ i++;
+ }
+ return 0;
+}
+#endif
+
+/* Try to guess the device name (useful until /proc/PID/tty is added) */
+static int guess_name(char *restrict const buf, unsigned maj, unsigned min){
+ struct stat sbuf;
+ int t0, t1;
+ unsigned tmpmin = min;
+
+ switch(maj){
+ case 3: /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
+ if(tmpmin > 255) return 0; // should never happen; array index protection
+ t0 = "pqrstuvwxyzabcde"[tmpmin>>4];
+ t1 = "0123456789abcdef"[tmpmin&0x0f];
+ sprintf(buf, "/dev/tty%c%c", t0, t1);
+ break;
+ case 4:
+ if(min<64){
+ sprintf(buf, "/dev/tty%d", min);
+ break;
+ }
+ sprintf(buf, "/dev/ttyS%d", min-64);
+ break;
+ case 11: sprintf(buf, "/dev/ttyB%d", min); break;
+ case 17: sprintf(buf, "/dev/ttyH%d", min); break;
+ case 19: sprintf(buf, "/dev/ttyC%d", min); break;
+ case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */
+ case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */
+ case 24: sprintf(buf, "/dev/ttyE%d", min); break;
+ case 32: sprintf(buf, "/dev/ttyX%d", min); break;
+ case 43: sprintf(buf, "/dev/ttyI%d", min); break;
+ case 46: sprintf(buf, "/dev/ttyR%d", min); break;
+ case 48: sprintf(buf, "/dev/ttyL%d", min); break;
+ case 57: sprintf(buf, "/dev/ttyP%d", min); break;
+ case 71: sprintf(buf, "/dev/ttyF%d", min); break;
+ case 75: sprintf(buf, "/dev/ttyW%d", min); break;
+ case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
+ case 105: sprintf(buf, "/dev/ttyV%d", min); break;
+ case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
+ /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
+ case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break;
+ case 148: sprintf(buf, "/dev/ttyT%d", min); break;
+ case 154: sprintf(buf, "/dev/ttySR%d", min); break;
+ case 156: sprintf(buf, "/dev/ttySR%d", min+256); break;
+ case 164: sprintf(buf, "/dev/ttyCH%d", min); break;
+ case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */
+ case 172: sprintf(buf, "/dev/ttyMX%d", min); break;
+ case 174: sprintf(buf, "/dev/ttySI%d", min); break;
+ case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */
+ case 204:
+ if(min >= sizeof low_density_names / sizeof low_density_names[0]) return 0;
+ memcpy(buf,"/dev/tty",8);
+ memcpy(buf+8, low_density_names[min], sizeof low_density_names[0]);
+ buf[8 + sizeof low_density_names[0]] = '\0';
+// snprintf(buf, 9 + sizeof low_density_names[0], "/dev/tty%.*s", sizeof low_density_names[0], low_density_names[min]);
+ break;
+ case 208: sprintf(buf, "/dev/ttyU%d", min); break;
+ case 216: sprintf(buf, "/dev/ttyUB%d", min); break; // "/dev/rfcomm%d" now?
+ case 224: sprintf(buf, "/dev/ttyY%d", min); break;
+ case 227: sprintf(buf, "/dev/3270/tty%d", min); break; /* bummer, HUGE */
+ case 229: sprintf(buf, "/dev/iseries/vtty%d", min); break; /* bummer, HUGE */
+ case 256: sprintf(buf, "/dev/ttyEQ%d", min); break;
+ default: return 0;
+ }
+ if(stat(buf, &sbuf) < 0) return 0;
+ if(min != MINOR_OF(sbuf.st_rdev)) return 0;
+ if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
+ return 1;
+}
+
+/* Linux 2.2 can give us filenames that might be correct.
+ * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
+ * and in /proc/PID/fd/255 (used by bash to remember the tty).
+ */
+static int link_name(char *restrict const buf, unsigned maj, unsigned min, int pid, const char *restrict name){
+ struct stat sbuf;
+ char path[32];
+ int count;
+ sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */
+ count = readlink(path,buf,TTY_NAME_SIZE-1);
+ if(count == -1) return 0;
+ buf[count] = '\0';
+ if(stat(buf, &sbuf) < 0) return 0;
+ if(min != MINOR_OF(sbuf.st_rdev)) return 0;
+ if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
+ return 1;
+}
+
+/* number --> name */
+unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) {
+ static char buf[TTY_NAME_SIZE];
+ char *restrict tmp = buf;
+ unsigned dev = dev_t_dev;
+ unsigned i = 0;
+ int c;
+ if(dev == 0u) goto no_tty;
+ if(linux_version_code > LINUX_VERSION(2, 7, 0)){ // not likely to make 2.6.xx
+ if(link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "tty" )) goto abbrev;
+ }
+ if(driver_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
+ if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/2" )) goto abbrev;
+ if( guess_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
+ if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/255")) goto abbrev;
+ // fall through if unable to find a device file
+no_tty:
+ strcpy(ret, "?");
+ return 1;
+abbrev:
+ if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5;
+ if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3;
+ if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4;
+ /* gotta check before we chop or we may chop someone else's memory */
+ if(chop + (unsigned long)(tmp-buf) <= sizeof buf)
+ tmp[chop] = '\0';
+ /* replace non-ASCII characters with '?' and return the number of chars */
+ for(;;){
+ c = *tmp;
+ tmp++;
+ if(!c) break;
+ i++;
+ if(c<=' ') c = '?';
+ if(c>126) c = '?';
+ *ret = c;
+ ret++;
+ }
+ *ret = '\0';
+ return i;
+}
+
+/* name --> number */
+int tty_to_dev(const char *restrict const name) {
+ struct stat sbuf;
+ static char buf[32];
+ if(name[0]=='/' && stat(name, &sbuf) >= 0) return sbuf.st_rdev;
+ snprintf(buf,32,"/dev/%s",name);
+ if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+ snprintf(buf,32,"/dev/tty%s",name);
+ if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+ snprintf(buf,32,"/dev/pts/%s",name);
+ if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+ return -1;
+}
diff --git a/smartt-top/proc/devname.h b/smartt-top/proc/devname.h
new file mode 100644
index 0000000..10c2cb6
--- /dev/null
+++ b/smartt-top/proc/devname.h
@@ -0,0 +1,17 @@
+#ifndef PROC_DEVNAME_H
+#define PROC_DEVNAME_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+#define ABBREV_DEV 1 /* remove /dev/ */
+#define ABBREV_TTY 2 /* remove tty */
+#define ABBREV_PTS 4 /* remove pts/ */
+
+extern unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags);
+
+extern int tty_to_dev(const char *restrict const name);
+
+EXTERN_C_END
+#endif
diff --git a/smartt-top/proc/escape.c b/smartt-top/proc/escape.c
new file mode 100644
index 0000000..0eb1418
--- /dev/null
+++ b/smartt-top/proc/escape.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include "procps.h"
+#include "escape.h"
+#include "readproc.h"
+
+#if (__GNU_LIBRARY__ >= 6)
+# include <wchar.h>
+# include <wctype.h>
+# include <stdlib.h> /* MB_CUR_MAX */
+# include <ctype.h>
+# include <langinfo.h>
+#endif
+
+#if (__GNU_LIBRARY__ >= 6)
+static int escape_str_utf8(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){
+ int my_cells = 0;
+ int my_bytes = 0;
+ mbstate_t s;
+
+ memset(&s, 0, sizeof (s));
+
+ for(;;) {
+ wchar_t wc;
+ int len = 0;
+
+ if(my_cells >= *maxcells || my_bytes+1 >= bufsize)
+ break;
+
+ if (!(len = mbrtowc (&wc, src, MB_CUR_MAX, &s)))
+ /* 'str' contains \0 */
+ break;
+
+ if (len < 0) {
+ /* invalid multibyte sequence -- zeroize state */
+ memset (&s, 0, sizeof (s));
+ *(dst++) = '?';
+ src++;
+ my_cells++;
+ my_bytes++;
+
+ } else if (len==1) {
+ /* non-multibyte */
+ *(dst++) = isprint(*src) ? *src : '?';
+ src++;
+ my_cells++;
+ my_bytes++;
+
+ } else if (!iswprint(wc)) {
+ /* multibyte - no printable */
+ *(dst++) = '?';
+ src+=len;
+ my_cells++;
+ my_bytes++;
+
+ } else {
+ /* multibyte - printable */
+ int wlen = wcwidth(wc);
+
+ if (wlen==0) {
+ // invisible multibyte -- we don't ignore it, because some terminal
+ // interpret it wrong and more safe is replace it with '?'
+ *(dst++) = '?';
+ src+=len;
+ my_cells++;
+ my_bytes++;
+ } else {
+ // multibyte - printable
+ // Got space?
+ if (my_cells+wlen > *maxcells || my_bytes+1+len >= bufsize) break;
+ // 0x9b is control byte for some terminals
+ if (memchr(src, 0x9B, len)) {
+ // unsafe multibyte
+ *(dst++) = '?';
+ src+=len;
+ my_cells++;
+ my_bytes++;
+ } else {
+ // safe multibyte
+ memcpy(dst, src, len);
+ my_cells += wlen;
+ dst += len;
+ my_bytes += len;
+ src += len;
+ }
+ }
+ }
+ //fprintf(stdout, "cells: %d\n", my_cells);
+ }
+ *(dst++) = '\0';
+
+ // fprintf(stderr, "maxcells: %d, my_cells; %d\n", *maxcells, my_cells);
+
+ *maxcells -= my_cells;
+ return my_bytes; // bytes of text, excluding the NUL
+}
+
+#endif /* __GNU_LIBRARY__ */
+
+/* sanitize a string via one-way mangle */
+int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){
+ unsigned char c;
+ int my_cells = 0;
+ int my_bytes = 0;
+ const char codes[] =
+ "Z-------------------------------"
+ "********************************"
+ "********************************"
+ "*******************************-"
+ "--------------------------------"
+ "********************************"
+ "********************************"
+ "********************************";
+
+#if (__GNU_LIBRARY__ >= 6)
+ static int utf_init=0;
+
+ if(utf_init==0){
+ /* first call -- check if UTF stuff is usable */
+ char *enc = nl_langinfo(CODESET);
+ utf_init = enc && strcasecmp(enc, "UTF-8")==0 ? 1 : -1;
+ }
+ if (utf_init==1)
+ /* UTF8 locales */
+ return escape_str_utf8(dst, src, bufsize, maxcells);
+#endif
+
+ if(bufsize > *maxcells+1) bufsize=*maxcells+1; // FIXME: assumes 8-bit locale
+
+ for(;;){
+ if(my_cells >= *maxcells || my_bytes+1 >= bufsize)
+ break;
+ c = (unsigned char) *(src++);
+ if(!c) break;
+ if(codes[c]=='-') c='?';
+ my_cells++;
+ my_bytes++;
+ *(dst++) = c;
+ }
+ *(dst++) = '\0';
+
+ *maxcells -= my_cells;
+ return my_bytes; // bytes of text, excluding the NUL
+}
+
+/////////////////////////////////////////////////
+
+// escape an argv or environment string array
+//
+// bytes arg means sizeof(buf)
+int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t bytes, int *cells){
+ size_t i = 0;
+
+ for(;;){
+ i += escape_str(dst+i, *src, bytes-i, cells);
+ if(bytes-i < 3) break; // need room for space, a character, and the NUL
+ src++;
+ if(!*src) break; // need something to print
+ if (*cells<=1) break; // need room for printed size of text
+ dst[i++] = ' ';
+ --*cells;
+ }
+ return i; // bytes, excluding the NUL
+}
+
+///////////////////////////////////////////////////
+
+int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags){
+ int overhead = 0;
+ int end = 0;
+
+ if(flags & ESC_ARGS){
+ const char **lc = (const char**)pp->cmdline;
+ if(lc && *lc) return escape_strlist(outbuf, lc, bytes, cells);
+ }
+ if(flags & ESC_BRACKETS){
+ overhead += 2;
+ }
+ if(flags & ESC_DEFUNCT){
+ if(pp->state=='Z') overhead += 10; // chars in " <defunct>"
+ else flags &= ~ESC_DEFUNCT;
+ }
+ if(overhead + 1 >= *cells){ // if no room for even one byte of the command name
+ // you'd damn well better have _some_ space
+// outbuf[0] = '-'; // Oct23
+ outbuf[1] = '\0';
+ return 1;
+ }
+ if(flags & ESC_BRACKETS){
+ outbuf[end++] = '[';
+ }
+ *cells -= overhead;
+ end += escape_str(outbuf+end, pp->cmd, bytes-overhead, cells);
+
+ // Hmmm, do we want "[foo] <defunct>" or "[foo <defunct>]"?
+ if(flags & ESC_BRACKETS){
+ outbuf[end++] = ']';
+ }
+ if(flags & ESC_DEFUNCT){
+ memcpy(outbuf+end, " <defunct>", 10);
+ end += 10;
+ }
+ outbuf[end] = '\0';
+ return end; // bytes, not including the NUL
+}
diff --git a/smartt-top/proc/escape.h b/smartt-top/proc/escape.h
new file mode 100644
index 0000000..172960f
--- /dev/null
+++ b/smartt-top/proc/escape.h
@@ -0,0 +1,22 @@
+#ifndef PROCPS_PROC_ESCAPE_H
+#define PROCPS_PROC_ESCAPE_H
+
+//#include <stdio.h>
+#include <sys/types.h>
+#include "procps.h"
+#include "readproc.h"
+
+EXTERN_C_BEGIN
+
+#define ESC_STRETCH 1 // since we mangle to '?' this is 1 (would be 4 for octal escapes)
+
+#define ESC_ARGS 0x1 // try to use cmdline instead of cmd
+#define ESC_BRACKETS 0x2 // if using cmd, put '[' and ']' around it
+#define ESC_DEFUNCT 0x4 // mark zombies with " <defunct>"
+
+extern int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t n, int *cells);
+extern int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells);
+extern int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags);
+
+EXTERN_C_END
+#endif
diff --git a/smartt-top/proc/ksym.c b/smartt-top/proc/ksym.c
new file mode 100644
index 0000000..2e5379f
--- /dev/null
+++ b/smartt-top/proc/ksym.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include "procps.h"
+#include "version.h"
+#include "sysinfo.h" /* smp_num_cpus */
+#include "wchan.h" // to verify prototypes
+
+#define KSYMS_FILENAME "/proc/ksyms"
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/would/be/nice/to/have/this/file"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked"
+#define linux_version_code 131598 /* ? */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.12"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12"
+#define linux_version_code 131852 /* 2.3.12 */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS"
+#define linux_version_code 131858 /* 2.3.18ac8 */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS"
+#define linux_version_code 131858 /* 2.3.18ac8 */
+#define smp_num_cpus 2
+#endif
+
+/* These are the symbol types, with relative popularity:
+ * ? w machine type junk for Alpha -- odd syntax
+ * ? S not for i386
+ * 4 W not for i386
+ * 60 R
+ * 100 A
+ * 125 r
+ * 363 s not for i386
+ * 858 B
+ * 905 g generated by modutils?
+ * 929 G generated by modutils?
+ * 1301 b
+ * 2750 D
+ * 4481 d
+ * 11417 ?
+ * 13666 t
+ * 15442 T
+ *
+ * For i386, that is: "RArBbDd?tT"
+ */
+
+#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS"
+
+/*
+ * '?' is a symbol type
+ * '.' is part of a name (versioning?)
+ * "\t[]" are for the module name in /proc/ksyms
+ */
+#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+/* System.map lines look like:
+ * hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n
+ *
+ * Alpha systems can start with a few lines that have the address replaced
+ * by space padding and a 'w' for the type. For those lines, the last space
+ * is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv
+ * (just one of those, always with a "_mv", then the newline)
+ *
+ * The /proc/ksyms lines are like System.map lines w/o the symbol type char.
+ * When odd features are used, the name part contains:
+ * "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}"
+ * It is likely that more crap will be added...
+ */
+
+typedef struct symb {
+ unsigned KLONG addr;
+ const char *name;
+} symb;
+
+/* These mostly rely on POSIX to make them zero. */
+
+static symb hashtable[256];
+
+static char *sysmap_data;
+static unsigned sysmap_room;
+static symb *sysmap_index;
+static unsigned sysmap_count;
+
+static char *ksyms_data;
+static unsigned ksyms_room = 4096;
+static symb *ksyms_index;
+static unsigned ksyms_count;
+static unsigned idx_room;
+
+/*********************************/
+
+/* Kill this: _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$
+ * We kill: (_R[^A-Z]*[0-9a-f]{8,})+$
+ *
+ * The loop should almost never be taken, but it has to be there.
+ * It gets rid of anything that _looks_ like a version code, even
+ * if a real version code has already been found. This is because
+ * the inability to perfectly recognize a version code may lead to
+ * symbol mangling, which in turn leads to mismatches between the
+ * /proc/ksyms and System.map data files.
+ */
+#if 0
+static char *chop_version(char *arg){
+ char *cp;
+ cp = strchr(arg,'\t');
+ if(cp) *cp = '\0'; /* kill trailing module name first */
+ for(;;){
+ char *p;
+ int len = 0;
+ cp = strrchr(arg, 'R');
+ if(!cp || cp<=arg+1 || cp[-1]!='_') break;
+ for(p=cp; *++p; ){
+ switch(*p){
+ default:
+ goto out;
+ case '0' ... '9':
+ case 'a' ... 'f':
+ len++;
+ continue;
+ case 'g' ... 'z':
+ case '_':
+ len=0;
+ continue;
+ }
+ }
+ if(len<8) break;
+ cp[-1] = '\0';
+ }
+out:
+ if(*arg=='G'){
+ int len = strlen(arg);
+ while( len>8 && !memcmp(arg,"GPLONLY_",8) ){
+ arg += 8;
+ len -= 8;
+ }
+ }
+ return arg;
+}
+#endif
+static char *chop_version(char *arg){
+ char *cp;
+ cp = strchr(arg,'\t');
+ if(cp) *cp = '\0'; /* kill trailing module name first */
+ for(;;){
+ int len;
+ cp = strrchr(arg, 'R');
+ if(!cp || cp<=arg+1 || cp[-1]!='_') break;
+ len=strlen(cp);
+ if(len<9) break;
+ if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break;
+ if(strspn(cp+len-8,"0123456789abcdef")!=8) break;
+ cp[-1] = '\0';
+ }
+ if(*arg=='G'){
+ int len = strlen(arg);
+ while( len>8 && !memcmp(arg,"GPLONLY_",8) ){
+ arg += 8;
+ len -= 8;
+ }
+ }
+ return arg;
+}
+
+/***********************************/
+
+static const symb *search(unsigned KLONG address, symb *idx, unsigned count){
+ unsigned left;
+ unsigned mid;
+ unsigned right;
+ if(!idx) return NULL; /* maybe not allocated */
+ if(address < idx[0].addr) return NULL;
+ if(address >= idx[count-1].addr) return idx+count-1;
+ left = 0;
+ right = count-1;
+ for(;;){
+ mid = (left + right) / 2;
+ if(address >= idx[mid].addr) left = mid;
+ if(address <= idx[mid].addr) right = mid;
+ if(right-left <= 1) break;
+ }
+ if(address == idx[right].addr) return idx+right;
+ return idx+left;
+}
+
+/*********************************/
+
+/* allocate if needed, read, and return buffer size */
+static void read_file(const char *restrict filename, char **bufp, unsigned *restrict roomp) {
+ int fd = 0;
+ ssize_t done;
+ char *buf = *bufp;
+ ssize_t total = 0;
+ unsigned room = *roomp;
+
+ if(!room) goto hell; /* failed before */
+ if(!buf) buf = malloc(room);
+ if(!buf) goto hell;
+open_again:
+ fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if(fd<0){
+ switch(errno){
+ case EINTR: goto open_again;
+ default: _exit(101);
+ case EACCES: /* somebody screwing around? */
+ /* FIXME: set a flag to disable symbol lookup? */
+ case ENOENT:; /* no module support */
+ }
+ goto hell;
+ }
+ for(;;){
+ done = read(fd, buf+total, room-total-1);
+ if(done==0) break; /* nothing left */
+ if(done==-1){
+ if(errno==EINTR) continue; /* try again */
+ perror("");
+ goto hell;
+ }
+ if(done==(ssize_t)room-total-1){
+ char *tmp;
+ total += done;
+ /* more to go, but no room in buffer */
+ room *= 2;
+ tmp = realloc(buf, room);
+ if(!tmp) goto hell;
+ buf = tmp;
+ continue;
+ }
+ if(done>0 && done<(ssize_t)room-total-1){
+ total += done;
+ continue; /* OK, we read some. Go do more. */
+ }
+ fprintf(stderr,"%ld can't happen\n", (long)done);
+ /* FIXME: memory leak */
+ _exit(42);
+ }
+ buf[total] = '\0'; // parse_ksyms() expects NUL-terminated file
+ *bufp = buf;
+ *roomp = room;
+ close(fd);
+ return;
+hell:
+ if(buf) free(buf);
+ *bufp = NULL;
+ *roomp = 0; /* this function will never work again */
+ total = 0;
+ if(fd>0) close(fd);
+ return;
+}
+
+/*********************************/
+
+static int parse_ksyms(void) {
+ char *endp;
+ if(!ksyms_room || !ksyms_data) goto quiet_goodbye;
+ endp = ksyms_data;
+ ksyms_count = 0;
+ if(idx_room) goto bypass; /* some space already allocated */
+ idx_room = 512;
+ for(;;){
+ void *vp;
+ idx_room *= 2;
+ vp = realloc(ksyms_index, sizeof(symb)*idx_room);
+ if(!vp) goto bad_alloc;
+ ksyms_index = vp;
+bypass:
+ for(;;){
+ char *saved;
+ if(!*endp) return 1;
+ saved = endp;
+ ksyms_index[ksyms_count].addr = STRTOUKL(endp, &endp, 16);
+ if(endp==saved || *endp != ' ') goto bad_parse;
+ endp++;
+ saved = endp;
+ endp = strchr(endp,'\n');
+ if(!endp) goto bad_parse; /* no newline */
+ *endp = '\0';
+ ksyms_index[ksyms_count].name = chop_version(saved);
+ ++endp;
+ if(++ksyms_count >= idx_room) break; /* need more space */
+ }
+ }
+
+ if(0){
+bad_alloc:
+ fprintf(stderr, "Warning: not enough memory available\n");
+ }
+ if(0){
+bad_parse:
+ fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n");
+ }
+quiet_goodbye:
+ idx_room = 0;
+ if(ksyms_data) free(ksyms_data) , ksyms_data = NULL;
+ ksyms_room = 0;
+ if(ksyms_index) free(ksyms_index) , ksyms_index = NULL;
+ ksyms_count = 0;
+ return 0;
+}
+
+/*********************************/
+
+#define VCNT 16
+
+static int sysmap_mmap(const char *restrict const filename, message_fn message) {
+ struct stat sbuf;
+ char *endp;
+ int fd;
+ char Version[32];
+ fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if(fd<0) return 0;
+ if(fstat(fd, &sbuf) < 0) goto bad_open;
+ if(!S_ISREG(sbuf.st_mode)) goto bad_open;
+ if(sbuf.st_size < 5000) goto bad_open; /* if way too small */
+ /* Would be shared read-only, but we want '\0' after each name. */
+ endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ sysmap_data = endp;
+ while(*endp==' '){ /* damn Alpha machine types */
+ if(strncmp(endp," w ", 19)) goto bad_parse;
+ endp += 19;
+ endp = strchr(endp,'\n');
+ if(!endp) goto bad_parse; /* no newline */
+ if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse;
+ endp++;
+ }
+ if(sysmap_data == (caddr_t) -1) goto bad_open;
+ close(fd);
+ fd = -1;
+ sprintf(Version, "Version_%d", linux_version_code);
+ sysmap_room = 512;
+ for(;;){
+ void *vp;
+ sysmap_room *= 2;
+ vp = realloc(sysmap_index, sizeof(symb)*sysmap_room);
+ if(!vp) goto bad_alloc;
+ sysmap_index = vp;
+ for(;;){
+ char *vstart;
+ if(endp - sysmap_data >= sbuf.st_size){ /* if we reached the end */
+ int i = VCNT; /* check VCNT times to verify this file */
+ if(*Version) goto bad_version;
+ if(!ksyms_index) return 1; /* if can not verify, assume success */
+ while(i--){
+#if 1
+ const symb *findme;
+ const symb *map_symb;
+ /* Choose VCNT entries from /proc/ksyms to test */
+ findme = ksyms_index + (ksyms_count*i/VCNT);
+ /* Search for them in the System.map */
+ map_symb = search(findme->addr, sysmap_index, sysmap_count);
+ if(map_symb){
+ if(map_symb->addr != findme->addr) continue;
+ /* backup to first matching address */
+ while (map_symb != sysmap_index){
+ if (map_symb->addr != (map_symb-1)->addr) break;
+ map_symb--;
+ }
+ /* search for name in symbols with same address */
+ while (map_symb != (sysmap_index+sysmap_count)){
+ if (map_symb->addr != findme->addr) break;
+ if (!strcmp(map_symb->name,findme->name)) goto good_match;
+ map_symb++;
+ }
+ map_symb--; /* backup to last symbol with matching address */
+ message("{%s} {%s}\n",map_symb->name,findme->name);
+ goto bad_match;
+ }
+good_match:;
+#endif
+ }
+ return 1; /* success */
+ }
+ sysmap_index[sysmap_count].addr = STRTOUKL(endp, &endp, 16);
+ if(*endp != ' ') goto bad_parse;
+ endp++;
+ if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse;
+ endp++;
+ if(*endp != ' ') goto bad_parse;
+ endp++;
+ vstart = endp;
+ endp = strchr(endp,'\n');
+ if(!endp) goto bad_parse; /* no newline */
+ *endp = '\0';
+ ++endp;
+ vstart = chop_version(vstart);
+ sysmap_index[sysmap_count].name = vstart;
+ if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0';
+ if(++sysmap_count >= sysmap_room) break; /* need more space */
+ }
+ }
+
+ if(0){
+bad_match:
+ message("Warning: %s does not match kernel data.\n", filename);
+ }
+ if(0){
+bad_version:
+ message("Warning: %s has an incorrect kernel version.\n", filename);
+ }
+ if(0){
+bad_alloc:
+ message("Warning: not enough memory available\n");
+ }
+ if(0){
+bad_parse:
+ message("Warning: %s not parseable as a System.map\n", filename);
+ }
+ if(0){
+bad_open:
+ message("Warning: %s could not be opened as a System.map\n", filename);
+ }
+
+ sysmap_room=0;
+ sysmap_count=0;
+ if(sysmap_index) free(sysmap_index);
+ sysmap_index = NULL;
+ if(fd>=0) close(fd);
+ if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1);
+ sysmap_data = NULL;
+ return 0;
+}
+
+/*********************************/
+
+static void read_and_parse(void){
+ static time_t stamp; /* after data gets old, load /proc/ksyms again */
+ if(time(NULL) != stamp){
+ read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room);
+ parse_ksyms();
+ memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */
+ stamp = time(NULL);
+ }
+}
+
+/*********************************/
+
+static void default_message(const char *restrict format, ...) __attribute__((format(printf,1,2)));
+static void default_message(const char *restrict format, ...) {
+ va_list arg;
+
+ va_start (arg, format);
+ vfprintf (stderr, format, arg);
+ va_end (arg);
+}
+
+/*********************************/
+
+static int use_wchan_file;
+
+int open_psdb_message(const char *restrict override, message_fn message) {
+ static const char *sysmap_paths[] = {
+ "/boot/System.map-%s",
+ "/boot/System.map",
+ "/lib/modules/%s/System.map",
+ "/usr/src/linux/System.map",
+ "/System.map",
+ NULL
+ };
+ struct stat sbuf;
+ struct utsname uts;
+ char path[128];
+ const char **fmt = sysmap_paths;
+ const char *sm;
+
+#ifdef SYSMAP_FILENAME /* debug feature */
+ override = SYSMAP_FILENAME;
+#endif
+
+ // first allow for a user-selected System.map file
+ if(
+ (sm=override)
+ ||
+ (sm=getenv("PS_SYSMAP"))
+ ||
+ (sm=getenv("PS_SYSTEM_MAP"))
+ ){
+ if(!have_privs){
+ read_and_parse();
+ if(sysmap_mmap(sm, message)) return 0;
+ }
+ /* failure is better than ignoring the user & using bad data */
+ return -1; /* ought to return "Namelist not found." */
+ }
+
+ // next try the Linux 2.5.xx method
+ if(!stat("/proc/self/wchan", &sbuf)){
+ use_wchan_file = 1; // hack
+ return 0;
+ }
+
+ // finally, search for the System.map file
+ uname(&uts);
+ path[sizeof path - 1] = '\0';
+ do{
+ int did_ksyms = 0;
+ snprintf(path, sizeof path - 1, *fmt, uts.release);
+ if(!stat(path, &sbuf)){
+ if (did_ksyms++) read_and_parse();
+ if (sysmap_mmap(path, message)) return 0;
+ }
+ }while(*++fmt);
+ /* TODO: Without System.map, no need to keep ksyms loaded. */
+ return -1;
+}
+
+/***************************************/
+
+int open_psdb(const char *restrict override) {
+ return open_psdb_message(override, default_message);
+}
+
+/***************************************/
+
+static const char * read_wchan_file(unsigned pid){
+ static char buf[64];
+ const char *ret = buf;
+ ssize_t num;
+ int fd;
+
+ snprintf(buf, sizeof buf, "/proc/%d/wchan", pid);
+ fd = open(buf, O_RDONLY);
+ if(fd==-1) return "?";
+ num = read(fd, buf, sizeof buf - 1);
+ close(fd);
+ if(num<1) return "?"; // allow for "0"
+ buf[num] = '\0';
+
+ if(buf[0]=='0' && buf[1]=='\0') return "-";
+
+ // would skip over numbers if they existed -- but no
+
+ // lame ppc64 has a '.' in front of every name
+ if(*ret=='.') ret++;
+ switch(*ret){
+ case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break;
+ case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break;
+ case '_': while(*ret=='_') ret++; break;
+ }
+ return ret;
+}
+
+/***************************************/
+
+static const symb fail = { .name = "?" };
+static const char dash[] = "-";
+static const char star[] = "*";
+
+#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */
+
+/* return pointer to temporary static buffer with function name */
+const char * lookup_wchan(unsigned KLONG address, unsigned pid) {
+ const symb *mod_symb;
+ const symb *map_symb;
+ const symb *good_symb;
+ const char *ret;
+ unsigned hash;
+
+ // can't cache it due to a race condition :-(
+ if(use_wchan_file) return read_wchan_file(pid);
+
+ if(!address) return dash;
+ if(!~address) return star;
+
+ read_and_parse();
+ hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */
+ if(hashtable[hash].addr == address) return hashtable[hash].name;
+ mod_symb = search(address, ksyms_index, ksyms_count);
+ if(!mod_symb) mod_symb = &fail;
+ map_symb = search(address, sysmap_index, sysmap_count);
+ if(!map_symb) map_symb = &fail;
+
+ /* which result is closest? */
+ good_symb = (mod_symb->addr > map_symb->addr)
+ ? mod_symb
+ : map_symb
+ ;
+ if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail;
+
+ /* good_symb->name has the data, but needs to be trimmed */
+ ret = good_symb->name;
+ // lame ppc64 has a '.' in front of every name
+ if(*ret=='.') ret++;
+ switch(*ret){
+ case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break;
+ case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break;
+ case '_': while(*ret=='_') ret++; break;
+ }
+ /* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */
+
+ /* cache name after abbreviation */
+ hashtable[hash].addr = address;
+ hashtable[hash].name = ret;
+
+ return ret;
+}
diff --git a/smartt-top/proc/library.map b/smartt-top/proc/library.map
new file mode 100644
index 0000000..8d69cd5
--- /dev/null
+++ b/smartt-top/proc/library.map
@@ -0,0 +1,24 @@
+# for --version-script
+# WTF is the syntax for this file?
+# Give me a BNF, man!
+_3_2_5 {
+global:
+ __cyg_profile_func_enter; __cyg_profile_func_exit; main;
+
+ readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command;
+ escape_str; escape_strlist;
+ openproc; closeproc; freeproc; allocsupgrp; freesupgrp;
+ tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
+ display_version; procps_version; linux_version_code;
+ Hertz; smp_num_cpus; have_privs;
+ sprint_uptime; uptime; user_from_uid; print_uptime; loadavg;
+ pretty_print_signals; print_given_signals; unix_print_signals; signal_name_to_number; signal_number_to_name;
+ meminfo; vminfo; getstat; getdiskstat; getpartitions_num; getslabinfo; get_pid_digits;
+ kb_active; kb_inactive; kb_main_buffers; kb_main_cached;
+ kb_main_free; kb_main_total; kb_main_used; kb_swap_free;
+ kb_swap_total; kb_swap_used; kb_main_shared;
+ kb_low_total; kb_low_free; kb_high_total; kb_high_free;
+ vm_pgpgin; vm_pgpgout; vm_pswpin; vm_pswpout;
+ free_slabinfo; put_slabinfo; get_slabinfo; get_proc_stats;
+local: *;
+};
diff --git a/smartt-top/proc/module.mk b/smartt-top/proc/module.mk
new file mode 100644
index 0000000..c4e85b4
--- /dev/null
+++ b/smartt-top/proc/module.mk
@@ -0,0 +1,130 @@
+# This file gets included into the main Makefile, in the top directory.
+
+# Ideally, we want something like this:
+#
+# /lib/libproc.so.w ELF soname ('w' is a digit, starting from 1)
+# /lib/procps-x.y.z.so file itself (x.y.z is the procps version)
+# /lib/libproc.so for linking, UNSUPPORTED
+# /usr/lib/libproc.a for linking, UNSUPPORTED
+# proc/libproc.so.w as above, if testing with LD_LIBRARY_PATH
+# proc/whatever if testing with LD_PRELOAD
+# proc/libproc.a for static build
+#
+# Without a stable ABI, there's no point in having any of that.
+# Without a stable API, there's no point in having the *.a file.
+#
+# A new ELF soname is required for every big ABI change. To conserve
+# numbers for future use, the ELF soname can be set equal to the
+# file name until some future date when a stable ABI is declared.
+
+SHARED ?= 1
+
+# for lib$(NAME).so and /usr/include/($NAME) and such
+NAME := proc
+
+LIBVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION)
+ABIVERSION := 0
+
+SOFILE := lib$(NAME)-$(LIBVERSION).so
+ifneq ($(ABIVERSION),0)
+SOLINK := lib$(NAME).so
+SONAME := lib$(NAME).so.$(ABIVERSION)
+else
+SONAME := $(SOFILE)
+SOLINK := $(SOFILE)
+endif
+
+ANAME := lib$(NAME).a
+
+############
+
+FPIC := -fpic
+
+ifeq ($(SHARED),1)
+ALL += proc/$(SONAME)
+INSTALL += ldconfig
+LIBFLAGS := -DSHARED=1 $(FPIC)
+# This is in gcc 3.5, but exported functions must be marked.
+#LIBFLAGS += $(call check_gcc,-fvisibility=hidden,)
+LIBPROC := proc/$(SONAME)
+else
+ALL += proc/$(ANAME)
+#INSTALL += $(usr/lib)$(ANAME)
+LIBFLAGS := -DSHARED=0
+LIBPROC := proc/$(ANAME)
+endif
+
+LIBSRC := $(wildcard proc/*.c)
+LIBHDR := $(wildcard proc/*.h)
+LIBOBJ := $(LIBSRC:.c=.o)
+
+# Separate rule for this directory, to use -fpic or -fPIC
+$(filter-out proc/version.o,$(LIBOBJ)): proc/%.o: proc/%.c
+ $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) $< -o $@
+
+LIB_X := COPYING module.mk library.map
+TARFILES += $(LIBSRC) $(LIBHDR) $(addprefix proc/,$(LIB_X))
+
+
+# Clean away all output files, .depend, and symlinks.
+# Use wildcards in case the version has changed.
+CLEAN += proc/.depend proc/lib*.so* proc/lib*.a $(LIBOBJ)
+DIRS += proc/
+
+proc/$(ANAME): $(LIBOBJ)
+ $(AR) rcs $@ $^
+
+#proc/$(SONAME): proc/library.map
+proc/$(SONAME): $(LIBOBJ)
+ $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -shared -Wl,-soname,$(SONAME) -Wl,--version-script=proc/library.map -o $@ $^ -lc
+
+
+# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT
+proc/.depend: $(LIBSRC) $(LIBHDR)
+ $(strip $(CC) $(ALL_CPPFLAGS) $(LIB_CFLAGS) -MM -MG $(LIBSRC) > $@)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),tar)
+ifneq ($(MAKECMDGOALS),extratar)
+ifneq ($(MAKECMDGOALS),beta)
+-include proc/.depend
+endif
+endif
+endif
+endif
+
+#################### install rules ###########################
+
+$(lib)$(SOFILE) : proc/$(SONAME)
+ $(install) --mode a=rx $< $@
+
+ifneq ($(SOLINK),$(SOFILE))
+.PHONY: $(lib)$(SOLINK)
+$(lib)$(SOLINK) : $(lib)$(SOFILE)
+ cd $(lib) && $(ln_sf) $(SOFILE) $(SOLINK)
+endif
+
+ifneq ($(SONAME),$(SOFILE))
+.PHONY: $(lib)$(SONAME)
+$(lib)$(SONAME) : $(lib)$(SOFILE)
+ cd $(lib) && $(ln_sf) $(SOFILE) $(SONAME)
+endif
+
+.PHONY: ldconfig
+ldconfig : $(lib)$(SONAME) $(lib)$(SOLINK)
+ $(ldconfig)
+
+$(usr/lib)$(ANAME) : proc/$(ANAME)
+ $(install) --mode a=r $< $@
+
+# Junk anyway... supposed to go in /usr/include/$(NAME)
+#INSTALL += $(addprefix $(include),$(HDRFILES))
+#
+#$(addprefix $(include),$(HDRFILES)): $(include)% : proc/%
+#$(include)% : proc/%
+# $(install) --mode a=r $< $@
+
+##################################################################
+
+proc/version.o: proc/version.c proc/version.h
+ $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c -o $@ $<
diff --git a/smartt-top/proc/procps.h b/smartt-top/proc/procps.h
new file mode 100644
index 0000000..a70e925
--- /dev/null
+++ b/smartt-top/proc/procps.h
@@ -0,0 +1,112 @@
+#ifndef PROCPS_PROC_PROCPS_H
+#define PROCPS_PROC_PROCPS_H
+
+#ifdef __cplusplus
+#define EXTERN_C_BEGIN extern "C" {
+#define EXTERN_C_END }
+#else
+#define EXTERN_C_BEGIN
+#define EXTERN_C_END
+#endif
+
+// Some ports make the mistake of running a 32-bit userspace
+// on a 64-bit kernel. Shame on them. It's not at all OK to
+// make everything "long long", since that causes unneeded
+// slowness on 32-bit hardware.
+//
+// SPARC: The 32-bit kernel was looking like an ex-penguin,
+// but it lives! ("I'm not dead yet.") So, 64-bit users will
+// just have to compile for 64-bit. Aw, the suffering.
+//
+// MIPS: Used 32-bit for embedded systems and obsolete hardware.
+// The 64-bit systems use an n32 format executable, defining
+// _ABIN32 to indicate this. Since n32 doesn't currently run on
+// any 32-bit system, nobody get hurt if it's bloated. Not that
+// this is sane of course, but it won't hurt the 32-bit users.
+// __mips_eabi means eabi, which comes in both sizes, but isn't used.
+//
+// PowerPC: Big ugly problem! 32-bit Macs are still popular. :-/
+//
+// x86-64: So far, nobody has been dumb enough to go 32-bit.
+//
+// Unknown: PA-RISC and zSeries
+//
+#if defined(k64test) || (defined(_ABIN32) && _MIPS_SIM == _ABIN32)
+#define KLONG long long // not typedef; want "unsigned KLONG" to work
+#define KLF "L"
+#define STRTOUKL strtoull
+#else
+#define KLONG long
+#define KLF "l"
+#define STRTOUKL strtoul
+#endif
+
+// since gcc-2.5
+#define NORETURN __attribute__((__noreturn__))
+#define FUNCTION __attribute__((__const__)) // no access to global mem, even via ptr, and no side effect
+
+#if !defined(restrict) && __STDC_VERSION__ < 199901
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 92
+#define restrict __restrict__
+#else
+#warning No restrict keyword?
+#define restrict
+#endif
+#endif
+
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 96
+// won't alias anything, and aligned enough for anything
+#define MALLOC __attribute__ ((__malloc__))
+// no side effect, may read globals
+#define PURE __attribute__ ((__pure__))
+// tell gcc what to expect: if(unlikely(err)) die(err);
+#define likely(x) __builtin_expect(!!(x),1)
+#define unlikely(x) __builtin_expect(!!(x),0)
+#define expected(x,y) __builtin_expect((x),(y))
+#else
+#define MALLOC
+#define PURE
+#define likely(x) (x)
+#define unlikely(x) (x)
+#define expected(x,y) (x)
+#endif
+
+#if SHARED==1 && (__GNUC__ > 2 || __GNUC_MINOR__ >= 96)
+#define LABEL_OFFSET
+#endif
+
+#define STRINGIFY_ARG(a) #a
+#define STRINGIFY(a) STRINGIFY_ARG(a)
+
+// marks old junk, to warn non-procps library users
+#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
+#define OBSOLETE __attribute__((deprecated))
+#else
+#define OBSOLETE
+#endif
+
+#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 1 ) || __GNUC__ > 3
+// Tells gcc that function is library-internal;
+// so no need to do dynamic linking at run-time.
+// This might work with slightly older compilers too.
+#define HIDDEN __attribute__((visibility("hidden")))
+// The opposite, in case -fvisibility=hidden used
+#define EXPORT __attribute__((visibility("default")))
+// Tell g++ that a function won't throw exceptions.
+#define NOTHROW __attribute__((__nothrow__))
+#else
+#define HIDDEN
+#define EXPORT
+#define NOTHROW
+#endif
+
+// Like HIDDEN, but for an alias that gets created.
+// In gcc-3.2 there is an alias+hidden conflict.
+// Many will have patched this bug, but oh well.
+#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 2 ) || __GNUC__ > 3
+#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x),visibility("hidden")))
+#else
+#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x)))
+#endif
+
+#endif
diff --git a/smartt-top/proc/pwcache.c b/smartt-top/proc/pwcache.c
new file mode 100644
index 0000000..ab7e528
--- /dev/null
+++ b/smartt-top/proc/pwcache.c
@@ -0,0 +1,77 @@
+// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com
+// Note: most likely none of his code remains
+//
+// Copyright 2002, Albert Cahalan
+//
+// This file is placed under the conditions of the GNU Library
+// General Public License, version 2, or any later version.
+// See file COPYING for information on distribution conditions.
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include "alloc.h"
+#include "pwcache.h"
+#include <grp.h>
+
+// might as well fill cache lines... else we waste memory anyway
+
+#define HASHSIZE 64 /* power of 2 */
+#define HASH(x) ((x) & (HASHSIZE - 1))
+
+static struct pwbuf {
+ struct pwbuf *next;
+ uid_t uid;
+ char name[P_G_SZ];
+} *pwhash[HASHSIZE];
+
+char *user_from_uid(uid_t uid) {
+ struct pwbuf **p;
+ struct passwd *pw;
+
+ p = &pwhash[HASH(uid)];
+ while (*p) {
+ if ((*p)->uid == uid)
+ return((*p)->name);
+ p = &(*p)->next;
+ }
+ *p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf));
+ (*p)->uid = uid;
+ pw = getpwuid(uid);
+ if(!pw || strlen(pw->pw_name) >= P_G_SZ)
+ sprintf((*p)->name, "%u", uid);
+ else
+ strcpy((*p)->name, pw->pw_name);
+
+ (*p)->next = NULL;
+ return((*p)->name);
+}
+
+static struct grpbuf {
+ struct grpbuf *next;
+ gid_t gid;
+ char name[P_G_SZ];
+} *grphash[HASHSIZE];
+
+char *group_from_gid(gid_t gid) {
+ struct grpbuf **g;
+ struct group *gr;
+
+ g = &grphash[HASH(gid)];
+ while (*g) {
+ if ((*g)->gid == gid)
+ return((*g)->name);
+ g = &(*g)->next;
+ }
+ *g = (struct grpbuf *) malloc(sizeof(struct grpbuf));
+ (*g)->gid = gid;
+ gr = getgrgid(gid);
+ if (!gr || strlen(gr->gr_name) >= P_G_SZ)
+ sprintf((*g)->name, "%u", gid);
+ else
+ strcpy((*g)->name, gr->gr_name);
+ (*g)->next = NULL;
+ return((*g)->name);
+}
diff --git a/smartt-top/proc/pwcache.h b/smartt-top/proc/pwcache.h
new file mode 100644
index 0000000..678554d
--- /dev/null
+++ b/smartt-top/proc/pwcache.h
@@ -0,0 +1,17 @@
+#ifndef PROCPS_PROC_PWCACHE_H
+#define PROCPS_PROC_PWCACHE_H
+
+#include <sys/types.h>
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+// used in pwcache and in readproc to set size of username or groupname
+#define P_G_SZ 20
+
+extern char *user_from_uid(uid_t uid);
+extern char *group_from_gid(gid_t gid);
+
+EXTERN_C_END
+
+#endif
diff --git a/smartt-top/proc/readproc.c b/smartt-top/proc/readproc.c
new file mode 100644
index 0000000..4203071
--- /dev/null
+++ b/smartt-top/proc/readproc.c
@@ -0,0 +1,1128 @@
+/*
+ * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+ * Copyright (C) 1996 Charles L. Blake.
+ * Copyright (C) 1998 Michael K. Johnson
+ * Copyright 1998-2003 Albert Cahalan
+ * May be distributed under the conditions of the
+ * GNU Library General Public License; a copy is in COPYING
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "version.h"
+#include "readproc.h"
+#include "alloc.h"
+#include "pwcache.h"
+#include "devname.h"
+#include "procps.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/dir.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// sometimes it's easier to do this manually, w/o gcc helping
+#ifdef PROF
+extern void __cyg_profile_func_enter(void*,void*);
+#define ENTER(x) __cyg_profile_func_enter((void*)x,(void*)x)
+#define LEAVE(x) __cyg_profile_func_exit((void*)x,(void*)x)
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#endif
+
+// convert hex string to unsigned long long
+static unsigned long long unhex(const char *restrict cp){
+ unsigned long long ull = 0;
+ for(;;){
+ char c = *cp++;
+ if(unlikely(c<0x30)) break;
+ ull = (ull<<4) | (c - (c>0x57) ? 0x57 : 0x30) ;
+ }
+ return ull;
+}
+
+static int task_dir_missing;
+
+///////////////////////////////////////////////////////////////////////////
+
+typedef struct status_table_struct {
+ unsigned char name[7]; // /proc/*/status field name
+ unsigned char len; // name length
+#ifdef LABEL_OFFSET
+ long offset; // jump address offset
+#else
+ void *addr;
+#endif
+} status_table_struct;
+
+#ifdef LABEL_OFFSET
+#define F(x) {#x, sizeof(#x)-1, (long)(&&case_##x-&&base)},
+#else
+#define F(x) {#x, sizeof(#x)-1, &&case_##x},
+#endif
+#define NUL {"", 0, 0},
+
+// Derived from:
+// gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf
+//
+// Suggested method:
+// Grep this file for "case_", then strip those down to the name.
+// (leave the colon and newline) So "Pid:\n" and "Threads:\n"
+// would be lines in the file. (no quote, no escape, etc.)
+//
+// Watch out for name size in the status_table_struct (grrr, expanding)
+// and the number of entries (we mask with 63 for now). The table
+// must be padded out to 64 entries, maybe 128 in the future.
+
+static void status2proc(char *S, proc_t *restrict P, int is_proc){
+ long Threads = 0;
+ long Tgid = 0;
+ long Pid = 0;
+ int hash = 0;
+ int isupgid = 0;
+
+ static const unsigned char asso[] =
+ {
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 0, 66,
+ 66, 66, 66, 66, 66, 66, 3, 30, 20, 30,
+ 66, 25, 66, 20, 66, 66, 30, 66, 25, 66,
+ 0, 66, 8, 10, 3, 18, 5, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 3, 66, 10,
+ 0, 0, 66, 25, 66, 5, 66, 66, 66, 25,
+ 66, 5, 66, 66, 0, 66, 0, 0, 66, 66,
+ 25, 66, 66, 66, 66, 66, 66, 66
+ };
+
+ static const status_table_struct table[] = {
+ F(Pid)
+ NUL NUL
+ F(Threads)
+ NUL
+ F(PPid)
+ NUL NUL
+ F(Tgid)
+ NUL
+ F(ShdPnd)
+ NUL NUL
+ F(State)
+ NUL
+ F(VmStk)
+ NUL NUL
+ F(Uid)
+ NUL
+ F(VmSize)
+ NUL NUL
+ F(VmRSS)
+ NUL
+ F(Gid)
+ NUL NUL
+ F(VmData)
+ NUL
+ F(Groups)
+ NUL NUL NUL NUL
+ F(SigPnd)
+ NUL NUL
+ F(SigBlk)
+ NUL
+ F(VmLib)
+ NUL NUL NUL NUL
+ F(VmLck)
+ NUL NUL NUL NUL
+ F(Name)
+ NUL NUL NUL NUL
+ F(SigIgn)
+ NUL NUL NUL NUL
+ F(VmExe)
+ NUL NUL NUL NUL
+ F(SigCgt)
+ };
+
+#undef F
+#undef NUL
+
+ENTER(0x220);
+
+ P->vm_size = 0;
+ P->vm_lock = 0;
+ P->vm_rss = 0;
+ P->vm_data = 0;
+ P->vm_stack= 0;
+ P->vm_exe = 0;
+ P->vm_lib = 0;
+ P->nlwp = 0;
+ P->nsupgid = 0;
+ P->supgid = NULL;
+ P->supgrp = NULL;
+ P->signal[0] = '\0'; // so we can detect it as missing for very old kernels
+
+ goto base;
+
+ for(;;){
+ char *colon;
+ status_table_struct entry;
+
+ // advance to next line
+ S = strchr(S, '\n');
+ if(unlikely(!S)) break; // if no newline
+ S++;
+
+ // examine a field name (hash and compare)
+ base:
+ if(unlikely(!*S)) break;
+ hash = asso[S[3]] + asso[S[2]] + asso[S[0]];
+ if (hash > 65) continue;
+ entry = table[hash];
+ colon = strchr(S, ':');
+ if(unlikely(!colon)) break;
+ if(unlikely(colon[1]!='\t')) break;
+ if(unlikely(colon-S != entry.len)) continue;
+ if(unlikely(memcmp(entry.name,S,colon-S))) continue;
+
+ S = colon+2; // past the '\t'
+
+#ifdef LABEL_OFFSET
+ goto *(&&base + entry.offset);
+#else
+ goto *entry.addr;
+#endif
+
+ case_Name:{
+ unsigned u = 0;
+ while(u < sizeof P->cmd - 1u){
+ int c = *S++;
+ if(unlikely(c=='\n')) break;
+ if(unlikely(c=='\0')) break; // should never happen
+ if(unlikely(c=='\\')){
+ c = *S++;
+ if(c=='\n') break; // should never happen
+ if(!c) break; // should never happen
+ if(c=='n') c='\n'; // else we assume it is '\\'
+ }
+ P->cmd[u++] = c;
+ }
+ P->cmd[u] = '\0';
+ S--; // put back the '\n' or '\0'
+ continue;
+ }
+#ifdef SIGNAL_STRING
+ case_ShdPnd:
+ memcpy(P->signal, S, 16);
+ P->signal[16] = '\0';
+ continue;
+ case_SigBlk:
+ memcpy(P->blocked, S, 16);
+ P->blocked[16] = '\0';
+ continue;
+ case_SigCgt:
+ memcpy(P->sigcatch, S, 16);
+ P->sigcatch[16] = '\0';
+ continue;
+ case_SigIgn:
+ memcpy(P->sigignore, S, 16);
+ P->sigignore[16] = '\0';
+ continue;
+ case_SigPnd:
+ memcpy(P->_sigpnd, S, 16);
+ P->_sigpnd[16] = '\0';
+ continue;
+#else
+ case_ShdPnd:
+ P->signal = unhex(S);
+ continue;
+ case_SigBlk:
+ P->blocked = unhex(S);
+ continue;
+ case_SigCgt:
+ P->sigcatch = unhex(S);
+ continue;
+ case_SigIgn:
+ P->sigignore = unhex(S);
+ continue;
+ case_SigPnd:
+ P->_sigpnd = unhex(S);
+ continue;
+#endif
+ case_State:
+ P->state = *S;
+ continue;
+ case_Tgid:
+ Tgid = strtol(S,&S,10);
+ continue;
+ case_Pid:
+ Pid = strtol(S,&S,10);
+ continue;
+ case_PPid:
+ P->ppid = strtol(S,&S,10);
+ continue;
+ case_Threads:
+ Threads = strtol(S,&S,10);
+ continue;
+ case_Uid:
+ P->ruid = strtol(S,&S,10);
+ P->euid = strtol(S,&S,10);
+ P->suid = strtol(S,&S,10);
+ P->fuid = strtol(S,&S,10);
+ continue;
+ case_Gid:
+ P->rgid = strtol(S,&S,10);
+ P->egid = strtol(S,&S,10);
+ P->sgid = strtol(S,&S,10);
+ P->fgid = strtol(S,&S,10);
+ continue;
+ case_Groups:
+ isupgid = 0;
+ if (*S != '\n'){ // Is there any supplementary group ?
+ P->supgid = (int *) xmalloc(0x0004 * sizeof(int));
+ int vctsize = 0x0004;
+ while (S[1] != '\n' && isupgid<INT_MAX){ // There is one blank before '\n'
+ if (isupgid == vctsize){
+ vctsize *= 2;
+ P->supgid = (int *)xrealloc(P->supgid,vctsize * sizeof(int));
+ }
+ P->supgid[isupgid++] = strtol(S,&S,10);
+ P->nsupgid++;
+ }
+ }
+ continue;
+ case_VmData:
+ P->vm_data = strtol(S,&S,10);
+ continue;
+ case_VmExe:
+ P->vm_exe = strtol(S,&S,10);
+ continue;
+ case_VmLck:
+ P->vm_lock = strtol(S,&S,10);
+ continue;
+ case_VmLib:
+ P->vm_lib = strtol(S,&S,10);
+ continue;
+ case_VmRSS:
+ P->vm_rss = strtol(S,&S,10);
+ continue;
+ case_VmSize:
+ P->vm_size = strtol(S,&S,10);
+ continue;
+ case_VmStk:
+ P->vm_stack = strtol(S,&S,10);
+ continue;
+ }
+
+#if 0
+ // recent kernels supply per-tgid pending signals
+ if(is_proc && *ShdPnd){
+ memcpy(P->signal, ShdPnd, 16);
+ P->signal[16] = '\0';
+ }
+#endif
+
+ // recent kernels supply per-tgid pending signals
+#ifdef SIGNAL_STRING
+ if(!is_proc || !P->signal[0]){
+ memcpy(P->signal, P->_sigpnd, 16);
+ P->signal[16] = '\0';
+ }
+#else
+ if(!is_proc || !have_process_pending){
+ P->signal = P->_sigpnd;
+ }
+#endif
+
+ // Linux 2.4.13-pre1 to max 2.4.xx have a useless "Tgid"
+ // that is not initialized for built-in kernel tasks.
+ // Only 2.6.0 and above have "Threads" (nlwp) info.
+
+ if(Threads){
+ P->nlwp = Threads;
+ P->tgid = Tgid; // the POSIX PID value
+ P->tid = Pid; // the thread ID
+ }else{
+ P->nlwp = 1;
+ P->tgid = Pid;
+ P->tid = Pid;
+ }
+
+LEAVE(0x220);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+// Reads /proc/*/stat files, being careful not to trip over processes with
+// names like ":-) 1 2 3 4 5 6".
+static void stat2proc(const char* S, proc_t *restrict P) {
+ unsigned num;
+ char* tmp;
+
+ENTER(0x160);
+
+ /* fill in default values for older kernels */
+ P->processor = 0;
+ P->rtprio = -1;
+ P->sched = -1;
+ P->nlwp = 0;
+
+ S = strchr(S, '(') + 1;
+ tmp = strrchr(S, ')');
+ num = tmp - S;
+ if(unlikely(num >= sizeof P->cmd)) num = sizeof P->cmd - 1;
+ memcpy(P->cmd, S, num);
+ P->cmd[num] = '\0';
+ S = tmp + 2; // skip ") "
+
+ num = sscanf(S,
+ "%c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu "
+ "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */
+ "%ld %ld "
+ "%d "
+ "%ld "
+ "%Lu " /* start_time */
+ "%lu "
+ "%ld "
+ "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
+ "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
+ "%"KLF"u %*lu %*lu "
+ "%d %d "
+ "%lu %lu",
+ &P->state,
+ &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
+ &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
+ &P->utime, &P->stime, &P->cutime, &P->cstime,
+ &P->priority, &P->nice,
+ &P->nlwp,
+ &P->alarm,
+ &P->start_time,
+ &P->vsize,
+ &P->rss,
+ &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
+/* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */
+ &P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */
+/* -- Linux 2.0.35 ends here -- */
+ &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */
+/* -- Linux 2.2.8 to 2.5.17 end here -- */
+ &P->rtprio, &P->sched /* both added to 2.5.18 */
+ );
+
+ if(!P->nlwp){
+ P->nlwp = 1;
+ }
+
+LEAVE(0x160);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static void statm2proc(const char* s, proc_t *restrict P) {
+ int num;
+ num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
+ &P->size, &P->resident, &P->share,
+ &P->trs, &P->lrs, &P->drs, &P->dt);
+/* fprintf(stderr, "statm2proc converted %d fields.\n",num); */
+}
+
+static int file2str(const char *directory, const char *what, char *ret, int cap) {
+ static char filename[80];
+ int fd, num_read;
+
+ sprintf(filename, "%s/%s", directory, what);
+ fd = open(filename, O_RDONLY, 0);
+ if(unlikely(fd==-1)) return -1;
+ num_read = read(fd, ret, cap - 1);
+ close(fd);
+ if(unlikely(num_read<=0)) return -1;
+ ret[num_read] = '\0';
+ return num_read;
+}
+
+static char** file2strvec(const char* directory, const char* what) {
+ char buf[2048]; /* read buf bytes at a time */
+ char *p, *rbuf = 0, *endbuf, **q, **ret;
+ int fd, tot = 0, n, c, end_of_file = 0;
+ int align;
+
+ sprintf(buf, "%s/%s", directory, what);
+ fd = open(buf, O_RDONLY, 0);
+ if(fd==-1) return NULL;
+
+ /* read whole file into a memory buffer, allocating as we go */
+ while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
+ if (n < (int)(sizeof buf - 1))
+ end_of_file = 1;
+ if (n == 0 && rbuf == 0)
+ return NULL; /* process died between our open and read */
+ if (n < 0) {
+ if (rbuf)
+ free(rbuf);
+ return NULL; /* read error */
+ }
+ if (end_of_file && buf[n-1]) /* last read char not null */
+ buf[n++] = '\0'; /* so append null-terminator */
+ rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */
+ memcpy(rbuf + tot, buf, n); /* copy buffer into it */
+ tot += n; /* increment total byte ctr */
+ if (end_of_file)
+ break;
+ }
+ close(fd);
+ if (n <= 0 && !end_of_file) {
+ if (rbuf) free(rbuf);
+ return NULL; /* read error */
+ }
+ endbuf = rbuf + tot; /* count space for pointers */
+ align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
+ for (c = 0, p = rbuf; p < endbuf; p++)
+ if (!*p)
+ c += sizeof(char*);
+ c += sizeof(char*); /* one extra for NULL term */
+
+ rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */
+ endbuf = rbuf + tot; /* addr just past data buf */
+ q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */
+ *q++ = p = rbuf; /* point ptrs to the strings */
+ endbuf--; /* do not traverse final NUL */
+ while (++p < endbuf)
+ if (!*p) /* NUL char implies that */
+ *q++ = p+1; /* next string -> next char */
+
+ *q = 0; /* null ptr list terminator */
+ return ret;
+}
+
+// warning: interface may change
+int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){
+ char name[32];
+ int fd;
+ unsigned n = 0;
+ dst[0] = '\0';
+ snprintf(name, sizeof name, "/proc/%u/cmdline", pid);
+ fd = open(name, O_RDONLY);
+ if(fd==-1) return 0;
+ for(;;){
+ ssize_t r = read(fd,dst+n,sz-n);
+ if(r==-1){
+ if(errno==EINTR) continue;
+ break;
+ }
+ n += r;
+ if(n==sz) break; // filled the buffer
+ if(r==0) break; // EOF
+ }
+ close(fd);
+ if(n){
+ int i;
+ if(n==sz) n--;
+ dst[n] = '\0';
+ i=n;
+ while(i--){
+ int c = dst[i];
+ if(c<' ' || c>'~') dst[i]=' ';
+ }
+ }
+ return n;
+}
+
+/* These are some nice GNU C expression subscope "inline" functions.
+ * The can be used with arbitrary types and evaluate their arguments
+ * exactly once.
+ */
+
+/* Test if item X of type T is present in the 0 terminated list L */
+# define XinL(T, X, L) ( { \
+ T x = (X), *l = (L); \
+ while (*l && *l != x) l++; \
+ *l == x; \
+ } )
+
+/* Test if item X of type T is present in the list L of length N */
+# define XinLN(T, X, L, N) ( { \
+ T x = (X), *l = (L); \
+ int i = 0, n = (N); \
+ while (i < n && l[i] != x) i++; \
+ i < n && l[i] == x; \
+ } )
+
+//////////////////////////////////////////////////////////////////////////////////
+// This reads process info from /proc in the traditional way, for one process.
+// The pid (tgid? tid?) is already in p, and a path to it in path, with some
+// room to spare.
+static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
+ static struct stat sb; // stat() buffer
+ static char sbuf[1024]; // buffer for stat,statm
+ char *restrict const path = PT->path;
+ unsigned flags = PT->flags;
+
+ if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
+ goto next_proc;
+
+ if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
+ goto next_proc; /* not one of the requested uids */
+
+ p->euid = sb.st_uid; /* need a way to get real uid */
+ p->egid = sb.st_gid; /* need a way to get real gid */
+
+ if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
+ if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
+ goto next_proc; /* error reading /proc/#/stat */
+ stat2proc(sbuf, p); /* parse /proc/#/stat */
+ }
+
+ if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
+ if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
+ statm2proc(sbuf, p); /* ignore statm errors here */
+ } /* statm fields just zero */
+
+ if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
+ if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
+ status2proc(sbuf, p, 1);
+ }
+ }
+
+ // if multithreaded, some values are crap
+ if(p->nlwp > 1){
+ p->wchan = (KLONG)~0ull;
+ }
+
+ /* some number->text resolving which is time consuming and kind of insane */
+ if (flags & PROC_FILLUSR){
+ memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
+ memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
+ memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
+ }
+ }
+
+ /* some number->text resolving which is time consuming and kind of insane */
+ if (flags & PROC_FILLGRP){
+ memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
+ memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
+ memcpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
+ }
+ }
+
+ if (flags & PROC_FILLSUPGRP && p->nsupgid > 0){
+ allocsupgrp(p);
+ int i;
+ for (i=0; i < p->nsupgid; i++)
+ memcpy(p->supgrp[i], group_from_gid(p->supgid[i]), P_G_SZ);
+ }
+
+ if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
+ p->cmdline = file2strvec(path, "cmdline");
+ else
+ p->cmdline = NULL;
+
+ if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
+ p->environ = file2strvec(path, "environ");
+ else
+ p->environ = NULL;
+
+ if(linux_version_code>=LINUX_VERSION(2,6,24) && (flags & PROC_FILLCGROUP)) {
+ p->cgroup = file2strvec(path, "cgroup"); /* read /proc/#/cgroup */
+ if(p->cgroup && *p->cgroup) {
+ int i = strlen(*p->cgroup);
+ if( (*p->cgroup)[i-1]=='\n' )
+ (*p->cgroup)[i-1] = ' '; //little hack to remove trailing \n
+ }
+ }
+ else
+ p->cgroup = NULL;
+
+ return p;
+next_proc:
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This reads /proc/*/task/* data, for one task.
+// p is the POSIX process (task group summary) (not needed by THIS implementation)
+// t is the POSIX thread (task group member, generally not the leader)
+// path is a path to the task, with some room to spare.
+static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
+ static struct stat sb; // stat() buffer
+ static char sbuf[1024]; // buffer for stat,statm
+ unsigned flags = PT->flags;
+
+//printf("hhh\n");
+ if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
+ goto next_task;
+
+// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
+// goto next_task; /* not one of the requested uids */
+
+ t->euid = sb.st_uid; /* need a way to get real uid */
+ t->egid = sb.st_gid; /* need a way to get real gid */
+
+//printf("iii\n");
+ if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
+ if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
+ goto next_task; /* error reading /proc/#/stat */
+ stat2proc(sbuf, t); /* parse /proc/#/stat */
+ }
+
+ if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
+#if 0
+ if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
+ statm2proc(sbuf, t); /* ignore statm errors here */
+#else
+ t->size = p->size;
+ t->resident = p->resident;
+ t->share = p->share;
+ t->trs = p->trs;
+ t->lrs = p->lrs;
+ t->drs = p->drs;
+ t->dt = p->dt;
+#endif
+ } /* statm fields just zero */
+
+ if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
+ if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
+ status2proc(sbuf, t, 0);
+ }
+ }
+
+ /* some number->text resolving which is time consuming */
+ if (flags & PROC_FILLUSR){
+ memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser);
+ memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser);
+ memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser);
+ }
+ }
+
+ /* some number->text resolving which is time consuming */
+ if (flags & PROC_FILLGRP){
+ memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup);
+ memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup);
+ memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup);
+ }
+ }
+
+ if (flags & PROC_FILLSUPGRP && t->nsupgid > 0){
+ allocsupgrp(t);
+ int i;
+ for (i=0; i < t->nsupgid; i++)
+ memcpy(t->supgrp[i], group_from_gid(t->supgid[i]), P_G_SZ);
+ }
+
+#if 0
+ if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
+ t->cmdline = file2strvec(path, "cmdline");
+ else
+ t->cmdline = NULL;
+
+ if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
+ t->environ = file2strvec(path, "environ");
+ else
+ t->environ = NULL;
+#else
+ t->cmdline = p->cmdline; // better not free these until done with all threads!
+ t->environ = p->environ;
+#endif
+ t->cgroup = p->cgroup;
+ t->ppid = p->ppid; // ought to put the per-task ppid somewhere
+
+ return t;
+next_task:
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This finds processes in /proc in the traditional way.
+// Return non-zero on success.
+static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
+ static struct direct *ent; /* dirent handle */
+ char *restrict const path = PT->path;
+ for (;;) {
+ ent = readdir(PT->procfs);
+ if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
+ if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
+ }
+ p->tgid = strtoul(ent->d_name, NULL, 10);
+ p->tid = p->tgid;
+ memcpy(path, "/proc/", 6);
+ strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries
+ return 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This finds tasks in /proc/*/task/ in the traditional way.
+// Return non-zero on success.
+static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
+ static struct direct *ent; /* dirent handle */
+ if(PT->taskdir_user != p->tgid){
+ if(PT->taskdir){
+ closedir(PT->taskdir);
+ }
+ // use "path" as some tmp space
+ snprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid);
+ PT->taskdir = opendir(path);
+ if(!PT->taskdir) return 0;
+ PT->taskdir_user = p->tgid;
+ }
+ for (;;) {
+ ent = readdir(PT->taskdir);
+ if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
+ if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
+ }
+ t->tid = strtoul(ent->d_name, NULL, 10);
+ t->tgid = p->tgid;
+ t->ppid = p->ppid; // cover for kernel behavior? we want both actually...?
+ snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name);
+ return 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This "finds" processes in a list that was given to openproc().
+// Return non-zero on success. (tgid was handy)
+static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
+ char *restrict const path = PT->path;
+ pid_t tgid = *(PT->pids)++;
+ if(likely( tgid )){
+ snprintf(path, PROCPATHLEN, "/proc/%d", tgid);
+ p->tgid = tgid;
+ p->tid = tgid; // they match for leaders
+ }
+ return tgid;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+/* readproc: return a pointer to a proc_t filled with requested info about the
+ * next process available matching the restriction set. If no more such
+ * processes are available, return a null pointer (boolean false). Use the
+ * passed buffer instead of allocating space if it is non-NULL. */
+
+/* This is optimized so that if a PID list is given, only those files are
+ * searched for in /proc. If other lists are given in addition to the PID list,
+ * the same logic can follow through as for the no-PID list case. This is
+ * fairly complex, but it does try to not to do any unnecessary work.
+ */
+proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
+ proc_t *ret;
+ proc_t *saved_p;
+
+ PT->did_fake=0;
+// if (PT->taskdir) {
+// closedir(PT->taskdir);
+// PT->taskdir = NULL;
+// PT->taskdir_user = -1;
+// }
+
+ saved_p = p;
+ if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
+ else memset(p, 0, sizeof *p);
+
+ for(;;){
+ // fills in the path, plus p->tid and p->tgid
+ if (unlikely(! PT->finder(PT,p) )) goto out;
+
+ // go read the process data
+ ret = PT->reader(PT,p);
+ if(ret) return ret;
+ }
+
+out:
+ if(!saved_p) free(p);
+ // FIXME: maybe set tid to -1 here, for "-" in display?
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// readtask: return a pointer to a proc_t filled with requested info about the
+// next task available. If no more such tasks are available, return a null
+// pointer (boolean false). Use the passed buffer instead of allocating
+// space if it is non-NULL.
+proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) {
+ static char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline
+ proc_t *ret;
+ proc_t *saved_t;
+
+ saved_t = t;
+ if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */
+ else memset(t, 0, sizeof *t);
+
+ // 1. got to fake a thread for old kernels
+ // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!)
+ if(task_dir_missing || p->nlwp < 2){
+ if(PT->did_fake) goto out;
+ PT->did_fake=1;
+ memcpy(t,p,sizeof(proc_t));
+ // use the per-task pending, not per-tgid pending
+#ifdef SIGNAL_STRING
+ memcpy(&t->signal, &t->_sigpnd, sizeof t->signal);
+#else
+ t->signal = t->_sigpnd;
+#endif
+ return t;
+ }
+
+ for(;;){
+ // fills in the path, plus t->tid and t->tgid
+ if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out; // simple_nexttid
+
+ // go read the task data
+ ret = PT->taskreader(PT,p,t,path); // simple_readtask
+ if(ret) return ret;
+ }
+
+out:
+ if(!saved_t) free(t);
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// initiate a process table scan
+PROCTAB* openproc(int flags, ...) {
+ va_list ap;
+ struct stat sbuf;
+ static int did_stat;
+ PROCTAB* PT = xmalloc(sizeof(PROCTAB));
+
+ if(!did_stat){
+ task_dir_missing = stat("/proc/self/task", &sbuf);
+ did_stat = 1;
+ }
+ PT->taskdir = NULL;
+ PT->taskdir_user = -1;
+ PT->taskfinder = simple_nexttid;
+ PT->taskreader = simple_readtask;
+
+ PT->reader = simple_readproc;
+ if (flags & PROC_PID){
+ PT->procfs = NULL;
+ PT->finder = listed_nextpid;
+ }else{
+ PT->procfs = opendir("/proc");
+ if(!PT->procfs) return NULL;
+ PT->finder = simple_nextpid;
+ }
+ PT->flags = flags;
+
+ va_start(ap, flags); /* Init args list */
+ if (flags & PROC_PID)
+ PT->pids = va_arg(ap, pid_t*);
+ else if (flags & PROC_UID) {
+ PT->uids = va_arg(ap, uid_t*);
+ PT->nuid = va_arg(ap, int);
+ }
+ va_end(ap); /* Clean up args list */
+
+ return PT;
+}
+
+// terminate a process table scan
+void closeproc(PROCTAB* PT) {
+ if (PT){
+ if (PT->procfs) closedir(PT->procfs);
+ if (PT->taskdir) closedir(PT->taskdir);
+ memset(PT,'#',sizeof(PROCTAB));
+ free(PT);
+ }
+}
+
+// allocate memory for supgrp
+void allocsupgrp(proc_t *p) {
+ if (!p || p->nsupgid == 0) return;
+ p->supgrp = (char**)xmalloc(p->nsupgid * sizeof(char*));
+ int i;
+ for (i=0; i<p->nsupgid; i++)
+ p->supgrp[i] = (char*)xmalloc(P_G_SZ * sizeof(char));
+}
+
+// free memory allocated for supgrp
+void freesupgrp(proc_t *p) {
+ int i;
+ for (i=0; i<p->nsupgid; i++)
+ if (p->supgrp[i]) free(p->supgrp[i]);
+ free(p->supgrp);
+}
+
+// deallocate the space allocated by readproc if the passed rbuf was NULL
+void freeproc(proc_t* p) {
+ if (!p) /* in case p is NULL */
+ return;
+ /* ptrs are after strings to avoid copying memory when building them. */
+ /* so free is called on the address of the address of strvec[0]. */
+ if (p->cmdline)
+ free((void*)*p->cmdline);
+ if (p->environ)
+ free((void*)*p->environ);
+ if (p->cgroup)
+ free((void*)*p->cgroup);
+ free(p);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+void look_up_our_self(proc_t *p) {
+ char sbuf[1024];
+
+ if(file2str("/proc/self", "stat", sbuf, sizeof sbuf) == -1){
+ fprintf(stderr, "Error, do this: mount -t proc none /proc\n");
+ _exit(47);
+ }
+ stat2proc(sbuf, p); // parse /proc/self/stat
+}
+
+HIDDEN_ALIAS(readproc);
+HIDDEN_ALIAS(readtask);
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * table subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with exit(). Access via tab[N]->member. The pointer
+ * list is NULL terminated.
+ */
+proc_t** readproctab(int flags, ...) {
+ PROCTAB* PT = NULL;
+ proc_t** tab = NULL;
+ int n = 0;
+ va_list ap;
+
+ va_start(ap, flags); /* pass through args to openproc */
+ if (flags & PROC_UID) {
+ /* temporary variables to ensure that va_arg() instances
+ * are called in the right order
+ */
+ uid_t* u;
+ int i;
+
+ u = va_arg(ap, uid_t*);
+ i = va_arg(ap, int);
+ PT = openproc(flags, u, i);
+ }
+ else if (flags & PROC_PID)
+ PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+ else
+ PT = openproc(flags);
+ va_end(ap);
+ if (!PT)
+ return 0;
+ do { /* read table: */
+ tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
+ tab[n] = readproc_direct(PT, NULL); /* final null to terminate */
+ } while (tab[n++]); /* stop when NULL reached */
+ closeproc(PT);
+ return tab;
+}
+
+// Try again, this time with threads and selection.
+proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) {
+ proc_t** ptab = NULL;
+ unsigned n_proc_alloc = 0;
+ unsigned n_proc = 0;
+
+ proc_t** ttab = NULL;
+ unsigned n_task_alloc = 0;
+ unsigned n_task = 0;
+
+ proc_t* data = NULL;
+ unsigned n_alloc = 0;
+ unsigned long n_used = 0;
+
+ proc_data_t *pd;
+
+ for(;;){
+ proc_t *tmp;
+ if(n_alloc == n_used){
+ //proc_t *old = data;
+ n_alloc = n_alloc*5/4+30; // grow by over 25%
+ data = realloc(data,sizeof(proc_t)*n_alloc);
+ //if(!data) return NULL;
+ }
+ if(n_proc_alloc == n_proc){
+ //proc_t **old = ptab;
+ n_proc_alloc = n_proc_alloc*5/4+30; // grow by over 25%
+ ptab = realloc(ptab,sizeof(proc_t*)*n_proc_alloc);
+ //if(!ptab) return NULL;
+ }
+ tmp = readproc_direct(PT, data+n_used);
+ if(!tmp) break;
+ if(!want_proc(tmp)) continue;
+ ptab[n_proc++] = (proc_t*)(n_used++);
+ if(!( PT->flags & PROC_LOOSE_TASKS )) continue;
+ for(;;){
+ proc_t *t;
+ if(n_alloc == n_used){
+ proc_t *old = data;
+ n_alloc = n_alloc*5/4+30; // grow by over 25%
+ data = realloc(data,sizeof(proc_t)*n_alloc);
+ // have to move tmp too
+ tmp = data+(tmp-old);
+ //if(!data) return NULL;
+ }
+ if(n_task_alloc == n_task){
+ //proc_t **old = ttab;
+ n_task_alloc = n_task_alloc*5/4+1; // grow by over 25%
+ ttab = realloc(ttab,sizeof(proc_t*)*n_task_alloc);
+ //if(!ttab) return NULL;
+ }
+ t = readtask_direct(PT, tmp, data+n_used);
+ if(!t) break;
+ if(!want_task(t)) continue;
+ ttab[n_task++] = (proc_t*)(n_used++);
+ }
+ }
+
+ pd = malloc(sizeof(proc_data_t));
+ pd->proc = ptab;
+ pd->task = ttab;
+ pd->nproc = n_proc;
+ pd->ntask = n_task;
+ if(PT->flags & PROC_LOOSE_TASKS){
+ pd->tab = ttab;
+ pd->n = n_task;
+ }else{
+ pd->tab = ptab;
+ pd->n = n_proc;
+ }
+ // change array indexes to pointers
+ while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]);
+ while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]);
+
+ return pd;
+}
+
+/*
+ * get_proc_stats - lookup a single tasks information and fill out a proc_t
+ *
+ * On failure, returns NULL. On success, returns 'p' and 'p' is a valid
+ * and filled out proc_t structure.
+ */
+proc_t * get_proc_stats(pid_t pid, proc_t *p) {
+ static char path[32], sbuf[1024];
+ struct stat statbuf;
+
+ sprintf(path, "/proc/%d", pid);
+ if (stat(path, &statbuf)) {
+ perror("stat");
+ return NULL;
+ }
+
+ if (file2str(path, "stat", sbuf, sizeof sbuf) >= 0)
+ stat2proc(sbuf, p); /* parse /proc/#/stat */
+ if (file2str(path, "statm", sbuf, sizeof sbuf) >= 0)
+ statm2proc(sbuf, p); /* ignore statm errors here */
+ if (file2str(path, "status", sbuf, sizeof sbuf) >= 0)
+ status2proc(sbuf, p, 0 /*FIXME*/);
+
+ return p;
+}
diff --git a/smartt-top/proc/readproc.h b/smartt-top/proc/readproc.h
new file mode 100644
index 0000000..c242eed
--- /dev/null
+++ b/smartt-top/proc/readproc.h
@@ -0,0 +1,265 @@
+#ifndef PROCPS_PROC_READPROC_H
+#define PROCPS_PROC_READPROC_H
+
+// New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+// Copyright 1996 Charles L. Blake.
+// Copyright 1998 Michael K. Johnson
+// Copyright 1998-2003 Albert Cahalan
+// May be distributed under the terms of the
+// GNU Library General Public License, a copy of which is provided
+// in the file COPYING
+
+
+#include "procps.h"
+#include "pwcache.h"
+
+#define SIGNAL_STRING
+
+EXTERN_C_BEGIN
+
+// ld cutime, cstime, priority, nice, timeout, alarm, rss,
+// c state,
+// d ppid, pgrp, session, tty, tpgid,
+// s signal, blocked, sigignore, sigcatch,
+// lu flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime,
+// lu rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip,
+// lu start_time, vsize, wchan,
+
+// This is to help document a transition from pid to tgid/tid caused
+// by the introduction of thread support. It is used in cases where
+// neither tgid nor tid seemed correct. (in other words, FIXME)
+#define XXXID tid
+
+// Basic data structure which holds all information we can get about a process.
+// (unless otherwise specified, fields are read from /proc/#/stat)
+//
+// Most of it comes from task_struct in linux/sched.h
+//
+typedef struct proc_t {
+// 1st 16 bytes
+ int
+ tid, // (special) task id, the POSIX thread ID (see also: tgid)
+ ppid; // stat,status pid of parent process
+ unsigned
+ pcpu; // stat (special) %CPU usage (is not filled in by readproc!!!)
+ char
+ state, // stat,status single-char code for process state (S=sleeping)
+ pad_1, // n/a padding
+ pad_2, // n/a padding
+ pad_3; // n/a padding
+// 2nd 16 bytes
+ unsigned long long
+ utime, // stat user-mode CPU time accumulated by process
+ stime, // stat kernel-mode CPU time accumulated by process
+// and so on...
+ cutime, // stat cumulative utime of process and reaped children
+ cstime, // stat cumulative stime of process and reaped children
+ start_time; // stat start time of process -- seconds since 1-1-70
+#ifdef SIGNAL_STRING
+ char
+ // Linux 2.1.7x and up have 64 signals. Allow 64, plus '\0' and padding.
+ signal[18], // status mask of pending signals, per-task for readtask() but per-proc for readproc()
+ blocked[18], // status mask of blocked signals
+ sigignore[18], // status mask of ignored signals
+ sigcatch[18], // status mask of caught signals
+ _sigpnd[18]; // status mask of PER TASK pending signals
+#else
+ long long
+ // Linux 2.1.7x and up have 64 signals.
+ signal, // status mask of pending signals, per-task for readtask() but per-proc for readproc()
+ blocked, // status mask of blocked signals
+ sigignore, // status mask of ignored signals
+ sigcatch, // status mask of caught signals
+ _sigpnd; // status mask of PER TASK pending signals
+#endif
+ unsigned KLONG
+ start_code, // stat address of beginning of code segment
+ end_code, // stat address of end of code segment
+ start_stack, // stat address of the bottom of stack for the process
+ kstk_esp, // stat kernel stack pointer
+ kstk_eip, // stat kernel instruction pointer
+ wchan; // stat (special) address of kernel wait channel proc is sleeping in
+ long
+ priority, // stat kernel scheduling priority
+ nice, // stat standard unix nice level of process
+ rss, // stat resident set size from /proc/#/stat (pages)
+ alarm, // stat ?
+ // the next 7 members come from /proc/#/statm
+ size, // statm total # of pages of memory
+ resident, // statm number of resident set (non-swapped) pages (4k)
+ share, // statm number of pages of shared (mmap'd) memory
+ trs, // statm text resident set size
+ lrs, // statm shared-lib resident set size
+ drs, // statm data resident set size
+ dt; // statm dirty pages
+ unsigned long
+ vm_size, // status same as vsize in kb
+ vm_lock, // status locked pages in kb
+ vm_rss, // status same as rss in kb
+ vm_data, // status data size
+ vm_stack, // status stack size
+ vm_exe, // status executable size
+ vm_lib, // status library size (all pages, not just used ones)
+ rtprio, // stat real-time priority
+ sched, // stat scheduling class
+ vsize, // stat number of pages of virtual memory ...
+ rss_rlim, // stat resident set size limit?
+ flags, // stat kernel flags for the process
+ min_flt, // stat number of minor page faults since process start
+ maj_flt, // stat number of major page faults since process start
+ cmin_flt, // stat cumulative min_flt of process and child processes
+ cmaj_flt; // stat cumulative maj_flt of process and child processes
+ char
+ **environ, // (special) environment string vector (/proc/#/environ)
+ **cmdline; // (special) command line string vector (/proc/#/cmdline)
+ char
+ // Be compatible: Digital allows 16 and NT allows 14 ???
+ euser[P_G_SZ], // stat(),status effective user name
+ ruser[P_G_SZ], // status real user name
+ suser[P_G_SZ], // status saved user name
+ fuser[P_G_SZ], // status filesystem user name
+ rgroup[P_G_SZ], // status real group name
+ egroup[P_G_SZ], // status effective group name
+ sgroup[P_G_SZ], // status saved group name
+ fgroup[P_G_SZ], // status filesystem group name
+ **supgrp, // status supplementary groups
+ cmd[16]; // stat,status basename of executable file in call to exec(2)
+ struct proc_t
+ *ring, // n/a thread group ring
+ *next; // n/a various library uses
+ int
+ pgrp, // stat process group id
+ session, // stat session id
+ nlwp, // stat,status number of threads, or 0 if no clue
+ tgid, // (special) task group ID, the POSIX PID (see also: tid)
+ tty, // stat full device number of controlling terminal
+ euid, egid, // stat(),status effective
+ ruid, rgid, // status real
+ suid, sgid, // status saved
+ fuid, fgid, // status fs (used for file access only)
+ tpgid, // stat terminal process group id
+ nsupgid, // status number of supplementary groups
+ *supgid, // status supplementary gid's
+ exit_signal, // stat might not be SIGCHLD
+ processor; // stat current (or most recent?) CPU
+ char **cgroup; // cgroup current cgroup, looks like a classic filepath
+} proc_t;
+
+// PROCTAB: data structure holding the persistent information readproc needs
+// from openproc(). The setup is intentionally similar to the dirent interface
+// and other system table interfaces (utmp+wtmp come to mind).
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#define PROCPATHLEN 64 // must hold /proc/2000222000/task/2000222000/cmdline
+
+typedef struct PROCTAB {
+ DIR* procfs;
+// char deBug0[64];
+ DIR* taskdir; // for threads
+// char deBug1[64];
+ pid_t taskdir_user; // for threads
+ int did_fake; // used when taskdir is missing
+ int(*finder)(struct PROCTAB *restrict const, proc_t *restrict const);
+ proc_t*(*reader)(struct PROCTAB *restrict const, proc_t *restrict const);
+ int(*taskfinder)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const);
+ proc_t*(*taskreader)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const);
+ pid_t* pids; // pids of the procs
+ uid_t* uids; // uids of procs
+ int nuid; // cannot really sentinel-terminate unsigned short[]
+ int i; // generic
+ unsigned flags;
+ unsigned u; // generic
+ void * vp; // generic
+ char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline
+ unsigned pathlen; // length of string in the above (w/o '\0')
+} PROCTAB;
+
+// initialize a PROCTAB structure holding needed call-to-call persistent data
+extern PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ );
+
+typedef struct proc_data_t {
+ proc_t **tab;
+ proc_t **proc;
+ proc_t **task;
+ int n;
+ int nproc;
+ int ntask;
+} proc_data_t;
+
+extern proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT);
+
+// Convenient wrapper around openproc and readproc to slurp in the whole process
+// table subset satisfying the constraints of flags and the optional PID list.
+// Free allocated memory with exit(). Access via tab[N]->member. The pointer
+// list is NULL terminated.
+
+extern proc_t** readproctab(int flags, ... /* same as openproc */ );
+
+// clean-up open files, etc from the openproc()
+extern void closeproc(PROCTAB* PT);
+
+// allocate memory for supgrp
+extern void allocsupgrp(proc_t *p);
+
+// free memory allocated for supgrp
+extern void freesupgrp(proc_t *p);
+
+// retrieve the next process matching the criteria set by the openproc()
+extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p);
+extern proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t);
+
+// warning: interface may change
+extern int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid);
+
+extern void look_up_our_self(proc_t *p);
+
+// deallocate space allocated by readproc
+
+extern void freeproc(proc_t* p);
+
+//fill out a proc_t for a single task
+extern proc_t * get_proc_stats(pid_t pid, proc_t *p);
+
+// openproc/readproctab:
+//
+// Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be
+// opened.) By default readproc will consider all processes as valid to parse
+// and return, but not actually fill in the cmdline, environ, and /proc/#/statm
+// derived memory fields.
+//
+// `flags' (a bitwise-or of PROC_* below) modifies the default behavior. The
+// "fill" options will cause more of the proc_t to be filled in. The "filter"
+// options all use the second argument as the pointer to a list of objects:
+// process status', process id's, user id's. The third
+// argument is the length of the list (currently only used for lists of user
+// id's since uid_t supports no convenient termination sentinel.)
+
+#define PROC_FILLMEM 0x0001 // read statm
+#define PROC_FILLCOM 0x0002 // alloc and fill in `cmdline'
+#define PROC_FILLENV 0x0004 // alloc and fill in `environ'
+#define PROC_FILLUSR 0x0008 // resolve user id number -> user name
+#define PROC_FILLGRP 0x0010 // resolve group id number -> group name
+#define PROC_FILLSTATUS 0x0020 // read status -- currently unconditional
+#define PROC_FILLSTAT 0x0040 // read stat -- currently unconditional
+#define PROC_FILLWCHAN 0x0080 // look up WCHAN name
+#define PROC_FILLARG 0x0100 // alloc and fill in `cmdline'
+#define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup`
+#define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id number -> group name
+
+#define PROC_LOOSE_TASKS 0x2000 // threat threads as if they were processes
+
+// Obsolete, consider only processes with one of the passed:
+#define PROC_PID 0x1000 // process id numbers ( 0 terminated)
+#define PROC_UID 0x4000 // user id numbers ( length needed )
+
+// it helps to give app code a few spare bits
+#define PROC_SPARE_1 0x01000000
+#define PROC_SPARE_2 0x02000000
+#define PROC_SPARE_3 0x04000000
+#define PROC_SPARE_4 0x08000000
+
+EXTERN_C_END
+#endif
diff --git a/smartt-top/proc/sig.c b/smartt-top/proc/sig.c
new file mode 100644
index 0000000..ea63397
--- /dev/null
+++ b/smartt-top/proc/sig.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sig.h"
+
+/* Linux signals:
+ *
+ * SIGSYS is required by Unix98.
+ * SIGEMT is part of SysV, BSD, and ancient UNIX tradition.
+ *
+ * They are provided by these Linux ports: alpha, mips, sparc, and sparc64.
+ * You get SIGSTKFLT and SIGUNUSED instead on i386, m68k, ppc, and arm.
+ * (this is a Linux & libc bug -- both must be fixed)
+ *
+ * Total garbage: SIGIO SIGINFO SIGIOT SIGLOST SIGCLD
+ * (popular ones are handled as aliases)
+ * Nearly garbage: SIGSTKFLT SIGUNUSED (nothing else to fill slots)
+ */
+
+/* Linux 2.3.29 replaces SIGUNUSED with the standard SIGSYS signal */
+#ifndef SIGSYS
+# warning Standards require that <signal.h> define SIGSYS
+# define SIGSYS SIGUNUSED
+#endif
+
+/* If we see both, it is likely SIGSTKFLT (junk) was replaced. */
+#ifdef SIGEMT
+# undef SIGSTKFLT
+#endif
+
+#ifndef SIGRTMIN
+# warning Standards require that <signal.h> define SIGRTMIN; assuming 32
+# define SIGRTMIN 32
+#endif
+
+/* It seems the SPARC libc does not know the kernel supports SIGPWR. */
+#ifndef SIGPWR
+# warning Your header files lack SIGPWR. (assuming it is number 29)
+# define SIGPWR 29
+#endif
+
+typedef struct mapstruct {
+ const char *name;
+ int num;
+} mapstruct;
+
+
+static const mapstruct sigtable[] = {
+ {"ABRT", SIGABRT}, /* IOT */
+ {"ALRM", SIGALRM},
+ {"BUS", SIGBUS},
+ {"CHLD", SIGCHLD}, /* CLD */
+ {"CONT", SIGCONT},
+#ifdef SIGEMT
+ {"EMT", SIGEMT},
+#endif
+ {"FPE", SIGFPE},
+ {"HUP", SIGHUP},
+ {"ILL", SIGILL},
+ {"INT", SIGINT},
+ {"KILL", SIGKILL},
+ {"PIPE", SIGPIPE},
+ {"POLL", SIGPOLL}, /* IO */
+ {"PROF", SIGPROF},
+ {"PWR", SIGPWR},
+ {"QUIT", SIGQUIT},
+ {"SEGV", SIGSEGV},
+#ifdef SIGSTKFLT
+ {"STKFLT", SIGSTKFLT},
+#endif
+ {"STOP", SIGSTOP},
+ {"SYS", SIGSYS}, /* UNUSED */
+ {"TERM", SIGTERM},
+ {"TRAP", SIGTRAP},
+ {"TSTP", SIGTSTP},
+ {"TTIN", SIGTTIN},
+ {"TTOU", SIGTTOU},
+ {"URG", SIGURG},
+ {"USR1", SIGUSR1},
+ {"USR2", SIGUSR2},
+ {"VTALRM", SIGVTALRM},
+ {"WINCH", SIGWINCH},
+ {"XCPU", SIGXCPU},
+ {"XFSZ", SIGXFSZ}
+};
+
+static const int number_of_signals = sizeof(sigtable)/sizeof(mapstruct);
+
+static int compare_signal_names(const void *a, const void *b){
+ return strcasecmp( ((const mapstruct*)a)->name, ((const mapstruct*)b)->name );
+}
+
+/* return -1 on failure */
+int signal_name_to_number(const char *restrict name){
+ long val;
+ int offset;
+
+ /* clean up name */
+ if(!strncasecmp(name,"SIG",3)) name += 3;
+
+ if(!strcasecmp(name,"CLD")) return SIGCHLD;
+ if(!strcasecmp(name,"IO")) return SIGPOLL;
+ if(!strcasecmp(name,"IOT")) return SIGABRT;
+
+ /* search the table */
+ {
+ const mapstruct ms = {name,0};
+ const mapstruct *restrict const ptr = bsearch(
+ &ms,
+ sigtable,
+ number_of_signals,
+ sizeof(mapstruct),
+ compare_signal_names
+ );
+ if(ptr) return ptr->num;
+ }
+
+ if(!strcasecmp(name,"RTMIN")) return SIGRTMIN;
+ if(!strcasecmp(name,"EXIT")) return 0;
+ if(!strcasecmp(name,"NULL")) return 0;
+
+ offset = 0;
+ if(!strncasecmp(name,"RTMIN+",6)){
+ name += 6;
+ offset = SIGRTMIN;
+ }
+
+ /* not found, so try as a number */
+ {
+ char *endp;
+ val = strtol(name,&endp,10);
+ if(*endp || endp==name) return -1; /* not valid */
+ }
+ if(val+SIGRTMIN>127) return -1; /* not valid */
+ return val+offset;
+}
+
+const char *signal_number_to_name(int signo){
+ static char buf[32];
+ int n = number_of_signals;
+ signo &= 0x7f; /* need to process exit values too */
+ while(n--){
+ if(sigtable[n].num==signo) return sigtable[n].name;
+ }
+ if(signo == SIGRTMIN) return "RTMIN";
+ if(signo) sprintf(buf, "RTMIN+%d", signo-SIGRTMIN);
+ else strcpy(buf,"0"); /* AIX has NULL; Solaris has EXIT */
+ return buf;
+}
+
+int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line){
+ char buf[1280]; /* 128 signals, "RTMIN+xx" is largest */
+ int ret = 0; /* to be used as exit code by caller */
+ int place = 0; /* position on this line */
+ int amt;
+ if(argc > 128) return 1;
+ while(argc--){
+ char tmpbuf[16];
+ const char *restrict const txt = *argv;
+ if(*txt >= '0' && *txt <= '9'){
+ long val;
+ char *endp;
+ val = strtol(txt,&endp,10);
+ if(*endp){
+ fprintf(stderr, "Signal \"%s\" not known.\n", txt);
+ ret = 1;
+ goto end;
+ }
+ amt = sprintf(tmpbuf, "%s", signal_number_to_name(val));
+ }else{
+ int sno;
+ sno = signal_name_to_number(txt);
+ if(sno == -1){
+ fprintf(stderr, "Signal \"%s\" not known.\n", txt);
+ ret = 1;
+ goto end;
+ }
+ amt = sprintf(tmpbuf, "%d", sno);
+ }
+
+ if(!place){
+ strcpy(buf,tmpbuf);
+ place = amt;
+ goto end;
+ }
+ if(amt+place+1 > max_line){
+ printf("%s\n", buf);
+ strcpy(buf,tmpbuf);
+ place = amt;
+ goto end;
+ }
+ sprintf(buf+place, " %s", tmpbuf);
+ place += amt+1;
+end:
+ argv++;
+ }
+ if(place) printf("%s\n", buf);
+ return ret;
+}
+
+void pretty_print_signals(void){
+ int i = 0;
+ while(++i <= number_of_signals){
+ int n;
+ n = printf("%2d %s", i, signal_number_to_name(i));
+ if(i%7) printf(" \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + n);
+ else printf("\n");
+ }
+ if((i-1)%7) printf("\n");
+}
+
+void unix_print_signals(void){
+ int pos = 0;
+ int i = 0;
+ while(++i <= number_of_signals){
+ if(i-1) printf("%c", (pos>73)?(pos=0,'\n'):(pos++,' ') );
+ pos += printf("%s", signal_number_to_name(i));
+ }
+ printf("\n");
+}
+
+/* sanity check */
+static int init_signal_list(void) __attribute__((constructor));
+static int init_signal_list(void){
+ if(number_of_signals != 31){
+ fprintf(stderr, "WARNING: %d signals -- adjust and recompile.\n", number_of_signals);
+ }
+ return 0;
+}
diff --git a/smartt-top/proc/sig.h b/smartt-top/proc/sig.h
new file mode 100644
index 0000000..ee850a2
--- /dev/null
+++ b/smartt-top/proc/sig.h
@@ -0,0 +1,30 @@
+#ifndef PROC_SIG_H
+#define PROC_SIG_H
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+/* return -1 on failure */
+extern int signal_name_to_number(const char *restrict name);
+
+extern const char *signal_number_to_name(int signo);
+
+extern int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line);
+
+extern void pretty_print_signals(void);
+
+extern void unix_print_signals(void);
+
+EXTERN_C_END
+#endif
diff --git a/smartt-top/proc/slab.c b/smartt-top/proc/slab.c
new file mode 100644
index 0000000..60c5696
--- /dev/null
+++ b/smartt-top/proc/slab.c
@@ -0,0 +1,337 @@
+/*
+ * slab.c - slab related functions for libproc
+ *
+ * Chris Rivera <cmrivera@ufl.edu>
+ * Robert Love <rml@tech9.net>
+ *
+ * This program is licensed under the GNU Library General Public License, v2
+ *
+ * Copyright (C) 2003 Chris Rivera
+ * Copyright 2004, Albert Cahalan
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "slab.h"
+#include "procps.h"
+
+#define SLABINFO_LINE_LEN 2048
+#define SLABINFO_VER_LEN 100
+#define SLABINFO_FILE "/proc/slabinfo"
+
+static struct slab_info *free_index;
+
+/*
+ * get_slabnode - allocate slab_info structures using a free list
+ *
+ * In the fast path, we simply return a node off the free list. In the slow
+ * list, we malloc() a new node. The free list is never automatically reaped,
+ * both for simplicity and because the number of slab caches is fairly
+ * constant.
+ */
+static struct slab_info *get_slabnode(void)
+{
+ struct slab_info *node;
+
+ if (free_index) {
+ node = free_index;
+ free_index = free_index->next;
+ } else {
+ node = malloc(sizeof(struct slab_info));
+ if (!node)
+ perror("malloc");
+ }
+
+ return node;
+}
+
+/*
+ * slab_badname_detect - return true if current slab was declared with
+ * whitespaces for instance
+ * FIXME :Other cases ?
+ */
+
+static int slab_badname_detect(const char *restrict buffer)
+{
+ int numberarea=0;
+ while (*buffer){
+ if((*buffer)==' ')
+ numberarea=1;
+ if(isalpha(*buffer)&&numberarea)
+ return 1;
+ buffer++;
+ }
+ return 0;
+}
+
+/*
+ * put_slabinfo - return all allocated nodes to the free list
+ */
+void put_slabinfo(struct slab_info *head)
+{
+ free_index = head;
+}
+
+/*
+ * free_slabinfo - deallocate the memory associated with each node in the
+ * slab_info linked list
+ */
+void free_slabinfo(struct slab_info *list)
+{
+ while (list) {
+ struct slab_info *temp = list->next;
+ free(list);
+ list = temp;
+ }
+}
+
+// parse_slabinfo20 - actual parse routine for slabinfo 2.x (2.6 kernels)
+// Note: difference between 2.0 and 2.1 is in the ": globalstat" part where version 2.1
+// has extra column <nodeallocs>. We don't use ": globalstat" part in both versions.
+//
+// Formats (we don't use "statistics" extensions)
+//
+// slabinfo - version: 2.1
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail>
+//
+// slabinfo - version: 2.1 (statistics)
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail> \
+// : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> <nodeallocs> \
+// : cpustat <allochit> <allocmiss> <freehit> <freemiss>
+//
+// slabinfo - version: 2.0
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail>
+//
+// slabinfo - version: 2.0 (statistics)
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail> \
+// : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> \
+// : cpustat <allochit> <allocmiss> <freehit> <freemiss>
+static int parse_slabinfo20(struct slab_info **list, struct slab_stat *stats,
+ FILE *f)
+{
+ struct slab_info *curr = NULL, *prev = NULL;
+ char buffer[SLABINFO_LINE_LEN];
+ int entries = 0;
+ int page_size = getpagesize();
+
+ stats->min_obj_size = INT_MAX;
+ stats->max_obj_size = 0;
+
+ while (fgets(buffer, SLABINFO_LINE_LEN, f)) {
+ int assigned;
+
+ if (buffer[0] == '#')
+ continue;
+
+ curr = get_slabnode();
+ if (!curr)
+ break;
+
+ if (entries++ == 0)
+ *list = curr;
+ else
+ prev->next = curr;
+
+ assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN)
+ "s %d %d %d %d %d : tunables %*d %*d %*d : \
+ slabdata %d %d %*d", curr->name,
+ &curr->nr_active_objs, &curr->nr_objs,
+ &curr->obj_size, &curr->objs_per_slab,
+ &curr->pages_per_slab, &curr->nr_active_slabs,
+ &curr->nr_slabs);
+
+ if (assigned < 8) {
+ fprintf(stderr, "unrecognizable data in slabinfo!\n");
+ curr = NULL;
+ break;
+ }
+
+ if (curr->obj_size < stats->min_obj_size)
+ stats->min_obj_size = curr->obj_size;
+ if (curr->obj_size > stats->max_obj_size)
+ stats->max_obj_size = curr->obj_size;
+
+ curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size;
+
+ if (curr->nr_objs) {
+ curr->use = 100 * curr->nr_active_objs / curr->nr_objs;
+ stats->nr_active_caches++;
+ } else
+ curr->use = 0;
+
+ stats->nr_objs += curr->nr_objs;
+ stats->nr_active_objs += curr->nr_active_objs;
+ stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size;
+ stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size;
+ stats->nr_pages += curr->nr_slabs * curr->pages_per_slab;
+ stats->nr_slabs += curr->nr_slabs;
+ stats->nr_active_slabs += curr->nr_active_slabs;
+
+ prev = curr;
+ }
+
+ if (!curr) {
+ fprintf(stderr, "\rerror reading slabinfo!\n");
+ return 1;
+ }
+
+ curr->next = NULL;
+ stats->nr_caches = entries;
+ if (stats->nr_objs)
+ stats->avg_obj_size = stats->total_size / stats->nr_objs;
+
+ return 0;
+}
+
+/*
+ * parse_slabinfo11 - actual parsing routine for slabinfo 1.1 (2.4 kernels)
+ */
+static int parse_slabinfo11(struct slab_info **list, struct slab_stat *stats,
+ FILE *f)
+{
+ struct slab_info *curr = NULL, *prev = NULL;
+ char buffer[SLABINFO_LINE_LEN];
+ int entries = 0;
+ int page_size = getpagesize();
+
+ stats->min_obj_size = INT_MAX;
+ stats->max_obj_size = 0;
+
+ while (fgets(buffer, SLABINFO_LINE_LEN, f)) {
+ int assigned;
+
+ curr = get_slabnode();
+ if (!curr)
+ break;
+
+ if (entries++ == 0)
+ *list = curr;
+ else
+ prev->next = curr;
+
+ assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN)
+ "s %d %d %d %d %d %d",
+ curr->name, &curr->nr_active_objs,
+ &curr->nr_objs, &curr->obj_size,
+ &curr->nr_active_slabs, &curr->nr_slabs,
+ &curr->pages_per_slab);
+
+ if (assigned < 6) {
+ fprintf(stderr, "unrecognizable data in your slabinfo version 1.1\n\r");
+ if(slab_badname_detect(buffer))
+ fprintf(stderr, "Found an error in cache name at line %s\n", buffer);
+ curr = NULL;
+ break;
+ }
+
+ if (curr->obj_size < stats->min_obj_size)
+ stats->min_obj_size = curr->obj_size;
+ if (curr->obj_size > stats->max_obj_size)
+ stats->max_obj_size = curr->obj_size;
+
+ curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size;
+
+ if (curr->nr_objs) {
+ curr->use = 100 * curr->nr_active_objs / curr->nr_objs;
+ stats->nr_active_caches++;
+ } else
+ curr->use = 0;
+
+ if (curr->obj_size)
+ curr->objs_per_slab = curr->pages_per_slab *
+ page_size / curr->obj_size;
+
+ stats->nr_objs += curr->nr_objs;
+ stats->nr_active_objs += curr->nr_active_objs;
+ stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size;
+ stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size;
+ stats->nr_pages += curr->nr_slabs * curr->pages_per_slab;
+ stats->nr_slabs += curr->nr_slabs;
+ stats->nr_active_slabs += curr->nr_active_slabs;
+
+ prev = curr;
+ }
+
+ if (!curr) {
+ fprintf(stderr, "\rerror reading slabinfo!\n");
+ return 1;
+ }
+
+ curr->next = NULL;
+ stats->nr_caches = entries;
+ if (stats->nr_objs)
+ stats->avg_obj_size = stats->total_size / stats->nr_objs;
+
+ return 0;
+}
+
+/*
+ * parse_slabinfo10 - actual parsing routine for slabinfo 1.0 (2.2 kernels)
+ *
+ * Not yet implemented. Please feel free.
+ */
+static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats,
+ FILE *f)
+{
+ (void) list, (void) stats, (void) f;
+ fprintf(stderr, "slabinfo version 1.0 not yet supported\n");
+ return 1;
+}
+
+/*
+ * slabinfo - parse the system's slabinfo and fill out both a linked list of
+ * slab_info structures and the slab_stat structure
+ *
+ * The function returns zero on success, in which case 'list' and 'stats' are
+ * valid. Nonzero is returned on failure and the state of 'list' and 'stats'
+ * are undefined.
+ */
+int get_slabinfo(struct slab_info **list, struct slab_stat *stats)
+{
+ FILE *slabfile;
+ char buffer[SLABINFO_VER_LEN];
+ int major, minor, ret = 0;
+
+ slabfile = fopen(SLABINFO_FILE, "r");
+ if (!slabfile) {
+ perror("fopen " SLABINFO_FILE);
+ return 1;
+ }
+
+ if (!fgets(buffer, SLABINFO_VER_LEN, slabfile)) {
+ fprintf(stderr, "cannot read from slabinfo\n");
+ return 1;
+ }
+
+ if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) {
+ fprintf(stderr, "not the good old slabinfo we know\n");
+ return 1;
+ }
+
+ if (major == 2)
+ ret = parse_slabinfo20(list, stats, slabfile);
+ else if (major == 1 && minor == 1)
+ ret = parse_slabinfo11(list, stats, slabfile);
+ else if (major == 1 && minor == 0)
+ ret = parse_slabinfo10(list, stats, slabfile);
+ else {
+ fprintf(stderr, "unrecognizable slabinfo version\n");
+ return 1;
+ }
+
+ fclose(slabfile);
+
+ return ret;
+}
diff --git a/smartt-top/proc/slab.h b/smartt-top/proc/slab.h
new file mode 100644
index 0000000..ea17ddc
--- /dev/null
+++ b/smartt-top/proc/slab.h
@@ -0,0 +1,39 @@
+#ifndef _PROC_SLAB_H
+#define _PROC_SLAB_H
+
+#define SLAB_INFO_NAME_LEN 64
+
+struct slab_info {
+ char name[SLAB_INFO_NAME_LEN]; /* name of this cache */
+ struct slab_info *next;
+ unsigned long cache_size; /* size of entire cache */
+ unsigned nr_objs; /* number of objects in this cache */
+ unsigned nr_active_objs; /* number of active objects */
+ unsigned obj_size; /* size of each object */
+ unsigned objs_per_slab; /* number of objects per slab */
+ unsigned pages_per_slab; /* number of pages per slab */
+ unsigned nr_slabs; /* number of slabs in this cache */
+ unsigned nr_active_slabs; /* number of active slabs */
+ unsigned use; /* percent full: total / active */
+};
+
+struct slab_stat {
+ unsigned long total_size; /* size of all objects */
+ unsigned long active_size; /* size of all active objects */
+ unsigned nr_objs; /* number of objects, among all caches */
+ unsigned nr_active_objs; /* number of active objects, among all caches */
+ unsigned nr_pages; /* number of pages consumed by all objects */
+ unsigned nr_slabs; /* number of slabs, among all caches */
+ unsigned nr_active_slabs; /* number of active slabs, among all caches */
+ unsigned nr_caches; /* number of caches */
+ unsigned nr_active_caches; /* number of active caches */
+ unsigned avg_obj_size; /* average object size */
+ unsigned min_obj_size; /* size of smallest object */
+ unsigned max_obj_size; /* size of largest object */
+};
+
+extern void put_slabinfo(struct slab_info *);
+extern void free_slabinfo(struct slab_info *);
+extern int get_slabinfo(struct slab_info **, struct slab_stat *);
+
+#endif /* _PROC_SLAB_H */
diff --git a/smartt-top/proc/smaps.c b/smartt-top/proc/smaps.c
new file mode 100644
index 0000000..17789a1
--- /dev/null
+++ b/smartt-top/proc/smaps.c
@@ -0,0 +1,171 @@
+#if 0
+#include "procps.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "procps.h"
+
+struct smap_entry {
+ unsigned KLONG start;
+ unsigned KLONG beyond;
+ long long offset;
+ char flags[8];
+ unsigned dev_major;
+ unsigned dev_minor;
+ unsigned long long inode;
+
+ unsigned long rss;
+ unsigned long pss;
+ unsigned long sclean;
+ unsigned long sdirty;
+ unsigned long pclean;
+ unsigned long pdirty;
+ unsigned long ref;
+ unsigned long swap;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This code will surely make normal programmers cry. I need speed though,
+// and /proc/*/smaps should make anybody cry. (WTF kind of brain damage...?)
+
+struct smap_summary {
+ unsigned long size;
+ unsigned long rss;
+ unsigned long pss;
+ unsigned long sclean;
+ unsigned long sdirty;
+ unsigned long pclean;
+ unsigned long pdirty;
+ unsigned long ref;
+ unsigned long swap;
+};
+
+struct ssjt {
+ char str[16];
+ int len;
+ int offset;
+};
+
+#define JTE(o,x) {#x,sizeof(#x)-1,o}
+
+void get_smap_sums(struct smap_summary *restrict ss, const char *restrict const filename){
+ static struct ssjt table[] = {
+ JTE(-1,default),
+ JTE( 1,Rss),
+ JTE(-1,default),
+ JTE( 2,Pss),
+ JTE( 8,Swap),
+ JTE( 5,Private_Clean),
+ JTE( 6,Private_Dirty),
+ JTE(-1,default),
+ JTE( 7,Referenced),
+ JTE(-1,default),
+ JTE( 0,Size),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default), // KernelPageSize would go here
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE( 4,Shared_Dirty),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE( 3,Shared_Clean),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ };
+ char buf[20480];
+ int p1 = 0;
+ int p2 = 0;
+ memset(ss,0,sizeof *ss);
+ int fd = open(filename,O_RDONLY);
+ if(fd==-1)
+ return;
+ for(;;){
+ char *nlp = memchr(buf+p1,'\n',p2-p1);
+ if(!nlp){
+ if(p1){
+ // the memmove should never do anything, because the
+ // kernel seems to give us the greatest number of
+ // complete lines of text that fit in a single page
+ // (and thus p2-p1 is zero)
+ memmove(buf,buf+p1,p2-p1);
+ p2 -= p1;
+ p1 = 0;
+ }
+ ssize_t rb = read(fd,buf+p1,sizeof buf - p1);
+ if(rb < 1)
+ break;
+ p2 += rb;
+ nlp = memchr(buf+p1,'\n',p2-p1);
+ if(!nlp)
+ break;
+ }
+ char *s = buf+p1;
+ int len = nlp-s;
+ p1 += len+1;
+ if(len<27)
+ continue;
+//printf("j <%13.13s>\n",s);
+ if(s[0]<'A' || s[0]>'Z')
+ continue;
+ unsigned hash = ( (s[8]&15) + (s[1]&15) ) ^ (s[0]&3);
+ hash &= 31;
+//printf("x %2d <%13.13s>\n",hash,s);
+ if(s[table[hash].len] != ':')
+ continue;
+//printf("y %2d <%13.13s>\n",hash,s);
+ if(memcmp(table[hash].str,s,table[hash].len))
+ continue;
+//printf("z %2d <%13.13s>\n",hash,s);
+ s += table[hash].len;
+ while(*++s==' ')
+ ;
+ unsigned long ul = 0;
+ for(;;){
+ char c = *s++;
+ if(c != ' '){
+ ul *= 10;
+ ul += c-'0';
+ continue;
+ }
+ break;
+ }
+// if(table[hash].offset == 2)
+// printf("Pss:%20lu kB\n",ul);
+ unsigned long *ulp = &ss->size + table[hash].offset;
+ *ulp += ul;
+// memcpy(ss+table[hash].offset*sizeof(unsigned long), &ul, sizeof(unsigned long));
+ }
+ close(fd);
+}
+
+int main(int argc, char *argv[]){
+ struct smap_summary ss;
+ get_smap_sums(&ss, argv[1]);
+ printf("%9lu\n",ss.size);
+ printf("%9lu\n",ss.rss);
+ printf("%9lu\n",ss.pss);
+ printf("%9lu\n",ss.sclean);
+ printf("%9lu\n",ss.sdirty);
+ printf("%9lu\n",ss.pclean);
+ printf("%9lu\n",ss.pdirty);
+ printf("%9lu\n",ss.ref);
+ printf("%9lu\n",ss.swap);
+ return 0;
+}
+#endif
diff --git a/smartt-top/proc/sysinfo.c b/smartt-top/proc/sysinfo.c
new file mode 100644
index 0000000..d144361
--- /dev/null
+++ b/smartt-top/proc/sysinfo.c
@@ -0,0 +1,942 @@
+// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com
+// Copyright 1998-2003 Albert Cahalan
+//
+// This file is placed under the conditions of the GNU Library
+// General Public License, version 2, or any later version.
+// See file COPYING for information on distribution conditions.
+//
+// File for parsing top-level /proc entities. */
+//
+// June 2003, Fabian Frederick, disk and slab info
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include "version.h"
+#include "sysinfo.h" /* include self to verify prototypes */
+
+#ifndef HZ
+#include <netinet/in.h> /* htons */
+#endif
+
+long smp_num_cpus; /* number of CPUs */
+
+#define BAD_OPEN_MESSAGE \
+"Error: /proc must be mounted\n" \
+" To mount /proc at boot you need an /etc/fstab line like:\n" \
+" /proc /proc proc defaults\n" \
+" In the meantime, run \"mount /proc /proc -t proc\"\n"
+
+#define STAT_FILE "/proc/stat"
+static int stat_fd = -1;
+#define UPTIME_FILE "/proc/uptime"
+static int uptime_fd = -1;
+#define LOADAVG_FILE "/proc/loadavg"
+static int loadavg_fd = -1;
+#define MEMINFO_FILE "/proc/meminfo"
+static int meminfo_fd = -1;
+#define VMINFO_FILE "/proc/vmstat"
+static int vminfo_fd = -1;
+
+// As of 2.6.24 /proc/meminfo seems to need 888 on 64-bit,
+// and would need 1258 if the obsolete fields were there.
+static char buf[2048];
+
+/* This macro opens filename only if necessary and seeks to 0 so
+ * that successive calls to the functions are more efficient.
+ * It also reads the current contents of the file into the global buf.
+ */
+#define FILE_TO_BUF(filename, fd) do{ \
+ static int local_n; \
+ if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
+ fputs(BAD_OPEN_MESSAGE, stderr); \
+ fflush(NULL); \
+ _exit(102); \
+ } \
+ lseek(fd, 0L, SEEK_SET); \
+ if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
+ perror(filename); \
+ fflush(NULL); \
+ _exit(103); \
+ } \
+ buf[local_n] = '\0'; \
+}while(0)
+
+/* evals 'x' twice */
+#define SET_IF_DESIRED(x,y) do{ if(x) *(x) = (y); }while(0)
+
+
+/***********************************************************************/
+int uptime(double *restrict uptime_secs, double *restrict idle_secs) {
+ double up=0, idle=0;
+ char *restrict savelocale;
+
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd);
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC,"C");
+ if (sscanf(buf, "%lf %lf", &up, &idle) < 2) {
+ setlocale(LC_NUMERIC,savelocale);
+ fputs("bad data in " UPTIME_FILE "\n", stderr);
+ return 0;
+ }
+ setlocale(LC_NUMERIC,savelocale);
+ SET_IF_DESIRED(uptime_secs, up);
+ SET_IF_DESIRED(idle_secs, idle);
+ return up; /* assume never be zero seconds in practice */
+}
+
+/***********************************************************************
+ * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
+ * is the kernel clock tick rate. One of these units is called a jiffy.
+ * The HZ value used in the kernel may vary according to hacker desire.
+ * According to Linus Torvalds, this is not true. He considers the values
+ * in /proc as being in architecture-dependant units that have no relation
+ * to the kernel clock tick rate. Examination of the kernel source code
+ * reveals that opinion as wishful thinking.
+ *
+ * In any case, we need the HZ constant as used in /proc. (the real HZ value
+ * may differ, but we don't care) There are several ways we could get HZ:
+ *
+ * 1. Include the kernel header file. If it changes, recompile this library.
+ * 2. Use the sysconf() function. When HZ changes, recompile the C library!
+ * 3. Ask the kernel. This is obviously correct...
+ *
+ * Linus Torvalds won't let us ask the kernel, because he thinks we should
+ * not know the HZ value. Oh well, we don't have to listen to him.
+ * Someone smuggled out the HZ value. :-)
+ *
+ * This code should work fine, even if Linus fixes the kernel to match his
+ * stated behavior. The code only fails in case of a partial conversion.
+ *
+ * Recent update: on some architectures, the 2.4 kernel provides an
+ * ELF note to indicate HZ. This may be for ARM or user-mode Linux
+ * support. This ought to be investigated. Note that sysconf() is still
+ * unreliable, because it doesn't return an error code when it is
+ * used with a kernel that doesn't support the ELF note. On some other
+ * architectures there may be a system call or sysctl() that will work.
+ */
+
+unsigned long long Hertz;
+
+static void old_Hertz_hack(void){
+ unsigned long long user_j, nice_j, sys_j, other_j, wait_j, hirq_j, sirq_j, stol_j; /* jiffies (clock ticks) */
+ double up_1, up_2, seconds;
+ unsigned long long jiffies;
+ unsigned h;
+ char *restrict savelocale;
+
+ wait_j = hirq_j = sirq_j = stol_j = 0;
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ do{
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_1);
+ /* uptime(&up_1, NULL); */
+ FILE_TO_BUF(STAT_FILE,stat_fd);
+ sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &user_j, &nice_j, &sys_j, &other_j, &wait_j, &hirq_j, &sirq_j, &stol_j);
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_2);
+ /* uptime(&up_2, NULL); */
+ } while((long long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
+ setlocale(LC_NUMERIC, savelocale);
+ jiffies = user_j + nice_j + sys_j + other_j + wait_j + hirq_j + sirq_j + stol_j ;
+ seconds = (up_1 + up_2) / 2;
+ h = (unsigned)( (double)jiffies/seconds/smp_num_cpus );
+ /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
+ switch(h){
+ case 9 ... 11 : Hertz = 10; break; /* S/390 (sometimes) */
+ case 18 ... 22 : Hertz = 20; break; /* user-mode Linux */
+ case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */
+ case 48 ... 52 : Hertz = 50; break;
+ case 58 ... 61 : Hertz = 60; break;
+ case 62 ... 65 : Hertz = 64; break; /* StrongARM /Shark */
+ case 95 ... 105 : Hertz = 100; break; /* normal Linux */
+ case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */
+ case 195 ... 204 : Hertz = 200; break; /* normal << 1 */
+ case 247 ... 252 : Hertz = 250; break;
+ case 253 ... 260 : Hertz = 256; break;
+ case 393 ... 408 : Hertz = 400; break; /* normal << 2 */
+ case 790 ... 808 : Hertz = 800; break; /* normal << 3 */
+ case 990 ... 1010 : Hertz = 1000; break; /* ARM */
+ case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */
+ case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */
+ default:
+#ifdef HZ
+ Hertz = (unsigned long long)HZ; /* <asm/param.h> */
+#else
+ /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
+ Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
+#endif
+ fprintf(stderr, "Unknown HZ value! (%d) Assume %Ld.\n", h, Hertz);
+ }
+}
+
+// same as: euid != uid || egid != gid
+#ifndef AT_SECURE
+#define AT_SECURE 23 // secure mode boolean (true if setuid, etc.)
+#endif
+
+#ifndef AT_CLKTCK
+#define AT_CLKTCK 17 // frequency of times()
+#endif
+
+#define NOTE_NOT_FOUND 42
+
+//extern char** environ;
+
+/* for ELF executables, notes are pushed before environment and args */
+static unsigned long find_elf_note(unsigned long findme){
+ unsigned long *ep = (unsigned long *)environ;
+ while(*ep++);
+ while(*ep){
+ if(ep[0]==findme) return ep[1];
+ ep+=2;
+ }
+ return NOTE_NOT_FOUND;
+}
+
+int have_privs;
+
+static int check_for_privs(void){
+ unsigned long rc = find_elf_note(AT_SECURE);
+ if(rc==NOTE_NOT_FOUND){
+ // not valid to run this code after UID or GID change!
+ // (if needed, may use AT_UID and friends instead)
+ rc = geteuid() != getuid() || getegid() != getgid();
+ }
+ return !!rc;
+}
+
+static void init_libproc(void) __attribute__((constructor));
+static void init_libproc(void){
+ have_privs = check_for_privs();
+ init_Linux_version(); /* Must be called before we check code */
+ // ought to count CPUs in /proc/stat instead of relying
+ // on glibc, which foolishly tries to parse /proc/cpuinfo
+ //
+ // SourceForge has an old Alpha running Linux 2.2.20 that
+ // appears to have a non-SMP kernel on a 2-way SMP box.
+ // _SC_NPROCESSORS_CONF returns 2, resulting in HZ=512
+ // _SC_NPROCESSORS_ONLN returns 1, which should work OK
+ smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if(smp_num_cpus<1) smp_num_cpus=1; /* SPARC glibc is buggy */
+#ifdef __linux__
+ if(linux_version_code > LINUX_VERSION(2, 4, 0)){
+ Hertz = find_elf_note(AT_CLKTCK);
+ if(Hertz!=NOTE_NOT_FOUND) return;
+ fputs("2.4+ kernel w/o ELF notes? -- report this\n", stderr);
+ }
+#endif
+#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+ /* On FreeBSD the Hertz hack is unrelaible, there is no ELF note and
+ * Hertz isn't defined in asm/params.h
+ * See Debian Bug #460331
+ */
+ Hertz = 100;
+ return;
+#endif
+ old_Hertz_hack();
+}
+
+#if 0
+/***********************************************************************
+ * The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we
+ * recover jiffies by adding up the 4 or 5 numbers we are given. SMP kernels
+ * (as of pre-2.4 era) can report idle time going backwards, perhaps due
+ * to non-atomic reads and updates. There is no locking for these values.
+ */
+#ifndef NAN
+#define NAN (-0.0)
+#endif
+#define JT unsigned long long
+void eight_cpu_numbers(double *restrict uret, double *restrict nret, double *restrict sret, double *restrict iret, double *restrict wret, double *restrict xret, double *restrict yret, double *restrict zret){
+ double tmp_u, tmp_n, tmp_s, tmp_i, tmp_w, tmp_x, tmp_y, tmp_z;
+ double scale; /* scale values to % */
+ static JT old_u, old_n, old_s, old_i, old_w, old_x, old_y, old_z;
+ JT new_u, new_n, new_s, new_i, new_w, new_x, new_y, new_z;
+ JT ticks_past; /* avoid div-by-0 by not calling too often :-( */
+
+ tmp_w = 0.0;
+ new_w = 0;
+ tmp_x = 0.0;
+ new_x = 0;
+ tmp_y = 0.0;
+ new_y = 0;
+ tmp_z = 0.0;
+ new_z = 0;
+
+ FILE_TO_BUF(STAT_FILE,stat_fd);
+ sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &new_u, &new_n, &new_s, &new_i, &new_w, &new_x, &new_y, &new_z);
+ ticks_past = (new_u+new_n+new_s+new_i+new_w+new_x+new_y+new_z)-(old_u+old_n+old_s+old_i+old_w+old_x+old_y+old_z);
+ if(ticks_past){
+ scale = 100.0 / (double)ticks_past;
+ tmp_u = ( (double)new_u - (double)old_u ) * scale;
+ tmp_n = ( (double)new_n - (double)old_n ) * scale;
+ tmp_s = ( (double)new_s - (double)old_s ) * scale;
+ tmp_i = ( (double)new_i - (double)old_i ) * scale;
+ tmp_w = ( (double)new_w - (double)old_w ) * scale;
+ tmp_x = ( (double)new_x - (double)old_x ) * scale;
+ tmp_y = ( (double)new_y - (double)old_y ) * scale;
+ tmp_z = ( (double)new_z - (double)old_z ) * scale;
+ }else{
+ tmp_u = NAN;
+ tmp_n = NAN;
+ tmp_s = NAN;
+ tmp_i = NAN;
+ tmp_w = NAN;
+ tmp_x = NAN;
+ tmp_y = NAN;
+ tmp_z = NAN;
+ }
+ SET_IF_DESIRED(uret, tmp_u);
+ SET_IF_DESIRED(nret, tmp_n);
+ SET_IF_DESIRED(sret, tmp_s);
+ SET_IF_DESIRED(iret, tmp_i);
+ SET_IF_DESIRED(wret, tmp_w);
+ SET_IF_DESIRED(xret, tmp_x);
+ SET_IF_DESIRED(yret, tmp_y);
+ SET_IF_DESIRED(zret, tmp_z);
+ old_u=new_u;
+ old_n=new_n;
+ old_s=new_s;
+ old_i=new_i;
+ old_w=new_w;
+ old_x=new_x;
+ old_y=new_y;
+ old_z=new_z;
+}
+#undef JT
+#endif
+
+/***********************************************************************/
+void loadavg(double *restrict av1, double *restrict av5, double *restrict av15) {
+ double avg_1=0, avg_5=0, avg_15=0;
+ char *restrict savelocale;
+
+ FILE_TO_BUF(LOADAVG_FILE,loadavg_fd);
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) {
+ fputs("bad data in " LOADAVG_FILE "\n", stderr);
+ exit(1);
+ }
+ setlocale(LC_NUMERIC, savelocale);
+ SET_IF_DESIRED(av1, avg_1);
+ SET_IF_DESIRED(av5, avg_5);
+ SET_IF_DESIRED(av15, avg_15);
+}
+
+ static char buff[BUFFSIZE]; /* used in the procedures */
+/***********************************************************************/
+
+static void crash(const char *filename) {
+ perror(filename);
+ exit(EXIT_FAILURE);
+}
+
+/***********************************************************************/
+
+static void getrunners(unsigned int *restrict running, unsigned int *restrict blocked) {
+ struct direct *ent;
+ DIR *proc;
+
+ *running=0;
+ *blocked=0;
+
+ if((proc=opendir("/proc"))==NULL) crash("/proc");
+
+ while(( ent=readdir(proc) )) {
+ char tbuf[32];
+ char *cp;
+ int fd;
+ char c;
+
+ if (!isdigit(ent->d_name[0])) continue;
+ sprintf(tbuf, "/proc/%s/stat", ent->d_name);
+
+ fd = open(tbuf, O_RDONLY, 0);
+ if (fd == -1) continue;
+ memset(tbuf, '\0', sizeof tbuf); // didn't feel like checking read()
+ read(fd, tbuf, sizeof tbuf - 1); // need 32 byte buffer at most
+ close(fd);
+
+ cp = strrchr(tbuf, ')');
+ if(!cp) continue;
+ c = cp[2];
+
+ if (c=='R') {
+ (*running)++;
+ continue;
+ }
+ if (c=='D') {
+ (*blocked)++;
+ continue;
+ }
+ }
+ closedir(proc);
+}
+
+/***********************************************************************/
+
+void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz,
+ unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout,
+ unsigned *restrict intr, unsigned *restrict ctxt,
+ unsigned int *restrict running, unsigned int *restrict blocked,
+ unsigned int *restrict btime, unsigned int *restrict processes) {
+ static int fd;
+ unsigned long long llbuf = 0;
+ int need_vmstat_file = 0;
+ int need_proc_scan = 0;
+ const char* b;
+ buff[BUFFSIZE-1] = 0; /* ensure null termination in buffer */
+
+ if(fd){
+ lseek(fd, 0L, SEEK_SET);
+ }else{
+ fd = open("/proc/stat", O_RDONLY, 0);
+ if(fd == -1) crash("/proc/stat");
+ }
+ read(fd,buff,BUFFSIZE-1);
+ *intr = 0;
+ *ciow = 0; /* not separated out until the 2.5.41 kernel */
+ *cxxx = 0; /* not separated out until the 2.6.0-test4 kernel */
+ *cyyy = 0; /* not separated out until the 2.6.0-test4 kernel */
+ *czzz = 0; /* not separated out until the 2.6.11 kernel */
+
+ b = strstr(buff, "cpu ");
+ if(b) sscanf(b, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", cuse, cice, csys, cide, ciow, cxxx, cyyy, czzz);
+
+ b = strstr(buff, "page ");
+ if(b) sscanf(b, "page %lu %lu", pin, pout);
+ else need_vmstat_file = 1;
+
+ b = strstr(buff, "swap ");
+ if(b) sscanf(b, "swap %lu %lu", s_in, sout);
+ else need_vmstat_file = 1;
+
+ b = strstr(buff, "intr ");
+ if(b) sscanf(b, "intr %Lu", &llbuf);
+ *intr = llbuf;
+
+ b = strstr(buff, "ctxt ");
+ if(b) sscanf(b, "ctxt %Lu", &llbuf);
+ *ctxt = llbuf;
+
+ b = strstr(buff, "btime ");
+ if(b) sscanf(b, "btime %u", btime);
+
+ b = strstr(buff, "processes ");
+ if(b) sscanf(b, "processes %u", processes);
+
+ b = strstr(buff, "procs_running ");
+ if(b) sscanf(b, "procs_running %u", running);
+ else need_proc_scan = 1;
+
+ b = strstr(buff, "procs_blocked ");
+ if(b) sscanf(b, "procs_blocked %u", blocked);
+ else need_proc_scan = 1;
+
+ if(need_proc_scan){ /* Linux 2.5.46 (approximately) and below */
+ getrunners(running, blocked);
+ }
+
+ (*running)--; // exclude vmstat itself
+
+ if(need_vmstat_file){ /* Linux 2.5.40-bk4 and above */
+ vminfo();
+ *pin = vm_pgpgin;
+ *pout = vm_pgpgout;
+ *s_in = vm_pswpin;
+ *sout = vm_pswpout;
+ }
+}
+
+/***********************************************************************/
+/*
+ * Copyright 1999 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+typedef struct mem_table_struct {
+ const char *name; /* memory type name */
+ unsigned long *slot; /* slot in return struct */
+} mem_table_struct;
+
+static int compare_mem_table_structs(const void *a, const void *b){
+ return strcmp(((const mem_table_struct*)a)->name,((const mem_table_struct*)b)->name);
+}
+
+/* example data, following junk, with comments added:
+ *
+ * MemTotal: 61768 kB old
+ * MemFree: 1436 kB old
+ * MemShared: 0 kB old (now always zero; not calculated)
+ * Buffers: 1312 kB old
+ * Cached: 20932 kB old
+ * Active: 12464 kB new
+ * Inact_dirty: 7772 kB new
+ * Inact_clean: 2008 kB new
+ * Inact_target: 0 kB new
+ * Inact_laundry: 0 kB new, and might be missing too
+ * HighTotal: 0 kB
+ * HighFree: 0 kB
+ * LowTotal: 61768 kB
+ * LowFree: 1436 kB
+ * SwapTotal: 122580 kB old
+ * SwapFree: 60352 kB old
+ * Inactive: 20420 kB 2.5.41+
+ * Dirty: 0 kB 2.5.41+
+ * Writeback: 0 kB 2.5.41+
+ * Mapped: 9792 kB 2.5.41+
+ * Slab: 4564 kB 2.5.41+
+ * Committed_AS: 8440 kB 2.5.41+
+ * PageTables: 304 kB 2.5.41+
+ * ReverseMaps: 5738 2.5.41+
+ * SwapCached: 0 kB 2.5.??+
+ * HugePages_Total: 220 2.5.??+
+ * HugePages_Free: 138 2.5.??+
+ * Hugepagesize: 4096 kB 2.5.??+
+ */
+
+/* obsolete */
+unsigned long kb_main_shared;
+/* old but still kicking -- the important stuff */
+unsigned long kb_main_buffers;
+unsigned long kb_main_cached;
+unsigned long kb_main_free;
+unsigned long kb_main_total;
+unsigned long kb_swap_free;
+unsigned long kb_swap_total;
+/* recently introduced */
+unsigned long kb_high_free;
+unsigned long kb_high_total;
+unsigned long kb_low_free;
+unsigned long kb_low_total;
+/* 2.4.xx era */
+unsigned long kb_active;
+unsigned long kb_inact_laundry;
+unsigned long kb_inact_dirty;
+unsigned long kb_inact_clean;
+unsigned long kb_inact_target;
+unsigned long kb_swap_cached; /* late 2.4 and 2.6+ only */
+/* derived values */
+unsigned long kb_swap_used;
+unsigned long kb_main_used;
+/* 2.5.41+ */
+unsigned long kb_writeback;
+unsigned long kb_slab;
+unsigned long nr_reversemaps;
+unsigned long kb_committed_as;
+unsigned long kb_dirty;
+unsigned long kb_inactive;
+unsigned long kb_mapped;
+unsigned long kb_pagetables;
+// seen on a 2.6.x kernel:
+static unsigned long kb_vmalloc_chunk;
+static unsigned long kb_vmalloc_total;
+static unsigned long kb_vmalloc_used;
+// seen on 2.6.24-rc6-git12
+static unsigned long kb_anon_pages;
+static unsigned long kb_bounce;
+static unsigned long kb_commit_limit;
+static unsigned long kb_nfs_unstable;
+static unsigned long kb_swap_reclaimable;
+static unsigned long kb_swap_unreclaimable;
+
+void meminfo(void){
+ char namebuf[16]; /* big enough to hold any row name */
+ mem_table_struct findme = { namebuf, NULL};
+ mem_table_struct *found;
+ char *head;
+ char *tail;
+ static const mem_table_struct mem_table[] = {
+ {"Active", &kb_active}, // important
+ {"AnonPages", &kb_anon_pages},
+ {"Bounce", &kb_bounce},
+ {"Buffers", &kb_main_buffers}, // important
+ {"Cached", &kb_main_cached}, // important
+ {"CommitLimit", &kb_commit_limit},
+ {"Committed_AS", &kb_committed_as},
+ {"Dirty", &kb_dirty}, // kB version of vmstat nr_dirty
+ {"HighFree", &kb_high_free},
+ {"HighTotal", &kb_high_total},
+ {"Inact_clean", &kb_inact_clean},
+ {"Inact_dirty", &kb_inact_dirty},
+ {"Inact_laundry",&kb_inact_laundry},
+ {"Inact_target", &kb_inact_target},
+ {"Inactive", &kb_inactive}, // important
+ {"LowFree", &kb_low_free},
+ {"LowTotal", &kb_low_total},
+ {"Mapped", &kb_mapped}, // kB version of vmstat nr_mapped
+ {"MemFree", &kb_main_free}, // important
+ {"MemShared", &kb_main_shared}, // important, but now gone!
+ {"MemTotal", &kb_main_total}, // important
+ {"NFS_Unstable", &kb_nfs_unstable},
+ {"PageTables", &kb_pagetables}, // kB version of vmstat nr_page_table_pages
+ {"ReverseMaps", &nr_reversemaps}, // same as vmstat nr_page_table_pages
+ {"SReclaimable", &kb_swap_reclaimable}, // "swap reclaimable" (dentry and inode structures)
+ {"SUnreclaim", &kb_swap_unreclaimable},
+ {"Slab", &kb_slab}, // kB version of vmstat nr_slab
+ {"SwapCached", &kb_swap_cached},
+ {"SwapFree", &kb_swap_free}, // important
+ {"SwapTotal", &kb_swap_total}, // important
+ {"VmallocChunk", &kb_vmalloc_chunk},
+ {"VmallocTotal", &kb_vmalloc_total},
+ {"VmallocUsed", &kb_vmalloc_used},
+ {"Writeback", &kb_writeback}, // kB version of vmstat nr_writeback
+ };
+ const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct);
+
+ fprintf(stderr, "Sudip -> In meminfo");
+ sleep(5);
+
+ FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);
+
+ kb_inactive = ~0UL;
+
+ head = buf;
+ for(;;){
+ tail = strchr(head, ':');
+ if(!tail) break;
+ *tail = '\0';
+ if(strlen(head) >= sizeof(namebuf)){
+ head = tail+1;
+ goto nextline;
+ }
+ strcpy(namebuf,head);
+ found = bsearch(&findme, mem_table, mem_table_count,
+ sizeof(mem_table_struct), compare_mem_table_structs
+ );
+ head = tail+1;
+ if(!found) goto nextline;
+ *(found->slot) = (unsigned long)strtoull(head,&tail,10);
+nextline:
+ tail = strchr(head, '\n');
+ if(!tail) break;
+ head = tail+1;
+ }
+ if(!kb_low_total){ /* low==main except with large-memory support */
+ kb_low_total = kb_main_total;
+ kb_low_free = kb_main_free;
+ }
+ if(kb_inactive==~0UL){
+ kb_inactive = kb_inact_dirty + kb_inact_clean + kb_inact_laundry;
+ }
+ kb_swap_used = kb_swap_total - kb_swap_free;
+ kb_main_used = kb_main_total - kb_main_free;
+}
+
+/*****************************************************************/
+
+/* read /proc/vminfo only for 2.5.41 and above */
+
+typedef struct vm_table_struct {
+ const char *name; /* VM statistic name */
+ unsigned long *slot; /* slot in return struct */
+} vm_table_struct;
+
+static int compare_vm_table_structs(const void *a, const void *b){
+ return strcmp(((const vm_table_struct*)a)->name,((const vm_table_struct*)b)->name);
+}
+
+// see include/linux/page-flags.h and mm/page_alloc.c
+unsigned long vm_nr_dirty; // dirty writable pages
+unsigned long vm_nr_writeback; // pages under writeback
+unsigned long vm_nr_pagecache; // pages in pagecache -- gone in 2.5.66+ kernels
+unsigned long vm_nr_page_table_pages;// pages used for pagetables
+unsigned long vm_nr_reverse_maps; // includes PageDirect
+unsigned long vm_nr_mapped; // mapped into pagetables
+unsigned long vm_nr_slab; // in slab
+unsigned long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line)
+unsigned long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line)
+unsigned long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line)
+unsigned long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line)
+unsigned long vm_pgalloc; // page allocations
+unsigned long vm_pgfree; // page freeings
+unsigned long vm_pgactivate; // pages moved inactive -> active
+unsigned long vm_pgdeactivate; // pages moved active -> inactive
+unsigned long vm_pgfault; // total faults (major+minor)
+unsigned long vm_pgmajfault; // major faults
+unsigned long vm_pgscan; // pages scanned by page reclaim
+unsigned long vm_pgrefill; // inspected by refill_inactive_zone
+unsigned long vm_pgsteal; // total pages reclaimed
+unsigned long vm_kswapd_steal; // pages reclaimed by kswapd
+// next 3 as defined by the 2.5.52 kernel
+unsigned long vm_pageoutrun; // times kswapd ran page reclaim
+unsigned long vm_allocstall; // times a page allocator ran direct reclaim
+unsigned long vm_pgrotated; // pages rotated to the tail of the LRU for immediate reclaim
+// seen on a 2.6.8-rc1 kernel, apparently replacing old fields
+static unsigned long vm_pgalloc_dma; //
+static unsigned long vm_pgalloc_high; //
+static unsigned long vm_pgalloc_normal; //
+static unsigned long vm_pgrefill_dma; //
+static unsigned long vm_pgrefill_high; //
+static unsigned long vm_pgrefill_normal; //
+static unsigned long vm_pgscan_direct_dma; //
+static unsigned long vm_pgscan_direct_high; //
+static unsigned long vm_pgscan_direct_normal; //
+static unsigned long vm_pgscan_kswapd_dma; //
+static unsigned long vm_pgscan_kswapd_high; //
+static unsigned long vm_pgscan_kswapd_normal; //
+static unsigned long vm_pgsteal_dma; //
+static unsigned long vm_pgsteal_high; //
+static unsigned long vm_pgsteal_normal; //
+// seen on a 2.6.8-rc1 kernel
+static unsigned long vm_kswapd_inodesteal; //
+static unsigned long vm_nr_unstable; //
+static unsigned long vm_pginodesteal; //
+static unsigned long vm_slabs_scanned; //
+
+void vminfo(void){
+ char namebuf[16]; /* big enough to hold any row name */
+ vm_table_struct findme = { namebuf, NULL};
+ vm_table_struct *found;
+ char *head;
+ char *tail;
+ static const vm_table_struct vm_table[] = {
+ {"allocstall", &vm_allocstall},
+ {"kswapd_inodesteal", &vm_kswapd_inodesteal},
+ {"kswapd_steal", &vm_kswapd_steal},
+ {"nr_dirty", &vm_nr_dirty}, // page version of meminfo Dirty
+ {"nr_mapped", &vm_nr_mapped}, // page version of meminfo Mapped
+ {"nr_page_table_pages", &vm_nr_page_table_pages},// same as meminfo PageTables
+ {"nr_pagecache", &vm_nr_pagecache}, // gone in 2.5.66+ kernels
+ {"nr_reverse_maps", &vm_nr_reverse_maps}, // page version of meminfo ReverseMaps GONE
+ {"nr_slab", &vm_nr_slab}, // page version of meminfo Slab
+ {"nr_unstable", &vm_nr_unstable},
+ {"nr_writeback", &vm_nr_writeback}, // page version of meminfo Writeback
+ {"pageoutrun", &vm_pageoutrun},
+ {"pgactivate", &vm_pgactivate},
+ {"pgalloc", &vm_pgalloc}, // GONE (now separate dma,high,normal)
+ {"pgalloc_dma", &vm_pgalloc_dma},
+ {"pgalloc_high", &vm_pgalloc_high},
+ {"pgalloc_normal", &vm_pgalloc_normal},
+ {"pgdeactivate", &vm_pgdeactivate},
+ {"pgfault", &vm_pgfault},
+ {"pgfree", &vm_pgfree},
+ {"pginodesteal", &vm_pginodesteal},
+ {"pgmajfault", &vm_pgmajfault},
+ {"pgpgin", &vm_pgpgin}, // important
+ {"pgpgout", &vm_pgpgout}, // important
+ {"pgrefill", &vm_pgrefill}, // GONE (now separate dma,high,normal)
+ {"pgrefill_dma", &vm_pgrefill_dma},
+ {"pgrefill_high", &vm_pgrefill_high},
+ {"pgrefill_normal", &vm_pgrefill_normal},
+ {"pgrotated", &vm_pgrotated},
+ {"pgscan", &vm_pgscan}, // GONE (now separate direct,kswapd and dma,high,normal)
+ {"pgscan_direct_dma", &vm_pgscan_direct_dma},
+ {"pgscan_direct_high", &vm_pgscan_direct_high},
+ {"pgscan_direct_normal",&vm_pgscan_direct_normal},
+ {"pgscan_kswapd_dma", &vm_pgscan_kswapd_dma},
+ {"pgscan_kswapd_high", &vm_pgscan_kswapd_high},
+ {"pgscan_kswapd_normal",&vm_pgscan_kswapd_normal},
+ {"pgsteal", &vm_pgsteal}, // GONE (now separate dma,high,normal)
+ {"pgsteal_dma", &vm_pgsteal_dma},
+ {"pgsteal_high", &vm_pgsteal_high},
+ {"pgsteal_normal", &vm_pgsteal_normal},
+ {"pswpin", &vm_pswpin}, // important
+ {"pswpout", &vm_pswpout}, // important
+ {"slabs_scanned", &vm_slabs_scanned},
+ };
+ const int vm_table_count = sizeof(vm_table)/sizeof(vm_table_struct);
+
+ vm_pgalloc = 0;
+ vm_pgrefill = 0;
+ vm_pgscan = 0;
+ vm_pgsteal = 0;
+
+ FILE_TO_BUF(VMINFO_FILE,vminfo_fd);
+
+ head = buf;
+ for(;;){
+ tail = strchr(head, ' ');
+ if(!tail) break;
+ *tail = '\0';
+ if(strlen(head) >= sizeof(namebuf)){
+ head = tail+1;
+ goto nextline;
+ }
+ strcpy(namebuf,head);
+ found = bsearch(&findme, vm_table, vm_table_count,
+ sizeof(vm_table_struct), compare_vm_table_structs
+ );
+ head = tail+1;
+ if(!found) goto nextline;
+ *(found->slot) = strtoul(head,&tail,10);
+nextline:
+
+//if(found) fprintf(stderr,"%s=%d\n",found->name,*(found->slot));
+//else fprintf(stderr,"%s not found\n",findme.name);
+
+ tail = strchr(head, '\n');
+ if(!tail) break;
+ head = tail+1;
+ }
+ if(!vm_pgalloc)
+ vm_pgalloc = vm_pgalloc_dma + vm_pgalloc_high + vm_pgalloc_normal;
+ if(!vm_pgrefill)
+ vm_pgrefill = vm_pgrefill_dma + vm_pgrefill_high + vm_pgrefill_normal;
+ if(!vm_pgscan)
+ vm_pgscan = vm_pgscan_direct_dma + vm_pgscan_direct_high + vm_pgscan_direct_normal
+ + vm_pgscan_kswapd_dma + vm_pgscan_kswapd_high + vm_pgscan_kswapd_normal;
+ if(!vm_pgsteal)
+ vm_pgsteal = vm_pgsteal_dma + vm_pgsteal_high + vm_pgsteal_normal;
+}
+
+///////////////////////////////////////////////////////////////////////
+// based on Fabian Frederick's /proc/diskstats parser
+
+
+unsigned int getpartitions_num(struct disk_stat *disks, int ndisks){
+ int i=0;
+ int partitions=0;
+
+ for (i=0;i<ndisks;i++){
+ partitions+=disks[i].partitions;
+ }
+ return partitions;
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+static int is_disk(char *dev)
+{
+ char syspath[PATH_MAX];
+ char *slash;
+
+ while ((slash = strchr(dev, '/')))
+ *slash = '!';
+ snprintf(syspath, sizeof(syspath), "/sys/block/%s", dev);
+ return !(access(syspath, F_OK));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+unsigned int getdiskstat(struct disk_stat **disks, struct partition_stat **partitions){
+ FILE* fd;
+ int cDisk = 0;
+ int cPartition = 0;
+ int fields;
+ unsigned dummy;
+ char devname[PATH_MAX];
+
+ *disks = NULL;
+ *partitions = NULL;
+ buff[BUFFSIZE-1] = 0;
+ fd = fopen("/proc/diskstats", "rb");
+ if(!fd) crash("/proc/diskstats");
+
+ for (;;) {
+ if (!fgets(buff,BUFFSIZE-1,fd)){
+ fclose(fd);
+ break;
+ }
+ fields = sscanf(buff, " %*d %*d %15s %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u",
+ &devname, &dummy);
+ if (fields == 2 && is_disk(devname)){
+ (*disks) = realloc(*disks, (cDisk+1)*sizeof(struct disk_stat));
+ sscanf(buff, " %*d %*d %15s %u %u %llu %u %u %u %llu %u %u %u %u",
+ //&disk_major,
+ //&disk_minor,
+ (*disks)[cDisk].disk_name,
+ &(*disks)[cDisk].reads,
+ &(*disks)[cDisk].merged_reads,
+ &(*disks)[cDisk].reads_sectors,
+ &(*disks)[cDisk].milli_reading,
+ &(*disks)[cDisk].writes,
+ &(*disks)[cDisk].merged_writes,
+ &(*disks)[cDisk].written_sectors,
+ &(*disks)[cDisk].milli_writing,
+ &(*disks)[cDisk].inprogress_IO,
+ &(*disks)[cDisk].milli_spent_IO,
+ &(*disks)[cDisk].weighted_milli_spent_IO
+ );
+ (*disks)[cDisk].partitions=0;
+ cDisk++;
+ }else{
+ (*partitions) = realloc(*partitions, (cPartition+1)*sizeof(struct partition_stat));
+ fflush(stdout);
+ sscanf(buff, (fields == 2)
+ ? " %*d %*d %15s %u %*u %llu %*u %u %*u %llu %*u %*u %*u %*u"
+ : " %*d %*d %15s %u %llu %u %llu",
+ //&part_major,
+ //&part_minor,
+ (*partitions)[cPartition].partition_name,
+ &(*partitions)[cPartition].reads,
+ &(*partitions)[cPartition].reads_sectors,
+ &(*partitions)[cPartition].writes,
+ &(*partitions)[cPartition].requested_writes
+ );
+ (*partitions)[cPartition++].parent_disk = cDisk-1;
+ (*disks)[cDisk-1].partitions++;
+ }
+ }
+
+ return cDisk;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// based on Fabian Frederick's /proc/slabinfo parser
+
+unsigned int getslabinfo (struct slab_cache **slab){
+ FILE* fd;
+ int cSlab = 0;
+ buff[BUFFSIZE-1] = 0;
+ *slab = NULL;
+ fd = fopen("/proc/slabinfo", "rb");
+ if(!fd) crash("/proc/slabinfo");
+ while (fgets(buff,BUFFSIZE-1,fd)){
+ if(!memcmp("slabinfo - version:",buff,19)) continue; // skip header
+ if(*buff == '#') continue; // skip comments
+ (*slab) = realloc(*slab, (cSlab+1)*sizeof(struct slab_cache));
+ sscanf(buff, "%47s %u %u %u %u", // allow 47; max seen is 24
+ (*slab)[cSlab].name,
+ &(*slab)[cSlab].active_objs,
+ &(*slab)[cSlab].num_objs,
+ &(*slab)[cSlab].objsize,
+ &(*slab)[cSlab].objperslab
+ ) ;
+ cSlab++;
+ }
+ fclose(fd);
+ return cSlab;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+unsigned get_pid_digits(void){
+ char pidbuf[24];
+ char *endp;
+ long rc;
+ int fd;
+ static unsigned ret;
+
+ if(ret) goto out;
+ ret = 5;
+ fd = open("/proc/sys/kernel/pid_max", O_RDONLY);
+ if(fd==-1) goto out;
+ rc = read(fd, pidbuf, sizeof pidbuf);
+ close(fd);
+ if(rc<3) goto out;
+ pidbuf[rc] = '\0';
+ rc = strtol(pidbuf,&endp,10);
+ if(rc<42) goto out;
+ if(*endp && *endp!='\n') goto out;
+ rc--; // the pid_max value is really the max PID plus 1
+ ret = 0;
+ while(rc){
+ rc /= 10;
+ ret++;
+ }
+out:
+ return ret;
+}
diff --git a/smartt-top/proc/sysinfo.h b/smartt-top/proc/sysinfo.h
new file mode 100644
index 0000000..494c4b3
--- /dev/null
+++ b/smartt-top/proc/sysinfo.h
@@ -0,0 +1,135 @@
+#ifndef PROC_SYSINFO_H
+#define PROC_SYSINFO_H
+#include <sys/types.h>
+#include <sys/dir.h>
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+extern unsigned long long Hertz; /* clock tick frequency */
+extern long smp_num_cpus; /* number of CPUs */
+extern int have_privs; /* boolean, true if setuid or similar */
+
+#if 0
+#define JT double
+extern void eight_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret, JT *wret, JT *xret, JT *yret, JT *zret);
+#undef JT
+#endif
+
+extern int uptime (double *uptime_secs, double *idle_secs);
+extern void loadavg(double *av1, double *av5, double *av15);
+
+
+/* obsolete */
+extern unsigned long kb_main_shared;
+/* old but still kicking -- the important stuff */
+extern unsigned long kb_main_buffers;
+extern unsigned long kb_main_cached;
+extern unsigned long kb_main_free;
+extern unsigned long kb_main_total;
+extern unsigned long kb_swap_free;
+extern unsigned long kb_swap_total;
+/* recently introduced */
+extern unsigned long kb_high_free;
+extern unsigned long kb_high_total;
+extern unsigned long kb_low_free;
+extern unsigned long kb_low_total;
+/* 2.4.xx era */
+extern unsigned long kb_active;
+extern unsigned long kb_inact_laundry; // grrr...
+extern unsigned long kb_inact_dirty;
+extern unsigned long kb_inact_clean;
+extern unsigned long kb_inact_target;
+extern unsigned long kb_swap_cached; /* late 2.4+ */
+/* derived values */
+extern unsigned long kb_swap_used;
+extern unsigned long kb_main_used;
+/* 2.5.41+ */
+extern unsigned long kb_writeback;
+extern unsigned long kb_slab;
+extern unsigned long nr_reversemaps;
+extern unsigned long kb_committed_as;
+extern unsigned long kb_dirty;
+extern unsigned long kb_inactive;
+extern unsigned long kb_mapped;
+extern unsigned long kb_pagetables;
+
+#define BUFFSIZE (64*1024)
+typedef unsigned long long jiff;
+extern void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz,
+ unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout,
+ unsigned *restrict intr, unsigned *restrict ctxt,
+ unsigned int *restrict running, unsigned int *restrict blocked,
+ unsigned int *restrict btime, unsigned int *restrict processes);
+
+extern void meminfo(void);
+
+
+extern unsigned long vm_nr_dirty;
+extern unsigned long vm_nr_writeback;
+extern unsigned long vm_nr_pagecache;
+extern unsigned long vm_nr_page_table_pages;
+extern unsigned long vm_nr_reverse_maps;
+extern unsigned long vm_nr_mapped;
+extern unsigned long vm_nr_slab;
+extern unsigned long vm_pgpgin;
+extern unsigned long vm_pgpgout;
+extern unsigned long vm_pswpin;
+extern unsigned long vm_pswpout;
+extern unsigned long vm_pgalloc;
+extern unsigned long vm_pgfree;
+extern unsigned long vm_pgactivate;
+extern unsigned long vm_pgdeactivate;
+extern unsigned long vm_pgfault;
+extern unsigned long vm_pgmajfault;
+extern unsigned long vm_pgscan;
+extern unsigned long vm_pgrefill;
+extern unsigned long vm_pgsteal;
+extern unsigned long vm_kswapd_steal;
+extern unsigned long vm_pageoutrun;
+extern unsigned long vm_allocstall;
+
+extern void vminfo(void);
+
+typedef struct disk_stat{
+ unsigned long long reads_sectors;
+ unsigned long long written_sectors;
+ char disk_name [16];
+ unsigned inprogress_IO;
+ unsigned merged_reads;
+ unsigned merged_writes;
+ unsigned milli_reading;
+ unsigned milli_spent_IO;
+ unsigned milli_writing;
+ unsigned partitions;
+ unsigned reads;
+ unsigned weighted_milli_spent_IO;
+ unsigned writes;
+}disk_stat;
+
+typedef struct partition_stat{
+ char partition_name [16];
+ unsigned long long reads_sectors;
+ unsigned parent_disk; // index into a struct disk_stat array
+ unsigned reads;
+ unsigned writes;
+ unsigned long long requested_writes;
+}partition_stat;
+
+extern unsigned int getpartitions_num(struct disk_stat *disks, int ndisks);
+extern unsigned int getdiskstat (struct disk_stat**,struct partition_stat**);
+
+typedef struct slab_cache{
+ char name[48];
+ unsigned active_objs;
+ unsigned num_objs;
+ unsigned objsize;
+ unsigned objperslab;
+}slab_cache;
+
+extern unsigned int getslabinfo (struct slab_cache**);
+
+extern unsigned get_pid_digits(void) FUNCTION;
+
+EXTERN_C_END
+#endif /* SYSINFO_H */
diff --git a/smartt-top/proc/version.c b/smartt-top/proc/version.c
new file mode 100644
index 0000000..cc215bc
--- /dev/null
+++ b/smartt-top/proc/version.c
@@ -0,0 +1,58 @@
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Ammended by cblake to only export the function symbol.
+ *
+ * Modified by Albert Cahalan, ????-2003
+ *
+ * Redistributable under the terms of the
+ * GNU Library General Public License; see COPYING
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "version.h"
+
+#ifdef MINORVERSION
+const char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION;
+#else
+const char procps_version[] = "procps version " VERSION "." SUBVERSION;
+#endif
+
+void display_version(void) {
+ fprintf(stdout, "%s\n", procps_version);
+}
+
+/* Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ */
+#include <sys/utsname.h>
+
+#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
+
+int linux_version_code;
+
+void init_Linux_version(void) {
+ int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */
+ FILE *fp;
+ char buf[256];
+
+ if ( (fp=fopen("/proc/version","r")) == NULL) {
+ fprintf(stderr, "Cannot find /proc/version - is /proc mounted?\n");
+ exit(1);
+ }
+ if (fgets(buf, 256, fp) == NULL) {
+ fprintf(stderr, "Cannot read kernel version from /proc/version\n");
+ fclose(fp);
+ exit(1);
+ }
+ fclose(fp);
+ if (sscanf(buf, "Linux version %d.%d.%d", &x, &y, &z) < 3)
+ fprintf(stderr, /* *very* unlikely to happen by accident */
+ "Non-standard uts for running kernel:\n"
+ "release %s=%d.%d.%d gives version code %d\n",
+ buf,
+ x, y, z, LINUX_VERSION(x,y,z));
+ linux_version_code = LINUX_VERSION(x, y, z);
+}
diff --git a/smartt-top/proc/version.h b/smartt-top/proc/version.h
new file mode 100644
index 0000000..04a75e9
--- /dev/null
+++ b/smartt-top/proc/version.h
@@ -0,0 +1,32 @@
+#ifndef PROC_VERSION_H
+#define PROC_VERSION_H
+
+#include "procps.h"
+
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ * Distributable under the terms of the GNU Library General Public License
+ *
+ * Copyright 2002 Albert Cahalan
+ */
+
+EXTERN_C_BEGIN
+
+void init_Linux_version(void); /* Get Linux version */
+extern void display_version(void); /* display suite version */
+extern const char procps_version[]; /* global buf for suite version */
+
+extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE
+ in /usr/include/linux/version.h */
+
+/* Convenience macros for composing/decomposing version codes */
+#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
+#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF)
+#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF)
+#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF)
+
+EXTERN_C_END
+
+#endif /* PROC_VERSION_H */
diff --git a/smartt-top/proc/wchan.h b/smartt-top/proc/wchan.h
new file mode 100644
index 0000000..93d4e70
--- /dev/null
+++ b/smartt-top/proc/wchan.h
@@ -0,0 +1,16 @@
+#ifndef PROCPS_PROC_WCHAN_H
+#define PROCPS_PROC_WCHAN_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+typedef void (*message_fn)(const char *restrict, ...) __attribute__((format(printf,1,2)));
+
+extern const char * lookup_wchan(unsigned KLONG address, unsigned pid);
+extern int open_psdb(const char *restrict override);
+extern int open_psdb_message(const char *restrict override, message_fn message);
+
+EXTERN_C_END
+
+#endif
diff --git a/smartt-top/proc/whattime.c b/smartt-top/proc/whattime.c
new file mode 100644
index 0000000..d2785a8
--- /dev/null
+++ b/smartt-top/proc/whattime.c
@@ -0,0 +1,87 @@
+/* This is a trivial uptime program. I hereby release this program
+ * into the public domain. I disclaim any responsibility for this
+ * program --- use it at your own risk. (as if there were any.. ;-)
+ * -michaelkjohnson (johnsonm@sunsite.unc.edu)
+ *
+ * Modified by Larry Greenfield to give a more traditional output,
+ * count users, etc. (greenfie@gauss.rutgers.edu)
+ *
+ * Modified by mkj again to fix a few tiny buglies.
+ *
+ * Modified by J. Cowley to add printing the uptime message to a
+ * string (for top) and to optimize file handling. 19 Mar 1993.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utmp.h>
+#include <sys/ioctl.h>
+#include "whattime.h"
+#include "sysinfo.h"
+
+static char buf[128];
+static double av[3];
+
+char *sprint_uptime(void) {
+ struct utmp *utmpstruct;
+ int upminutes, uphours, updays;
+ int pos;
+ struct tm *realtime;
+ time_t realseconds;
+ int numuser;
+ double uptime_secs, idle_secs;
+
+/* first get the current time */
+
+ time(&realseconds);
+ realtime = localtime(&realseconds);
+ pos = sprintf(buf, " %02d:%02d:%02d ",
+ realtime->tm_hour, realtime->tm_min, realtime->tm_sec);
+
+/* read and calculate the amount of uptime */
+
+ uptime(&uptime_secs, &idle_secs);
+
+ updays = (int) uptime_secs / (60*60*24);
+ strcat (buf, "up ");
+ pos += 3;
+ if (updays)
+ pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : "");
+ upminutes = (int) uptime_secs / 60;
+ uphours = upminutes / 60;
+ uphours = uphours % 24;
+ upminutes = upminutes % 60;
+ if(uphours)
+ pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes);
+ else
+ pos += sprintf(buf + pos, "%d min, ", upminutes);
+
+/* count the number of users */
+
+ numuser = 0;
+ setutent();
+ while ((utmpstruct = getutent())) {
+ if ((utmpstruct->ut_type == USER_PROCESS) &&
+ (utmpstruct->ut_name[0] != '\0'))
+ numuser++;
+ }
+ endutent();
+
+ pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s");
+
+ loadavg(&av[0], &av[1], &av[2]);
+
+ pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f",
+ av[0], av[1], av[2]);
+
+ return buf;
+}
+
+void print_uptime(void) {
+ printf("%s\n", sprint_uptime());
+}
diff --git a/smartt-top/proc/whattime.h b/smartt-top/proc/whattime.h
new file mode 100644
index 0000000..891ccd3
--- /dev/null
+++ b/smartt-top/proc/whattime.h
@@ -0,0 +1,13 @@
+#ifndef PROC_WHATTIME_H
+#define PROC_WHATTIME_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+extern void print_uptime(void);
+extern char *sprint_uptime(void);
+
+EXTERN_C_END
+
+#endif
diff --git a/smartt-top/procps.lsm b/smartt-top/procps.lsm
new file mode 100644
index 0000000..f2b8064
--- /dev/null
+++ b/smartt-top/procps.lsm
@@ -0,0 +1,15 @@
+Begin4
+Title: procps
+Version: 3.2.7
+Entered-date: 2006-06-25
+Description: Linux system utilities
+Keywords: procps /proc libproc sysctl pmap ps uptime tload slabtop
+ free w top vmstat watch skill snice kill pgrep pkill
+Author: Albert Cahalan, Michael K. Johnson, Jim Warner, etc.
+Maintained-by: various <procps-feedback@lists.sf.net>
+Primary-site: http://procps.sf.net/
+ 281kB procps-3.2.7.tar.gz
+Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html
+ 281kB procps-3.2.7.tar.gz
+Copying-policy: mixed
+End
diff --git a/smartt-top/procps.spec b/smartt-top/procps.spec
new file mode 100644
index 0000000..5ac1be9
--- /dev/null
+++ b/smartt-top/procps.spec
@@ -0,0 +1,52 @@
+URL: http://procps.sf.net/
+Summary: System and process monitoring utilities
+Name: procps
+%define major_version 3
+%define minor_version 2
+%define revision 8
+%define version %{major_version}.%{minor_version}.%{revision}
+Version: %{version}
+Release: 1
+License: LGPL, GPL, BSD-like
+Group: Applications/System
+Source: http://procps.sf.net/procps-%{version}.tar.gz
+BuildRoot: %{_tmppath}/procps-root
+Packager: <procps-feedback@lists.sf.net>
+
+%description
+The procps package contains a set of system utilities which provide
+system information. Procps includes ps, free, sysctl, skill, snice,
+tload, top, uptime, vmstat, w, and watch. You need some of these.
+
+%prep
+%setup
+
+%build
+make SKIP="/bin/kill /usr/share/man/man1/kill.1" CFLAGS="$RPM_OPT_FLAGS"
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make SKIP="/bin/kill /usr/share/man/man1/kill.1" DESTDIR=$RPM_BUILD_ROOT ldconfig=echo install="install -D" lib="$RPM_BUILD_ROOT/%{_lib}/" install
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+# add libproc to the cache
+/sbin/ldconfig
+
+%files
+%defattr(0644,root,root,755)
+%doc NEWS BUGS TODO COPYING COPYING.LIB README.top README AUTHORS sysctl.conf
+%attr(555,root,root) /lib*/libproc*.so*
+%attr(555,root,root) /bin/*
+%attr(555,root,root) /sbin/*
+%attr(555,root,root) /usr/bin/*
+
+%attr(0644,root,root) /usr/share/man/man1/*
+%attr(0644,root,root) /usr/share/man/man5/*
+%attr(0644,root,root) /usr/share/man/man8/*
+
+%changelog
+* Fri Apr 14 09:23:45 PDT 2006 Jesse Brandeburg <jesse.brandeburg@in...>
+- fix missing trailing slash in %install to fix builds on x86_64
diff --git a/smartt-top/ps/COPYING b/smartt-top/ps/COPYING
new file mode 100644
index 0000000..92b8903
--- /dev/null
+++ b/smartt-top/ps/COPYING
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/smartt-top/ps/HACKING b/smartt-top/ps/HACKING
new file mode 100644
index 0000000..ffae92c
--- /dev/null
+++ b/smartt-top/ps/HACKING
@@ -0,0 +1,53 @@
+Warning:
+
+This code must corrctly handle lots of picky little details to meet
+the Unix98 standard while simultaneously being as compatible as
+possible with the original Linux ps. Don't "fix" something without
+considering the impact on all the special-case code. For example,
+the "tty" format _must_ use "TT" as the header, even though the SysV
+output formats _must_ use "TTY".
+
+File overview:
+
+display.c main(), debug code, iterates over processes
+escape.c Does stuff like \202 and &lt; to command and environment.
+global.c Data + code to init it.
+help.c Help message.
+output.c Giant tables and lots of output functions.
+parser.c Initial command parsing.
+select.c want_this_proc() checks a process against flags & lists
+sortformat.c Parses sort & format specifier lists. Picks output format.
+stacktrace.c Debug code, not normally used.
+../proc/* Library used to gather data.
+regression Regression tests that ought to be run.
+common.h Lots of interesting stuff.
+Makefile Makefile
+p Script used to test ps when the library is not installed.
+utf Empty file used to test "ps ut?" unmangling behavior.
+ps.1 Man page.
+
+Compiling:
+
+Whatever you do, don't trust the Makefiles. They are severely broken.
+You can touch top.h and top won't be recompiled, or you can change
+library files and discover that the library and programs won't be
+recompiled.
+
+Operation:
+
+Unless the personality forces BSD parsing, parser.c tries to parse the
+command line as a mixed BSD+SysV+Gnu mess. On failure, BSD parsing is
+attempted. If BSD parsing fails _after_ SysV parsing has been attempted,
+the error message comes from the original SysV parse.
+
+Control goes to sortformat.c, which must pick apart ambiguous options
+like "O". Failure can reset the whole program and set PER_FORCE_BSD,
+which means a second trip through parser.c and sortformat.c.
+
+The choice of output format happens in sortformat.c. There is a switch()
+with all the valid format_flags combinations. The SysV and default
+options are NULL (unless overridden by personality), which causes a
+trip through SysV output format generation hackery. Note that the
+default format always goes through there, even if it is for BSD.
+Formats that came from the switch() (generally BSD, plus overrides)
+get mangled a bit to support various SysV output modifiers.
diff --git a/smartt-top/ps/TRANSLATION b/smartt-top/ps/TRANSLATION
new file mode 100644
index 0000000..ff8df84
--- /dev/null
+++ b/smartt-top/ps/TRANSLATION
@@ -0,0 +1,39 @@
+Initially I only want to translate the --help output and man page.
+Common error messages would be next on the list. I want to avoid
+run-time overhead and bloat.
+
+Translations of the --help output should not be longer than 22 lines long.
+Feel free to leave out the less useful options to save space. (not even
+the English help text has all the options)
+
+I think these are the most important options:
+
+*** selection ***
+-C by command name list
+-G by real group ID list (supports names)
+-U by real user ID list (supports names)
+-u by effective user ID list (supports names)
+-e all processes
+-p by process ID list
+
+*** output ***
+--no-heading No header line.
+-o,o user-defined output
+-j,j job control format
+-l,l long format
+-f full format
+s signal format
+u user-oriented format
+--forest ASCII art forest (process hierarchy)
+c show true command name
+
+List of man page translators:
+
+de Wed Jan 10 19:09:15 2001 by Martin Schulze <joey@infodrom.ffis.de>
+es 19 Jan 1999 by Diego Sevilla Ruiz (dsevilla@ditec.um.es)
+fr 09/06/1997 par Christophe Blaess (ccb@club-internet.fr)
+hu Horv#th Andr#s (the '#' is 'a' w/ '/') <horvatha@rs1.szif.hu>
+it Traduzione in italiano di Giovanni Bortolozzo <borto@dei.unipd.it>
+it Revisione parziale di Daniele Giacomini <daniele@evo.it> 30/03/1999
+ja Tue Nov 14 2000 by NAKANO Takeo <nakano@apm.seikei.ac.jp>
+nl <manpages-nl@nl.linux.org>
diff --git a/smartt-top/ps/common.h b/smartt-top/ps/common.h
new file mode 100644
index 0000000..0b47d90
--- /dev/null
+++ b/smartt-top/ps/common.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#ifndef PROCPS_PS_H
+#define PROCPS_PS_H
+
+#include "../proc/procps.h"
+#include "../proc/escape.h"
+#include "../proc/readproc.h"
+
+#if 0
+#define trace(args...) printf(## args)
+#else
+#define trace(args...)
+#endif
+
+
+/***************** GENERAL DEFINE ********************/
+
+
+/* selection list */
+#define SEL_RUID 1
+#define SEL_EUID 2
+#define SEL_SUID 3
+#define SEL_FUID 4
+#define SEL_RGID 5
+#define SEL_EGID 6
+#define SEL_SGID 7
+#define SEL_FGID 8
+#define SEL_PGRP 9
+#define SEL_PID 10
+#define SEL_TTY 11
+#define SEL_SESS 12
+#define SEL_COMM 13
+#define SEL_PPID 14
+
+/* Since an enum could be smashed by a #define, it would be bad. */
+#define U98 0 /* Unix98 standard */ /* This must be 0 */
+#define XXX 1 /* Common extension */
+#define DEC 2 /* Digital Unix */
+#define AIX 3 /* AIX */
+#define SCO 4 /* SCO */
+#define LNX 5 /* Linux original :-) */
+#define BSD 6 /* FreeBSD and OpenBSD */
+#define SUN 7 /* SunOS 5 (Solaris) */
+#define HPU 8 /* HP-UX */
+#define SGI 9 /* Irix */
+#define SOE 10 /* IBM's S/390 OpenEdition */
+#define TST 11 /* test code */
+
+/*
+ * Try not to overflow the output buffer:
+ * 32 pages for env+cmd
+ * 64 kB pages on IA-64
+ * 4 chars for "\377", or 1 when mangling to '?' (ESC_STRETCH)
+ * plus some slack for other stuff
+ * That is about 8.5 MB on IA-64, or 0.6 MB on i386
+ *
+ * Sadly, current kernels only supply one page of env/command data.
+ * The buffer is now protected with a guard page, and via other means
+ * to avoid hitting the guard page.
+ */
+
+/* output buffer size */
+#define OUTBUF_SIZE (2 * 64*1024 * ESC_STRETCH)
+
+/******************* PS DEFINE *******************/
+
+// Column flags
+// Justification control for flags field comes first.
+#define CF_JUST_MASK 0x0f
+// CF_AIXHACK 0
+#define CF_USER 1 // left if text, right if numeric
+#define CF_LEFT 2
+#define CF_RIGHT 3
+#define CF_UNLIMITED 4
+#define CF_WCHAN 5 // left if text, right if numeric
+#define CF_SIGNAL 6 // right in 9, or 16 if screen_cols>107
+// Then the other flags
+#define CF_PIDMAX 0x00000010 // react to pid_max
+// Only one allowed; use separate bits to catch errors.
+#define CF_PRINT_THREAD_ONLY 0x10000000
+#define CF_PRINT_PROCESS_ONLY 0x20000000
+#define CF_PRINT_EVERY_TIME 0x40000000
+#define CF_PRINT_AS_NEEDED 0x80000000 // means we have no clue, so assume EVERY TIME
+#define CF_PRINT_MASK 0xf0000000
+
+#define needs_for_select (PROC_FILLSTAT | PROC_FILLSTATUS)
+
+/* thread_flags */
+#define TF_B_H 0x0001
+#define TF_B_m 0x0002
+#define TF_U_m 0x0004
+#define TF_U_T 0x0008
+#define TF_U_L 0x0010
+#define TF_show_proc 0x0100 // show the summary line
+#define TF_show_task 0x0200 // show the per-thread lines
+#define TF_show_both 0x0400 // distinct proc/task format lists
+#define TF_loose_tasks 0x0800 // let sorting break up task groups (BSDish)
+#define TF_no_sort 0x1000 // don't know if thread-grouping should survive a sort
+#define TF_no_forest 0x2000 // don't see how to do threads w/ forest option
+#define TF_must_use 0x4000 // options only make sense if LWP/SPID column added
+
+/* personality control flags */
+#define PER_BROKEN_o 0x0001
+#define PER_BSD_h 0x0002
+#define PER_BSD_m 0x0004
+#define PER_IRIX_l 0x0008
+#define PER_FORCE_BSD 0x0010
+#define PER_GOOD_o 0x0020
+#define PER_OLD_m 0x0040
+#define PER_NO_DEFAULT_g 0x0080
+#define PER_ZAP_ADDR 0x0100
+#define PER_SANE_USER 0x0200
+#define PER_HPUX_x 0x0400
+#define PER_SVR4_x 0x0800
+#define PER_BSD_COLS 0x1000
+#define PER_UNIX_COLS 0x2000
+
+/* Simple selections by bit mask */
+#define SS_B_x 0x01
+#define SS_B_g 0x02
+#define SS_U_d 0x04
+#define SS_U_a 0x08
+#define SS_B_a 0x10
+
+/* predefined format flags such as: -l -f l u s -j */
+#define FF_Uf 0x0001 /* -f */
+#define FF_Uj 0x0002 /* -j */
+#define FF_Ul 0x0004 /* -l */
+#define FF_Bj 0x0008 /* j */
+#define FF_Bl 0x0010 /* l */
+#define FF_Bs 0x0020 /* s */
+#define FF_Bu 0x0040 /* u */
+#define FF_Bv 0x0080 /* v */
+#define FF_LX 0x0100 /* X */
+#define FF_Lm 0x0200 /* m */ /* overloaded: threads, sort, format */
+#define FF_Fc 0x0400 /* --context */ /* Flask security context format */
+
+/* predefined format modifier flags such as: -l -f l u s -j */
+#define FM_c 0x0001 /* -c */
+#define FM_j 0x0002 /* -j */ /* only set when !sysv_j_format */
+#define FM_y 0x0004 /* -y */
+//#define FM_L 0x0008 /* -L */
+#define FM_P 0x0010 /* -P */
+#define FM_M 0x0020 /* -M */
+//#define FM_T 0x0040 /* -T */
+#define FM_F 0x0080 /* -F */ /* -F also sets the regular -f flags */
+
+/* sorting & formatting */
+/* U,B,G is Unix,BSD,Gnu and then there is the option itself */
+#define SF_U_O 1
+#define SF_U_o 2
+#define SF_B_O 3
+#define SF_B_o 4
+#define SF_B_m 5 /* overloaded: threads, sort, format */
+#define SF_G_sort 6
+#define SF_G_format 7
+
+/* headers */
+#define HEAD_SINGLE 0 /* default, must be 0 */
+#define HEAD_NONE 1
+#define HEAD_MULTI 2
+
+
+/********************** GENERAL TYPEDEF *******************/
+
+/* Other fields that might be useful:
+ *
+ * char *name; user-defined column name (format specification)
+ * int reverse; sorting in reverse (sort specification)
+ *
+ * name in place of u
+ * reverse in place of n
+ */
+
+typedef union sel_union {
+ pid_t pid;
+ pid_t ppid;
+ uid_t uid;
+ gid_t gid;
+ dev_t tty;
+ char cmd[16]; /* this is _not_ \0 terminated */
+} sel_union;
+
+typedef struct selection_node {
+ struct selection_node *next;
+ sel_union *u; /* used if selection type has a list of values */
+ int n; /* used if selection type has a list of values */
+ int typecode;
+} selection_node;
+
+typedef struct sort_node {
+ struct sort_node *next;
+ int (*sr)(const proc_t* P, const proc_t* Q); /* sort function */
+ int reverse; /* can sort backwards */
+ int typecode;
+ int need;
+} sort_node;
+
+typedef struct format_node {
+ struct format_node *next;
+ char *name; /* user can override default name */
+ int (*pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function
+/* int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */
+ int width;
+ int need;
+ int vendor; /* Vendor that invented this */
+ int flags;
+ int typecode;
+} format_node;
+
+typedef struct format_struct {
+ const char *spec; /* format specifier */
+ const char *head; /* default header in the POSIX locale */
+ int (* const pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function
+ int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */
+ const int width;
+ const int need; /* data we will need (files to read, etc.) */
+ const int vendor; /* Where does this come from? */
+ const int flags;
+} format_struct;
+
+/* though ps-specific, needed by general file */
+typedef struct macro_struct {
+ const char *spec; /* format specifier */
+ const char *head; /* default header in the POSIX locale */
+} macro_struct;
+
+/**************** PS TYPEDEF ***********************/
+
+typedef struct aix_struct {
+ const int desc; /* 1-character format code */
+ const char *spec; /* format specifier */
+ const char *head; /* default header in the POSIX locale */
+} aix_struct;
+
+typedef struct shortsort_struct {
+ const int desc; /* 1-character format code */
+ const char *spec; /* format specifier */
+} shortsort_struct;
+
+/* Save these options for later: -o o -O O --format --sort */
+typedef struct sf_node {
+ struct sf_node *next; /* next arg */
+ format_node *f_cooked; /* convert each arg alone, then merge */
+ sort_node *s_cooked; /* convert each arg alone, then merge */
+ char *sf;
+ int sf_code;
+} sf_node;
+
+/********************* UNDECIDED GLOBALS **************/
+
+/* output.c */
+extern void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt);
+extern void print_format_specifiers(void);
+extern const aix_struct *search_aix_array(const int findme);
+extern const shortsort_struct *search_shortsort_array(const int findme);
+extern const format_struct *search_format_array(const char *findme);
+extern const macro_struct *search_macro_array(const char *findme);
+extern void init_output(void);
+extern int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp);
+
+/* global.c */
+extern void reset_global(void);
+
+/* global.c */
+extern int all_processes;
+extern const char *bsd_j_format;
+extern const char *bsd_l_format;
+extern const char *bsd_s_format;
+extern const char *bsd_u_format;
+extern const char *bsd_v_format;
+extern int bsd_c_option;
+extern int bsd_e_option;
+extern uid_t cached_euid;
+extern dev_t cached_tty;
+extern char forest_prefix[4 * 32*1024 + 100];
+extern int forest_type;
+extern unsigned format_flags; /* -l -f l u s -j... */
+extern format_node *format_list; /* digested formatting options */
+extern unsigned format_modifiers; /* -c -j -y -P -L... */
+extern int header_gap;
+extern int header_type; /* none, single, multi... */
+extern int include_dead_children;
+extern int lines_to_next_header;
+extern int max_line_width;
+extern const char *namelist_file;
+extern int negate_selection;
+extern int page_size; // "int" for math reasons?
+extern unsigned personality;
+extern int prefer_bsd_defaults;
+extern int running_only;
+extern int screen_cols;
+extern int screen_rows;
+extern unsigned long seconds_since_boot;
+extern selection_node *selection_list;
+extern unsigned simple_select;
+extern sort_node *sort_list;
+extern const char *sysv_f_format;
+extern const char *sysv_fl_format;
+extern const char *sysv_j_format;
+extern const char *sysv_l_format;
+extern unsigned thread_flags;
+extern int unix_f_option;
+extern int user_is_number;
+extern int wchan_is_number;
+
+/************************* PS GLOBALS *********************/
+
+/* sortformat.c */
+extern int defer_sf_option(const char *arg, int source);
+extern const char *process_sf_options(int localbroken);
+extern void reset_sortformat(void);
+
+/* select.c */
+extern int want_this_proc(proc_t *buf);
+extern const char *select_bits_setup(void);
+
+/* help.c */
+extern const char *help_message;
+
+/* global.c */
+extern void self_info(void);
+
+/* parser.c */
+extern int arg_parse(int argc, char *argv[]);
+
+#endif
diff --git a/smartt-top/ps/display.c b/smartt-top/ps/display.c
new file mode 100644
index 0000000..6ef5d58
--- /dev/null
+++ b/smartt-top/ps/display.c
@@ -0,0 +1,623 @@
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#if (__GNU_LIBRARY__ >= 6)
+# include <locale.h>
+#endif
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* major/minor number */
+#include <sys/sysmacros.h>
+
+#include <signal.h> /* catch signals */
+
+#include "common.h"
+#include "../proc/wchan.h"
+#include "../proc/version.h"
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "../proc/sig.h"
+
+#ifndef SIGCHLD
+#define SIGCHLD SIGCLD
+#endif
+
+/* just reports a crash */
+static void signal_handler(int signo){
+ if(signo==SIGPIPE) _exit(0); /* "ps | head" will cause this */
+ /* fprintf() is not reentrant, but we _exit() anyway */
+ fprintf(stderr,
+ "\n\n"
+ "Signal %d (%s) caught by ps (%s).\n"
+ "Please send bug reports to <feedback@lists.sf.net> or <albert@users.sf.net>\n",
+ signo,
+ signal_number_to_name(signo),
+ procps_version
+ );
+ _exit(signo+128);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+#undef DEBUG
+#ifdef DEBUG
+void init_stack_trace(char *prog_name);
+
+#include <ctype.h>
+
+void hex_dump(void *vp){
+ char *charlist;
+ int i = 0;
+ int line = 45;
+ char *cp = (char *)vp;
+
+ while(line--){
+ printf("%8lx ", (unsigned long)cp);
+ charlist = cp;
+ cp += 16;
+ for(i=0; i<16; i++){
+ if((charlist[i]>31) && (charlist[i]<127)){
+ printf("%c", charlist[i]);
+ }else{
+ printf(".");
+ }
+ }
+ printf(" ");
+ for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i])));
+ printf("\n");
+ i=0;
+ }
+}
+
+static void show_tgid(char *s, int n, sel_union *data){
+ printf("%s ", s);
+ while(--n){
+ printf("%d,", data[n].tgid);
+ }
+ printf("%d\n", data[0].tgid);
+}
+
+static void show_uid(char *s, int n, sel_union *data){
+ struct passwd *pw_data;
+ printf("%s ", s);
+ while(--n){
+ pw_data = getpwuid(data[n].uid);
+ if(pw_data) printf("%s,", pw_data->pw_name);
+ else printf("%d,", data[n].uid);
+ }
+ pw_data = getpwuid(data[n].uid);
+ if(pw_data) printf("%s\n", pw_data->pw_name);
+ else printf("%d\n", data[n].uid);
+}
+
+static void show_gid(char *s, int n, sel_union *data){
+ struct group *gr_data;
+ printf("%s ", s);
+ while(--n){
+ gr_data = getgrgid(data[n].gid);
+ if(gr_data) printf("%s,", gr_data->gr_name);
+ else printf("%d,", data[n].gid);
+ }
+ gr_data = getgrgid(data[n].gid);
+ if(gr_data) printf("%s\n", gr_data->gr_name);
+ else printf("%d\n", data[n].gid);
+}
+
+static void show_tty(char *s, int n, sel_union *data){
+ printf("%s ", s);
+ while(--n){
+ printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty));
+ }
+ printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty));
+}
+
+static void show_cmd(char *s, int n, sel_union *data){
+ printf("%s ", s);
+ while(--n){
+ printf("%.8s,", data[n].cmd);
+ }
+ printf("%.8s\n", data[0].cmd);
+}
+
+static void arg_show(void){
+ selection_node *walk = selection_list;
+ while(walk){
+ switch(walk->typecode){
+ case SEL_RUID: show_uid("RUID", walk->n, walk->u); break;
+ case SEL_EUID: show_uid("EUID", walk->n, walk->u); break;
+ case SEL_SUID: show_uid("SUID", walk->n, walk->u); break;
+ case SEL_FUID: show_uid("FUID", walk->n, walk->u); break;
+ case SEL_RGID: show_gid("RGID", walk->n, walk->u); break;
+ case SEL_EGID: show_gid("EGID", walk->n, walk->u); break;
+ case SEL_SGID: show_gid("SGID", walk->n, walk->u); break;
+ case SEL_FGID: show_gid("FGID", walk->n, walk->u); break;
+ case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break;
+ case SEL_PID : show_pid("PID ", walk->n, walk->u); break;
+ case SEL_PPID: show_pid("PPID", walk->n, walk->u); break;
+ case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break;
+ case SEL_SESS: show_pid("SESS", walk->n, walk->u); break;
+ case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break;
+ default: printf("Garbage typecode value!\n");
+ }
+ walk = walk->next;
+ }
+}
+
+#endif
+//////////////////////////////////////////////////////////////////////////
+
+
+/***** check the header */
+/* Unix98: must not print empty header */
+static void check_headers(void){
+ format_node *walk = format_list;
+ int head_normal = 0;
+ if(header_type==HEAD_MULTI){
+ header_gap = screen_rows-1; /* true BSD */
+ return;
+ }
+ if(header_type==HEAD_NONE){
+ lines_to_next_header = -1; /* old Linux */
+ return;
+ }
+ while(walk){
+ if(!*(walk->name)){
+ walk = walk->next;
+ continue;
+ }
+ if(walk->pr){
+ head_normal++;
+ walk = walk->next;
+ continue;
+ }
+ walk = walk->next;
+ }
+ if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */
+}
+
+/***** check sort needs */
+/* see what files need to be read, etc. */
+static unsigned check_sort_needs(sort_node *walk){
+ unsigned needs = 0;
+ while(walk){
+ needs |= walk->need;
+ walk = walk->next;
+ }
+ return needs;
+}
+
+/***** check needs */
+/* see what files need to be read, etc. */
+static unsigned collect_format_needs(format_node *walk){
+ unsigned needs = 0;
+ while(walk){
+ needs |= walk->need;
+ walk = walk->next;
+ }
+ return needs;
+}
+
+static format_node *proc_format_list;
+static format_node *task_format_list;
+
+static unsigned needs_for_threads;
+static unsigned needs_for_sort;
+static unsigned proc_format_needs;
+static unsigned task_format_needs;
+
+#define needs_for_format (proc_format_needs|task_format_needs)
+
+#define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM|PROC_FILLCGROUP)
+/***** munge lists and determine openproc() flags */
+static void lists_and_needs(void){
+ check_headers();
+
+ // only care about the difference when showing both
+ if(thread_flags & TF_show_both){
+ format_node pfn, tfn; // junk, to handle special case at begin of list
+ format_node *walk = format_list;
+ format_node *p_end = &pfn;
+ format_node *t_end = &tfn;
+ while(walk){
+ format_node *new = malloc(sizeof(format_node));
+ memcpy(new,walk,sizeof(format_node));
+ p_end->next = walk;
+ t_end->next = new;
+ p_end = walk;
+ t_end = new;
+ switch(walk->flags & CF_PRINT_MASK){
+ case CF_PRINT_THREAD_ONLY:
+ p_end->pr = pr_nop;
+ p_end->need = 0;
+ break;
+ case CF_PRINT_PROCESS_ONLY:
+ t_end->pr = pr_nop;
+ t_end->need = 0;
+ break;
+ default:
+ fprintf(stderr, "please report this bug\n");
+ // FALL THROUGH
+ case CF_PRINT_AS_NEEDED:
+ case CF_PRINT_EVERY_TIME:
+ break;
+ }
+ walk = walk->next;
+ }
+ t_end->next = NULL;
+ p_end->next = NULL;
+ proc_format_list = pfn.next;
+ task_format_list = tfn.next;
+ }else{
+ proc_format_list = format_list;
+ task_format_list = format_list;
+ }
+
+ proc_format_needs = collect_format_needs(proc_format_list);
+ task_format_needs = collect_format_needs(task_format_list);
+
+ needs_for_sort = check_sort_needs(sort_list);
+
+ // move process-only flags to the process
+ proc_format_needs |= (task_format_needs &~ PROC_ONLY_FLAGS);
+ task_format_needs &= ~PROC_ONLY_FLAGS;
+
+ if(bsd_c_option){
+ proc_format_needs &= ~PROC_FILLARG;
+ needs_for_sort &= ~PROC_FILLARG;
+ }
+ if(!unix_f_option){
+ proc_format_needs &= ~PROC_FILLCOM;
+ needs_for_sort &= ~PROC_FILLCOM;
+ }
+ // convert ARG to COM as a standard
+ if(proc_format_needs & PROC_FILLARG){
+ proc_format_needs |= PROC_FILLCOM;
+ proc_format_needs &= ~PROC_FILLARG;
+ }
+ if(bsd_e_option){
+ if(proc_format_needs&PROC_FILLCOM) proc_format_needs |= PROC_FILLENV;
+ }
+
+ /* FIXME broken filthy hack -- got to unify some stuff here */
+ if( ( (proc_format_needs|task_format_needs|needs_for_sort) & PROC_FILLWCHAN) && !wchan_is_number)
+ if (open_psdb(namelist_file)) wchan_is_number = 1;
+
+ if(thread_flags&TF_loose_tasks) needs_for_threads |= PROC_LOOSE_TASKS;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+/***** fill in %CPU; not in libproc because of include_dead_children */
+/* Note: for sorting, not display, so 0..0x7fffffff would be OK */
+static int want_this_proc_pcpu(proc_t *buf){
+ unsigned long long used_jiffies;
+ unsigned long pcpu = 0;
+ unsigned long long avail_jiffies;
+
+ if(!want_this_proc(buf)) return 0;
+
+ used_jiffies = buf->utime + buf->stime;
+ if(include_dead_children) used_jiffies += (buf->cutime + buf->cstime);
+
+ avail_jiffies = seconds_since_boot * Hertz - buf->start_time;
+ if(avail_jiffies) pcpu = (used_jiffies << 24) / avail_jiffies;
+
+ buf->pcpu = pcpu; // fits in an int, summing children on 128 CPUs
+
+ return 1;
+}
+
+/***** just display */
+static void simple_spew(void){
+ proc_t buf;
+ PROCTAB* ptp;
+ ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
+ if(!ptp) {
+ fprintf(stderr, "Error: can not access /proc.\n");
+ exit(1);
+ }
+ memset(&buf, '#', sizeof(proc_t));
+ switch(thread_flags & (TF_show_proc|TF_loose_tasks|TF_show_task)){
+ case TF_show_proc: // normal non-thread output
+ while(readproc(ptp,&buf)){
+ if(want_this_proc(&buf)){
+ show_one_proc(&buf, proc_format_list);
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
+ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
+ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ case TF_show_proc|TF_loose_tasks: // H option
+ while(readproc(ptp,&buf)){
+ proc_t buf2;
+ // must still have the process allocated
+ while(readtask(ptp,&buf,&buf2)){
+ if(want_this_proc(&buf)) show_one_proc(&buf2, task_format_list);
+ if(buf2.nsupgid > 0 && buf2.supgid && buf.supgid!=buf2.supgid) free(buf2.supgid);
+ if((ptp->flags & PROC_FILLSUPGRP) && buf2.nsupgid>0 && buf2.supgrp && buf.supgrp!=buf2.supgrp)
+ freesupgrp(&buf2);
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
+ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
+ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ case TF_show_proc|TF_show_task: // m and -m options
+ while(readproc(ptp,&buf)){
+ if(want_this_proc(&buf)){
+ proc_t buf2;
+ show_one_proc(&buf, proc_format_list);
+ // must still have the process allocated
+ while(readtask(ptp,&buf,&buf2)){
+ show_one_proc(&buf2, task_format_list);
+ if(buf2.nsupgid > 0 && buf2.supgid && buf.supgid!=buf2.supgid) free(buf2.supgid);
+ if(ptp->flags & PROC_FILLSUPGRP && buf2.nsupgid>0 && buf2.supgrp && buf.supgrp!=buf2.supgrp)
+ freesupgrp(&buf2);
+ }
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
+ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
+ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ case TF_show_task: // -L and -T options
+ while(readproc(ptp,&buf)){
+ if(want_this_proc(&buf)){
+ proc_t buf2;
+ // must still have the process allocated
+ while(readtask(ptp,&buf,&buf2)){
+ show_one_proc(&buf2, task_format_list);
+ if(buf2.nsupgid > 0 && buf2.supgid && buf.supgid!=buf2.supgid) free(buf2.supgid);
+ if(ptp->flags & PROC_FILLSUPGRP && buf2.nsupgid>0 && buf2.supgrp && buf.supgrp!=buf2.supgrp)
+ freesupgrp(&buf2);
+ }
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ if(buf.cgroup) free((void*)*buf.cgroup);
+ if(buf.nsupgid > 0 && buf.supgid) free(buf.supgid);
+ if((ptp->flags & PROC_FILLSUPGRP) && buf.nsupgid>0 && buf.supgrp) freesupgrp(&buf);
+ }
+ break;
+ }
+ closeproc(ptp);
+}
+
+/***** forest output requires sorting by ppid; add start_time by default */
+static void prep_forest_sort(void){
+ sort_node *tmp_list = sort_list;
+ const format_struct *incoming;
+
+ if(!sort_list) { /* assume start time order */
+ incoming = search_format_array("start_time");
+ if(!incoming) fprintf(stderr, "Could not find start_time!\n");
+ tmp_list = malloc(sizeof(sort_node));
+ tmp_list->reverse = 0;
+ tmp_list->typecode = '?'; /* what was this for? */
+ tmp_list->sr = incoming->sr;
+ tmp_list->need = incoming->need;
+ tmp_list->next = sort_list;
+ sort_list = tmp_list;
+ }
+ /* this is required for the forest option */
+ incoming = search_format_array("ppid");
+ if(!incoming) fprintf(stderr, "Could not find ppid!\n");
+ tmp_list = malloc(sizeof(sort_node));
+ tmp_list->reverse = 0;
+ tmp_list->typecode = '?'; /* what was this for? */
+ tmp_list->sr = incoming->sr;
+ tmp_list->need = incoming->need;
+ tmp_list->next = sort_list;
+ sort_list = tmp_list;
+}
+
+/* we rely on the POSIX requirement for zeroed memory */
+//static proc_t *processes[98*1024]; // FIXME
+static proc_t **processes;
+
+/***** compare function for qsort */
+static int compare_two_procs(const void *a, const void *b){
+ sort_node *tmp_list = sort_list;
+ while(tmp_list){
+ int result;
+ result = (*tmp_list->sr)(*(const proc_t *const*)a, *(const proc_t *const*)b);
+ if(result) return (tmp_list->reverse) ? -result : result;
+ tmp_list = tmp_list->next;
+ }
+ return 0; /* no conclusion */
+}
+
+/***** show pre-sorted array of process pointers */
+static void show_proc_array(PROCTAB *restrict ptp, int n){
+ proc_t **p = processes;
+ while(n--){
+ if(thread_flags & TF_show_proc) show_one_proc(*p, proc_format_list);
+ if(thread_flags & TF_show_task){
+ proc_t buf2;
+ // must still have the process allocated
+ while(readtask(ptp,*p,&buf2)) show_one_proc(&buf2, task_format_list);
+ // must not attempt to free cmdline and environ
+ }
+ /* no point freeing any of this -- won't need more mem */
+// if((*p)->cmdline) free((void*)*(*p)->cmdline);
+// if((*p)->environ) free((void*)*(*p)->environ);
+// memset(*p, '%', sizeof(proc_t)); /* debug */
+// free(*p);
+ p++;
+ }
+}
+
+/***** show tree */
+/* this needs some optimization work */
+#define ADOPTED(x) 1
+static void show_tree(const int self, const int n, const int level, const int have_sibling){
+ int i = 0;
+ if(level){
+ /* add prefix of "+" or "L" */
+ if(have_sibling) forest_prefix[level-1] = '+';
+ else forest_prefix[level-1] = 'L';
+ forest_prefix[level] = '\0';
+ }
+ show_one_proc(processes[self],format_list); /* first show self */
+ /* no point freeing any of this -- won't need more mem */
+// if(processes[self]->cmdline) free((void*)*processes[self]->cmdline);
+// if(processes[self]->environ) free((void*)*processes[self]->environ);
+ for(;;){ /* look for children */
+ if(i >= n) return; /* no children */
+ if(processes[i]->ppid == processes[self]->XXXID) break;
+ i++;
+ }
+ if(level){
+ /* change our prefix to "|" or " " for the children */
+ if(have_sibling) forest_prefix[level-1] = '|';
+ else forest_prefix[level-1] = ' ';
+ forest_prefix[level] = '\0';
+ }
+ for(;;){
+ int self_pid;
+ int more_children = 1;
+ if(i >= n) break; /* over the edge */
+ self_pid=processes[self]->XXXID;
+ if(i+1 >= n)
+ more_children = 0;
+ else
+ if(processes[i+1]->ppid != self_pid) more_children = 0;
+ if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u')
+ show_tree(i++, n, level, more_children);
+ else
+ show_tree(i++, n, level+1, more_children);
+ if(!more_children) break;
+ }
+ /* chop prefix that children added -- do we need this? */
+ forest_prefix[level] = '\0';
+// memset(processes[self], '$', sizeof(proc_t)); /* debug */
+}
+
+/***** show forest */
+static void show_forest(const int n){
+ int i = n;
+ int j;
+ while(i--){ /* cover whole array looking for trees */
+ j = n;
+ while(j--){ /* search for parent: if none, i is a tree! */
+ if(processes[j]->XXXID == processes[i]->ppid) goto not_root;
+ }
+ show_tree(i,n,0,0);
+not_root:
+ ;
+ }
+ /* don't free the array because it takes time and ps will exit anyway */
+}
+
+static int want_this_proc_nop(proc_t *dummy){
+ (void)dummy;
+ return 1;
+}
+
+/***** sorted or forest */
+static void fancy_spew(void){
+ proc_data_t *pd = NULL;
+ PROCTAB *restrict ptp;
+ int n = 0; /* number of processes & index into array */
+
+ ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
+ if(!ptp) {
+ fprintf(stderr, "Error: can not access /proc.\n");
+ exit(1);
+ }
+
+ if(thread_flags & TF_loose_tasks){
+ pd = readproctab2(want_this_proc_nop, want_this_proc_pcpu, ptp);
+ }else{
+ pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp);
+ }
+ n = pd->n;
+ processes = pd->tab;
+
+ if(!n) return; /* no processes */
+ if(forest_type) prep_forest_sort();
+ qsort(processes, n, sizeof(proc_t*), compare_two_procs);
+ if(forest_type) show_forest(n);
+ else show_proc_array(ptp,n);
+ int i;
+ for (i=0; i<n; i++)
+ if (processes[i]->nsupgid>0 && processes[i]->supgid) free(processes[i]->supgid);
+ if (ptp->flags & PROC_FILLSUPGRP)
+ for (i=0; i<n; i++)
+ if (processes[i]->nsupgid>0 && processes[i]->supgrp) freesupgrp(processes[i]);
+ closeproc(ptp);
+}
+
+
+/***** no comment */
+int main(int argc, char *argv[]){
+#if (__GNU_LIBRARY__ >= 6)
+ setlocale (LC_CTYPE, "");
+#endif
+
+#ifdef DEBUG
+ init_stack_trace(argv[0]);
+#else
+ do {
+ struct sigaction sa;
+ int i = 32;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler;
+ sigfillset(&sa.sa_mask);
+ while(i--) switch(i){
+ default:
+ sigaction(i,&sa,NULL);
+ case 0:
+ case SIGINT: /* ^C */
+ case SIGTSTP: /* ^Z */
+ case SIGTTOU: /* see stty(1) man page */
+ case SIGQUIT: /* ^\ */
+ case SIGPROF: /* profiling */
+ case SIGKILL: /* can not catch */
+ case SIGSTOP: /* can not catch */
+ case SIGWINCH: /* don't care if window size changes */
+ ;
+ }
+ } while (0);
+#endif
+
+ reset_global(); /* must be before parser */
+ arg_parse(argc,argv);
+
+/* arg_show(); */
+ trace("screen is %ux%u\n",screen_cols,screen_rows);
+/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */
+ trace("======= ps output follows =======\n");
+
+ init_output(); /* must be between parser and output */
+
+ lists_and_needs();
+
+ if(forest_type || sort_list) fancy_spew(); /* sort or forest */
+ else simple_spew(); /* no sort, no forest */
+ show_one_proc((proc_t *)-1,format_list); /* no output yet? */
+ return 0;
+}
diff --git a/smartt-top/ps/global.c b/smartt-top/ps/global.c
new file mode 100644
index 0000000..0b3bc5b
--- /dev/null
+++ b/smartt-top/ps/global.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <stdlib.h>
+#include <termios.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#include "common.h"
+
+#include <sys/sysmacros.h>
+#include "../proc/wchan.h"
+#include "../proc/version.h"
+#include "../proc/sysinfo.h"
+
+
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__ -1
+#endif
+#ifndef __GLIBC__
+#define __GLIBC__ -1
+#endif
+#ifndef __GLIBC_MINOR__
+#define __GLIBC_MINOR__ -1
+#endif
+
+
+static const char * saved_personality_text = "You found a bug!";
+
+int all_processes = -1;
+const char *bsd_j_format = (const char *)0xdeadbeef;
+const char *bsd_l_format = (const char *)0xdeadbeef;
+const char *bsd_s_format = (const char *)0xdeadbeef;
+const char *bsd_u_format = (const char *)0xdeadbeef;
+const char *bsd_v_format = (const char *)0xdeadbeef;
+int bsd_c_option = -1;
+int bsd_e_option = -1;
+uid_t cached_euid = -1;
+dev_t cached_tty = -1;
+char forest_prefix[4 * 32*1024 + 100]; // FIXME
+int forest_type = -1;
+unsigned format_flags = 0xffffffff; /* -l -f l u s -j... */
+format_node *format_list = (format_node *)0xdeadbeef; /* digested formatting options */
+unsigned format_modifiers = 0xffffffff; /* -c -j -y -P -L... */
+int header_gap = -1;
+int header_type = -1;
+int include_dead_children = -1;
+int lines_to_next_header = -1;
+const char *namelist_file = (const char *)0xdeadbeef;
+int negate_selection = -1;
+int running_only = -1;
+int page_size = -1; // "int" for math reasons?
+unsigned personality = 0xffffffff;
+int prefer_bsd_defaults = -1;
+int screen_cols = -1;
+int screen_rows = -1;
+unsigned long seconds_since_boot = -1;
+selection_node *selection_list = (selection_node *)0xdeadbeef;
+unsigned simple_select = 0xffffffff;
+sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */
+const char *sysv_f_format = (const char *)0xdeadbeef;
+const char *sysv_fl_format = (const char *)0xdeadbeef;
+const char *sysv_j_format = (const char *)0xdeadbeef;
+const char *sysv_l_format = (const char *)0xdeadbeef;
+unsigned thread_flags = 0xffffffff;
+int unix_f_option = -1;
+int user_is_number = -1;
+int wchan_is_number = -1;
+
+
+static void reset_selection_list(void){
+ selection_node *old;
+ selection_node *walk = selection_list;
+ if(selection_list == (selection_node *)0xdeadbeef){
+ selection_list = NULL;
+ return;
+ }
+ while(walk){
+ old = walk;
+ walk = old->next;
+ free(old->u);
+ free(old);
+ }
+ selection_list = NULL;
+}
+
+// The rules:
+// 1. Defaults are implementation-specific. (ioctl,termcap,guess)
+// 2. COLUMNS and LINES override the defaults. (standards compliance)
+// 3. Command line options override everything else.
+// 4. Actual output may be more if the above is too narrow.
+//
+// SysV tends to spew semi-wide output in all cases. The args
+// will be limited to 64 or 80 characters, without regard to
+// screen size. So lines of 120 to 160 chars are normal.
+// Tough luck if you want more or less than that! HP-UX has a
+// new "-x" option for 1024-char args in place of comm that
+// we'll implement at some point.
+//
+// BSD tends to make a good effort, then fall back to 80 cols.
+// Use "ww" to get infinity. This is nicer for "ps | less"
+// and "watch ps". It can run faster too.
+static void set_screen_size(void){
+ struct winsize ws;
+ char *columns; /* Unix98 environment variable */
+ char *lines; /* Unix98 environment variable */
+
+ do{
+ int fd;
+ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ if(ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ if(ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ fd = open("/dev/tty", O_NOCTTY|O_NONBLOCK|O_RDONLY);
+ if(fd != -1){
+ int ret = ioctl(fd, TIOCGWINSZ, &ws);
+ close(fd);
+ if(ret != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ }
+ // TODO: ought to do tgetnum("co") and tgetnum("li") here
+ ws.ws_col = 80;
+ ws.ws_row = 24;
+ }while(0);
+ screen_cols = ws.ws_col; // hmmm, NetBSD subtracts 1
+ screen_rows = ws.ws_row;
+
+ // TODO: delete this line
+ if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE;
+
+ columns = getenv("COLUMNS");
+ if(columns && *columns){
+ long t;
+ char *endptr;
+ t = strtol(columns, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t;
+ }
+
+ lines = getenv("LINES");
+ if(lines && *lines){
+ long t;
+ char *endptr;
+ t = strtol(lines, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t;
+ }
+
+ if((screen_cols<9) || (screen_rows<2))
+ fprintf(stderr,"Your %dx%d screen size is bogus. Expect trouble.\n",
+ screen_cols, screen_rows
+ );
+}
+
+/**************** personality control **************/
+
+typedef struct personality_table_struct {
+ const char *name; /* personality name */
+ const void *jump; /* See gcc extension info. :-) */
+} personality_table_struct;
+
+static int compare_personality_table_structs(const void *a, const void *b){
+ return strcasecmp(((const personality_table_struct*)a)->name,((const personality_table_struct*)b)->name);
+}
+
+static const char *set_personality(void){
+ const char *s;
+ size_t sl;
+ char buf[16];
+ personality_table_struct findme = { buf, NULL};
+ personality_table_struct *found;
+ static const personality_table_struct personality_table[] = {
+ {"390", &&case_390},
+ {"aix", &&case_aix},
+ {"bsd", &&case_bsd},
+ {"compaq", &&case_compaq},
+ {"debian", &&case_debian},
+ {"default", &&case_default},
+ {"digital", &&case_digital},
+ {"gnu", &&case_gnu},
+ {"hp", &&case_hp},
+ {"hpux", &&case_hpux},
+ {"irix", &&case_irix},
+ {"linux", &&case_linux},
+ {"old", &&case_old},
+ {"os390", &&case_os390},
+ {"posix", &&case_posix},
+ {"s390", &&case_s390},
+ {"sco", &&case_sco},
+ {"sgi", &&case_sgi},
+ {"solaris2", &&case_solaris2},
+ {"sunos4", &&case_sunos4},
+ {"svr4", &&case_svr4},
+ {"sysv", &&case_sysv},
+ {"tru64", &&case_tru64},
+ {"unix", &&case_unix},
+ {"unix95", &&case_unix95},
+ {"unix98", &&case_unix98},
+ {"unknown", &&case_unknown}
+ };
+ const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct);
+
+ personality = 0;
+ prefer_bsd_defaults = 0;
+
+ bsd_j_format = "OL_j";
+ bsd_l_format = "OL_l";
+ bsd_s_format = "OL_s";
+ bsd_u_format = "OL_u";
+ bsd_v_format = "OL_v";
+
+ /* When these are NULL, the code does SysV output modifier logic */
+ sysv_f_format = NULL;
+ sysv_fl_format = NULL;
+ sysv_j_format = NULL;
+ sysv_l_format = NULL;
+
+ s = getenv("PS_PERSONALITY");
+ if(!s || !*s) s = getenv("CMD_ENV");
+ if(!s || !*s) s="unknown"; /* "Do The Right Thing[tm]" */
+ if(getenv("I_WANT_A_BROKEN_PS")) s="old";
+ sl = strlen(s);
+ if(sl > 15) return "Environment specified an unknown personality.";
+ strncpy(buf, s, sl);
+ buf[sl] = '\0';
+ saved_personality_text = strdup(buf);
+
+ found = bsearch(&findme, personality_table, personality_table_count,
+ sizeof(personality_table_struct), compare_personality_table_structs
+ );
+
+ if(!found) return "Environment specified an unknown personality.";
+
+ goto *(found->jump); /* See gcc extension info. :-) */
+
+ case_bsd:
+ personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m;
+ prefer_bsd_defaults = 1;
+ bsd_j_format = "FB_j";
+ bsd_l_format = "FB_l";
+ /* bsd_s_format not used */
+ bsd_u_format = "FB_u";
+ bsd_v_format = "FB_v";
+ return NULL;
+
+ case_old:
+ personality = PER_FORCE_BSD | PER_OLD_m;
+ prefer_bsd_defaults = 1;
+ return NULL;
+
+ case_debian: /* Toss this? They don't seem to care much. */
+ case_gnu:
+ personality = PER_GOOD_o | PER_OLD_m;
+ prefer_bsd_defaults = 1;
+ sysv_f_format = "RD_f";
+ /* sysv_fl_format = "RD_fl"; */ /* old Debian ps can't do this! */
+ sysv_j_format = "RD_j";
+ sysv_l_format = "RD_l";
+ return NULL;
+
+ case_linux:
+ personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER;
+ return NULL;
+
+ case_default: /* use defaults for ps, ignoring other environment variables */
+ return NULL;
+
+ case_unknown: /* defaults, but also check inferior environment variables */
+ if(
+ getenv("UNIX95") /* Irix */
+ || getenv("POSIXLY_CORRECT") /* most gnu stuff */
+ || (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */
+ ) personality = PER_BROKEN_o;
+ return NULL;
+
+ case_aix:
+ bsd_j_format = "FB_j";
+ bsd_l_format = "FB_l";
+ /* bsd_s_format not used */
+ bsd_u_format = "FB_u";
+ bsd_v_format = "FB_v";
+ return NULL;
+
+ case_tru64:
+ case_compaq:
+ case_digital:
+ // no PER_NO_DEFAULT_g even though man page claims it
+ // Reality: the g is a NOP
+ personality = PER_GOOD_o | PER_BSD_h;
+ prefer_bsd_defaults = 1;
+ sysv_f_format = "F5FMT";
+ sysv_fl_format = "FL5FMT";
+ sysv_j_format = "JFMT";
+ sysv_l_format = "L5FMT";
+ bsd_j_format = "JFMT";
+ bsd_l_format = "LFMT";
+ bsd_s_format = "SFMT";
+ bsd_u_format = "UFMT";
+ bsd_v_format = "VFMT";
+ return NULL;
+
+ case_sunos4:
+ personality = PER_NO_DEFAULT_g;
+ prefer_bsd_defaults = 1;
+ bsd_j_format = "FB_j";
+ bsd_l_format = "FB_l";
+ /* bsd_s_format not used */
+ bsd_u_format = "FB_u";
+ bsd_v_format = "FB_v";
+ return NULL;
+
+ case_irix:
+ case_sgi:
+ s = getenv("_XPG");
+ if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o;
+ else personality = PER_IRIX_l;
+ return NULL;
+
+ case_os390: /* IBM's OS/390 OpenEdition on the S/390 mainframe */
+ case_s390:
+ case_390:
+ sysv_j_format = "J390"; /* don't know what -jl and -jf do */
+ return NULL;
+
+ case_hp:
+ case_hpux:
+ personality = PER_BROKEN_o | PER_HPUX_x;
+ return NULL;
+
+ case_svr4:
+ case_sysv:
+ case_sco:
+ personality = PER_BROKEN_o | PER_SVR4_x;
+ return NULL;
+
+ case_posix:
+ case_solaris2:
+ case_unix95:
+ case_unix98:
+ case_unix:
+ personality = PER_BROKEN_o;
+ return NULL;
+}
+
+
+/************ Call this to reinitialize everything ***************/
+void reset_global(void){
+ static proc_t p;
+ reset_selection_list();
+ look_up_our_self(&p);
+ set_screen_size();
+ set_personality();
+
+ all_processes = 0;
+ bsd_c_option = 0;
+ bsd_e_option = 0;
+ cached_euid = geteuid();
+ cached_tty = p.tty;
+/* forest_prefix must be all zero because of POSIX */
+ forest_type = 0;
+ format_flags = 0; /* -l -f l u s -j... */
+ format_list = NULL; /* digested formatting options */
+ format_modifiers = 0; /* -c -j -y -P -L... */
+ header_gap = -1; /* send lines_to_next_header to -infinity */
+ header_type = HEAD_SINGLE;
+ include_dead_children = 0;
+ lines_to_next_header = 1;
+ namelist_file = NULL;
+ negate_selection = 0;
+ page_size = getpagesize();
+ running_only = 0;
+ seconds_since_boot = uptime(0,0);
+ selection_list = NULL;
+ simple_select = 0;
+ sort_list = NULL;
+ thread_flags = 0;
+ unix_f_option = 0;
+ user_is_number = 0;
+ wchan_is_number = 0;
+}
+
+static const char archdefs[] =
+#ifdef __alpha__
+" alpha"
+#endif
+#ifdef __arm__
+" arm"
+#endif
+#ifdef __hppa__
+" hppa"
+#endif
+#ifdef __i386__
+" i386"
+#endif
+#ifdef __ia64__
+" ia64"
+#endif
+#ifdef __mc68000__
+" mc68000"
+#endif
+#ifdef __mips64__
+" mips64"
+#endif
+#ifdef __mips__
+" mips"
+#endif
+#ifdef __powerpc__
+" powerpc"
+#endif
+#ifdef __sh3__
+" sh3"
+#endif
+#ifdef __sh__
+" sh"
+#endif
+#ifdef __sparc__
+" sparc"
+#endif
+#ifdef __sparc_v9__
+" sparc_v9"
+#endif
+#ifdef __x86_64__
+" x86_64"
+#endif
+"";
+
+/*********** spew variables ***********/
+void self_info(void){
+ fprintf(stderr,
+ "BSD j %s\n"
+ "BSD l %s\n"
+ "BSD s %s\n"
+ "BSD u %s\n"
+ "BSD v %s\n"
+ "SysV -f %s\n"
+ "SysV -fl %s\n"
+ "SysV -j %s\n"
+ "SysV -l %s\n"
+ "\n",
+ bsd_j_format ? bsd_j_format : "(none)",
+ bsd_l_format ? bsd_l_format : "(none)",
+ bsd_s_format ? bsd_s_format : "(none)",
+ bsd_u_format ? bsd_u_format : "(none)",
+ bsd_v_format ? bsd_v_format : "(none)",
+ sysv_f_format ? sysv_f_format : "(none)",
+ sysv_fl_format ? sysv_fl_format : "(none)",
+ sysv_j_format ? sysv_j_format : "(none)",
+ sysv_l_format ? sysv_l_format : "(none)"
+ );
+
+ display_version();
+ fprintf(stderr, "Linux version %d.%d.%d\n",
+ LINUX_VERSION_MAJOR(linux_version_code),
+ LINUX_VERSION_MINOR(linux_version_code),
+ LINUX_VERSION_PATCH(linux_version_code)
+ );
+ /* __libc_print_version(); */ /* how can we get the run-time version? */
+ fprintf(stderr, "Compiled with: glibc %d.%d, gcc %d.%d\n\n",
+ __GLIBC__, __GLIBC_MINOR__, __GNUC__, __GNUC_MINOR__
+ );
+
+ fprintf(stderr,
+ "header_gap=%d lines_to_next_header=%d\n"
+ "screen_cols=%d screen_rows=%d\n"
+ "\n",
+ header_gap, lines_to_next_header,
+ screen_cols, screen_rows
+ );
+
+ fprintf(stderr,
+ "personality=0x%08x (from \"%s\")\n"
+ "EUID=%d TTY=%d,%d Hertz=%Ld page_size=%d\n",
+ personality, saved_personality_text,
+ cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz,
+ (int)(page_size)
+ );
+
+ fprintf(stderr,
+ "sizeof(proc_t)=%d sizeof(long)=%d sizeof(KLONG)=%d\n",
+ (int)sizeof(proc_t), (int)sizeof(long), (int)sizeof(KLONG)
+ );
+
+ fprintf(stderr, "archdefs:%s\n", archdefs);
+
+ open_psdb(namelist_file);
+ fprintf(stderr,"namelist_file=\"%s\"\n",namelist_file?namelist_file:"<no System.map file>");
+}
diff --git a/smartt-top/ps/help.c b/smartt-top/ps/help.c
new file mode 100644
index 0000000..2f39a9d
--- /dev/null
+++ b/smartt-top/ps/help.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1998-2004 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/*
+ * The help message must not become longer, because it must fit
+ * on an 80x24 screen _with_ the error message and command prompt.
+ */
+
+const char *help_message =
+"********* simple selection ********* ********* selection by list *********\n"
+"-A all processes -C by command name\n"
+"-N negate selection -G by real group ID (supports names)\n"
+"-a all w/ tty except session leaders -U by real user ID (supports names)\n"
+"-d all except session leaders -g by session OR by effective group name\n"
+"-e all processes -p by process ID\n"
+"T all processes on this terminal -s processes in the sessions given\n"
+"a all w/ tty, including other users -t by tty\n"
+"g OBSOLETE -- DO NOT USE -u by effective user ID (supports names)\n"
+"r only running processes U processes for specified users\n"
+"x processes w/o controlling ttys t by tty\n"
+"*********** output format ********** *********** long options ***********\n"
+"-o,o user-defined -f full --Group --User --pid --cols --ppid\n"
+"-j,j job control s signal --group --user --sid --rows --info\n"
+"-O,O preloaded -o v virtual memory --cumulative --format --deselect\n"
+"-l,l long u user-oriented --sort --tty --forest --version\n"
+"-F extra full X registers --heading --no-heading --context\n"
+" ********* misc options *********\n"
+"-V,V show version L list format codes f ASCII art forest\n"
+"-m,m,-L,-T,H threads S children in sum -y change -l format\n"
+"-M,Z security data c true command name -c scheduling class\n"
+"-w,w wide output n numeric WCHAN,UID -H process hierarchy\n"
+;
+
+
+
+/* Missing:
+ *
+ * -P e k
+ *
+ */
diff --git a/smartt-top/ps/it b/smartt-top/ps/it
new file mode 100644
index 0000000..07fd6dc
--- /dev/null
+++ b/smartt-top/ps/it
@@ -0,0 +1,35 @@
+From ddainese@dsi.unive.it Sun Apr 18 14:12:27 1999
+
+here is a first translation of the text:
+---------------------------------------------------------------------
+const char *help_message =
+"****** seleziona i processi ******* * seleziona una lista specificando: *\n"
+"-A tutti -C il nome del comando\n"
+"-N nega la selezione -G il real group ID (supporta i nomi)\n"
+"-a con tty, tranne i session leader -U il real user ID (supporta i nomi)\n"
+"-d tutti, tranne i session leader -g il session leader OPPURE il gruppo\n"
+"-e tutti -p l'ID del processo\n"
+"T su questo terminale -s la sessione\n"
+"a con tty, di tutti gli utenti -t il tty\n"
+"g tutti, anche i leader di gruppo -u l'effective user ID (supporta i nomi)\n"
+"r in stato running U una lista di utenti\n"
+"x senza tty t il tty\n"
+"******** formato dell'output ****** ********** opzioni lunghe **********\n"
+"-o,o definito dall'utente --Group --User --pid --cols\n"
+"-j,j job s segnali --group --user --sid --rows\n"
+"-O,O -o preimpostato v memoria virtuale --cumulative --format --deselect\n"
+"-l,l lungo u utenti --sort --tty --forest --version\n"
+"-f completo X registri --heading --no-heading\n"
+" ******** opzioni varie *********\n"
+"-V,V versione L codici di formato f foresta di ASCII\n"
+"-m,m vista ad albero S figli in sum -y cambia il formato -l\n"
+"-n,N namelist file c nome reale del comando n WCHAN,UID numerici\n"
+"-w,w output ampio e mostra l'environment -H gerarchia dei processi\n"
+;
+---------------------------------------------------------------------
+
+Unfortunately it isn't really understandable for a newbie, because
+there is too little space for a good translation; to make it more
+meaningful, I would need about an entire line for every option, thus
+if you really want the help text stays under 22 lines, it must
+contains only 22 options. What do you think about it?
diff --git a/smartt-top/ps/module.mk b/smartt-top/ps/module.mk
new file mode 100755
index 0000000..2902a3a
--- /dev/null
+++ b/smartt-top/ps/module.mk
@@ -0,0 +1,40 @@
+# This file gets included into the main Makefile, in the top directory.
+
+INSTALL += $(bin)ps $(man1)ps.1
+
+# files to remove
+CLEAN += ps/ps ps/debug
+
+# a directory for cleaning
+DIRS += ps/
+
+# a file to create
+ALL += ps/ps
+
+PS_C := display global help output parser select sortformat
+PSNAMES := $(addprefix ps/,$(PS_C))
+PSOBJ := $(addsuffix .o,$(PSNAMES))
+PSSRC := $(addsuffix .c,$(PSNAMES))
+
+PS_X := COPYING HACKING TRANSLATION common.h module.mk it p ps.1 regression
+TARFILES += $(PSSRC) $(addprefix ps/,$(PS_X))
+
+ps/ps: $(PSOBJ) $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ $(ldl)
+
+# This just adds the stacktrace code
+ps/debug: $(PSOBJ) stacktrace.o $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ -lefence $(ldl)
+
+$(PSOBJ): %.o: %.c ps/common.h $(LIBPROC)
+ $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< -o $@
+
+ps/stacktrace.o: ps/stacktrace.c
+
+
+$(bin)ps: ps/ps
+ $(install) --mode a=rx $< $@
+
+$(man1)ps.1 : ps/ps.1
+ $(install) --mode a=r $< $@
+ -rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz
diff --git a/smartt-top/ps/output.c b/smartt-top/ps/output.c
new file mode 100644
index 0000000..f9a29ce
--- /dev/null
+++ b/smartt-top/ps/output.c
@@ -0,0 +1,2054 @@
+/*
+ * Copyright 1999-2004 by Albert Cahalan; all rights reserved.
+ *
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/*
+ * This file is really gross, and I know it. I looked into several
+ * alternate ways to deal with the mess, and they were all ugly.
+ *
+ * FreeBSD has a fancy hack using offsets into a struct -- that
+ * saves code but it is _really_ gross. See the PO macro below.
+ *
+ * We could have a second column width for wide output format.
+ * For example, Digital prints the real-time signals.
+ */
+
+
+/*
+ * Data table idea:
+ *
+ * table 1 maps aix to specifier
+ * table 2 maps shortsort to specifier
+ * table 3 maps macro to specifiers
+ * table 4 maps specifier to title,datatype,offset,vendor,helptext
+ * table 5 maps datatype to justification,width,widewidth,sorting,printing
+ *
+ * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
+ * It must be enough to determine printing and sorting.
+ *
+ * After the tables, increase width as needed to fit the header.
+ *
+ * Table 5 could go in a file with the output functions.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "../proc/wchan.h"
+#include "../proc/procps.h"
+#include "../proc/devname.h"
+#include "../proc/escape.h"
+#include "common.h"
+
+/* TODO:
+ * Stop assuming system time is local time.
+ */
+
+#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
+
+static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
+static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */
+
+
+
+static int wide_signals; /* true if we have room */
+
+static unsigned long seconds_since_1970;
+static unsigned long time_of_boot;
+static unsigned long page_shift;
+
+
+/*************************************************************************/
+/************ Lots of sort functions, starting with the NOP **************/
+
+static int sr_nop(const proc_t* a, const proc_t* b){
+ (void)a;(void)b; /* shut up gcc */
+ return 0;
+}
+
+#define CMP_STR(NAME) \
+static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \
+ return strcmp(P->NAME, Q->NAME); \
+}
+
+#define CMP_INT(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+ if (P->NAME < Q->NAME) return -1; \
+ if (P->NAME > Q->NAME) return 1; \
+ return 0; \
+}
+
+/* fast version, for values which either:
+ * a. differ by no more than 0x7fffffff
+ * b. only need to be grouped same w/ same
+ */
+#define CMP_SMALL(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+ return (int)(P->NAME) - (int)(Q->NAME); \
+}
+
+#define cook_time(P) (P->utime + P->stime) / Hertz
+
+#define cook_etime(P) seconds_since_boot - (unsigned long)(P->start_time / Hertz)
+
+#define CMP_COOKED_TIME(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+ unsigned long p_time,q_time; \
+ p_time=cook_ ##NAME (P); \
+ q_time=cook_ ##NAME (Q); \
+ if (p_time < q_time) return -1; \
+ if (p_time > q_time) return 1; \
+ return 0; \
+}
+
+CMP_INT(rtprio)
+CMP_SMALL(sched)
+CMP_INT(cutime)
+CMP_INT(cstime)
+CMP_SMALL(priority) /* nice */
+CMP_SMALL(nlwp)
+CMP_SMALL(nice) /* priority */
+CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */
+CMP_INT(alarm)
+CMP_INT(size) /* total pages */ /* vm_size, vsize */
+CMP_INT(resident) /* resident pages */ /* vm_rss, rss */
+CMP_INT(share) /* shared pages */
+CMP_INT(trs) /* executable pages */
+CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */
+CMP_INT(drs) /* other pages (assumed data?) */
+CMP_INT(dt) /* dirty pages */
+
+CMP_INT(vm_size) /* kB VM */ /* size, vsize */
+CMP_INT(vm_lock) /* kB locked */
+CMP_INT(vm_rss) /* kB rss */ /* rss, resident */
+CMP_INT(vm_data) /* kB "data" == data-stack */
+CMP_INT(vm_stack) /* kB stack */
+CMP_INT(vm_exe) /* kB "exec" == exec-lib */
+CMP_INT(vm_lib) /* kB "libraries" */
+CMP_INT(vsize) /* pages VM */ /* size, vm_size */
+CMP_INT(rss_rlim)
+CMP_SMALL(flags)
+CMP_INT(min_flt)
+CMP_INT(maj_flt)
+CMP_INT(cmin_flt)
+CMP_INT(cmaj_flt)
+CMP_INT(utime)
+CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */
+CMP_INT(start_code)
+CMP_INT(end_code)
+CMP_INT(start_stack)
+CMP_INT(kstk_esp)
+CMP_INT(kstk_eip)
+CMP_INT(start_time)
+CMP_INT(wchan)
+
+/* CMP_STR(*environ) */
+/* CMP_STR(*cmdline) */
+
+CMP_STR(ruser)
+CMP_STR(euser)
+CMP_STR(suser)
+CMP_STR(fuser)
+CMP_STR(rgroup)
+CMP_STR(egroup)
+CMP_STR(sgroup)
+CMP_STR(fgroup)
+CMP_STR(cmd)
+/* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */
+
+CMP_INT(ruid)
+CMP_INT(rgid)
+CMP_INT(euid)
+CMP_INT(egid)
+CMP_INT(suid)
+CMP_INT(sgid)
+CMP_INT(fuid)
+CMP_INT(fgid)
+CMP_SMALL(tid)
+CMP_SMALL(tgid)
+CMP_SMALL(ppid)
+CMP_SMALL(pgrp)
+CMP_SMALL(session)
+CMP_INT(tty)
+CMP_SMALL(tpgid)
+
+CMP_SMALL(pcpu)
+
+CMP_SMALL(state)
+
+CMP_COOKED_TIME(time)
+CMP_COOKED_TIME(etime)
+
+/* approximation to: kB of address space that could end up in swap */
+static int sr_swapable(const proc_t* P, const proc_t* Q) {
+ unsigned long p_swapable = P->vm_data + P->vm_stack;
+ unsigned long q_swapable = Q->vm_data + Q->vm_stack;
+ if (p_swapable < q_swapable) return -1;
+ if (p_swapable > q_swapable) return 1;
+ return 0;
+}
+
+static int sr_supgid(const proc_t* P, const proc_t* Q){
+ int i;
+ for (i = 0; i < INT_MAX; i++){
+ if (P->nsupgid == i){
+ if (Q->nsupgid == i) return 0;
+ else return -1;
+ }
+ if (Q->nsupgid == i) return 1;
+ if (P->supgid[i] != Q->supgid[i]) return P->supgid[i] - Q->supgid[i];
+ }
+ return 0;
+}
+
+static int sr_supgrp(const proc_t* P, const proc_t* Q){
+ int i;
+ for (i = 0; i < INT_MAX; i++){
+ if (P->nsupgid == i){
+ if (Q->nsupgid == i) return 0;
+ else return -1;
+ }
+ if (Q->nsupgid == i) return 1;
+ int cmp = strncmp(P->supgrp[i],Q->supgrp[i],P_G_SZ);
+ if (cmp != 0) return cmp;
+ }
+ return 0;
+}
+
+/***************************************************************************/
+/************ Lots of format functions, starting with the NOP **************/
+
+// so popular it can't be "static"
+int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%c", '-');
+}
+
+
+/********* Unix 98 ************/
+
+/***
+
+Only comm and args are allowed to contain blank characters; all others are
+not. Any implementation-dependent variables will be specified in the system
+documentation along with the default header and indicating if the field
+may contain blank characters.
+
+Some headers do not have a standardized specifier!
+
+%CPU pcpu The % of cpu time used recently, with unspecified "recently".
+ADDR The address of the process.
+C Processor utilisation for scheduling.
+CMD The command name, or everything with -f.
+COMMAND args Command + args. May chop as desired. May use either version.
+COMMAND comm argv[0]
+ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss
+F Flags (octal and additive)
+GROUP group Effective group ID, prefer text over decimal.
+NI nice Decimal system scheduling priority, see nice(1).
+PGID pgid The decimal value of the process group ID.
+PID pid Decimal PID.
+PPID ppid Decimal PID.
+PRI Priority. Higher numbers mean lower priority.
+RGROUP rgroup Real group ID, prefer text over decimal.
+RUSER ruser Real user ID, prefer text over decimal.
+S The state of the process.
+STIME Starting time of the process.
+SZ The size in blocks of the core image of the process.
+TIME time Cumulative CPU time. [dd-]hh:mm:ss
+TT tty Name of tty in format used by who(1).
+TTY The controlling terminal for the process.
+UID UID, or name when -f
+USER user Effective user ID, prefer text over decimal.
+VSZ vsz Virtual memory size in decimal kB.
+WCHAN Where waiting/sleeping or blank if running.
+
+The nice value is used to compute the priority.
+
+For some undefined ones, Digital does:
+
+F flag Process flags -- but in hex!
+PRI pri Process priority
+S state Symbolic process status
+TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??"
+UID uid Process user ID (effective UID)
+WCHAN wchan Address of event on which a
+
+For some undefined ones, Sun does:
+
+ADDR addr memory address of the process
+C c Processor utilization for scheduling (obsolete).
+CMD
+F f
+S s state: OSRZT
+STIME start time, printed w/o blanks. If 24h old, months & days
+SZ size (in pages) of the swappable process's image in main memory
+TTY
+UID uid
+WCHAN wchan
+
+For some undefined ones, SCO does:
+ADDR addr Virtual address of the process' entry in the process table.
+SZ swappable size in kB of the virtual data and stack
+STIME stime hms or md time format
+***/
+
+/* Source & destination are known. Return bytes or screen characters? */
+static int forest_helper(char *restrict const outbuf){
+ char *p = forest_prefix;
+ char *q = outbuf;
+ int rightward=max_rightward;
+ if(!*p) return 0;
+ /* Arrrgh! somebody defined unix as 1 */
+ if(forest_type == 'u') goto unixy;
+ while(*p){
+ switch(*p){
+ case ' ': strcpy(q, " "); break;
+ case 'L': strcpy(q, " \\_ "); break;
+ case '+': strcpy(q, " \\_ "); break;
+ case '|': strcpy(q, " | "); break;
+ case '\0': return q-outbuf; /* redundant & not used */
+ }
+ if (rightward-4 < 0) {
+ *(q+rightward)='\0';
+ return max_rightward;
+ }
+ q += 4;
+ rightward -= 4;
+ p++;
+ }
+ return q-outbuf; /* gcc likes this here */
+unixy:
+ while(*p){
+ switch(*p){
+ case ' ': strcpy(q, " "); break;
+ case 'L': strcpy(q, " "); break;
+ case '+': strcpy(q, " "); break;
+ case '|': strcpy(q, " "); break;
+ case '\0': return q-outbuf; /* redundant & not used */
+ }
+ if (rightward-2 < 0) {
+ *(q+rightward)='\0';
+ return max_rightward;
+ }
+ q += 2;
+ rightward -= 2;
+ p++;
+ }
+ return q-outbuf; /* gcc likes this here */
+}
+
+
+/* XPG4-UNIX, according to Digital:
+The "args" and "command" specifiers show what was passed to the command.
+Modifications to the arguments are not shown.
+*/
+
+/*
+ * pp->cmd short accounting name (comm & ucomm)
+ * pp->cmdline long name with args (args & command)
+ * pp->environ environment
+ */
+
+// FIXME: some of these may hit the guard page in forest mode
+
+/* "command" is the same thing: long unless c */
+static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ unsigned flags;
+ int rightward=max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if(bsd_c_option) flags = ESC_DEFUNCT;
+ else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
+
+ if(bsd_e_option && rightward>1){
+ const char **env = (const char**)pp->environ;
+ if(env && *env){
+ *endp++ = ' ';
+ rightward--;
+ endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
+ }
+ }
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+
+static int pr_cgroup(char *restrict const outbuf,const proc_t *restrict const pp) {
+ if(pp->cgroup && *pp->cgroup) {
+ char *endp = outbuf;
+ int rightward=max_rightward;
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if(rightward>1){
+ *endp++ = ' ';
+ rightward--;
+ endp += escape_str(endp, *pp->cgroup, OUTBUF_SIZE, &rightward);
+ }
+ return max_rightward-rightward;
+ }
+ else
+ return pr_nop(outbuf,pp);
+}
+
+/* "ucomm" is the same thing: short unless -f */
+static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ unsigned flags;
+ int rightward=max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ else flags = ESC_DEFUNCT;
+ endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
+
+ if(bsd_e_option && rightward>1){
+ const char **env = (const char**)pp->environ;
+ if(env && *env){
+ *endp++ = ' ';
+ rightward--;
+ endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
+ }
+ }
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+/* Non-standard, from SunOS 5 */
+static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ int rightward = max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if (rightward>8) /* 8=default, but forest maybe feeds more */
+ rightward = 8;
+
+ endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward);
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+
+/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
+static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ char *cp = outbuf;
+ t = cook_etime(pp);
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ dd = t;
+ cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 );
+ cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 );
+ cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ;
+ return (int)(cp-outbuf);
+}
+
+/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */
+static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu = 0; /* scaled %cpu, 99 means 99% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz;
+ if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
+ if (pcpu > 99U) pcpu = 99U;
+ return snprintf(outbuf, COLWID, "%2u", pcpu);
+}
+/* normal %CPU in ##.# format. */
+static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz;
+ if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+ if (pcpu > 999U)
+ return snprintf(outbuf, COLWID, "%u", pcpu/10U);
+ return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U);
+}
+/* this is a "per-mill" format, like %cpu with no decimal point */
+static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz ;
+ if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+ if (pcpu > 999U) pcpu = 999U;
+ return snprintf(outbuf, COLWID, "%3u", pcpu);
+}
+
+static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->pgrp);
+}
+static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->tgid);
+}
+static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->ppid);
+}
+
+
+/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
+static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ int c;
+ t = cook_time(pp);
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ dd = t;
+ c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 );
+ c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) );
+ return c;
+}
+
+/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages.
+ * Unix98 requires "vsz" to be kB.
+ * Tru64 does both vsize and vsz like "1.23M"
+ *
+ * Our pp->vm_size is kB and our pp->vsize is pages.
+ *
+ * TODO: add flag for "1.23M" behavior, on this and other columns.
+ */
+static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+// "PRI" is created by "opri", or by "pri" when -c is used.
+//
+// Unix98 only specifies that a high "PRI" is low priority.
+// Sun and SCO add the -c behavior. Sun defines "pri" and "opri".
+// Linux may use "priority" for historical purposes.
+//
+// According to the kernel's fs/proc/array.c and kernel/sched.c source,
+// the kernel reports it in /proc via this:
+// p->prio - MAX_RT_PRIO
+// such that "RT tasks are offset by -200. Normal tasks are centered
+// around 0, value goes from -16 to +15" but who knows if that is
+// before or after the conversion...
+//
+// <linux/sched.h> says:
+// MAX_RT_PRIO is currently 100. (so we see 0 in /proc)
+// RT tasks have a p->prio of 0 to 99. (so we see -100 to -1)
+// non-RT tasks are from 100 to 139. (so we see 0 to 39)
+// Lower values have higher priority, as in the UNIX standard.
+//
+// In any case, pp->priority+100 should get us back to what the kernel
+// has for p->prio.
+//
+// Test results with the "yes" program on a 2.6.x kernel:
+//
+// # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm
+// PRI PRI PRI PRI NI %CPU PID COMMAND
+// 0 99 99 39 19 10.6 8686 19
+// 34 65 65 5 -20 94.7 8687 _20
+//
+// Grrr. So the UNIX standard "PRI" must NOT be from "pri".
+// Either of the others will do. We use "opri" for this.
+// (and use "pri" when the "-c" option is used)
+// Probably we should have Linux-specific "pri_for_l" and "pri_for_lc"
+//
+// sched_get_priority_min.2 says the Linux static priority is
+// 1..99 for RT and 0 for other... maybe 100 is kernel-only?
+//
+// A nice range would be -99..0 for RT and 1..40 for normal,
+// which is pp->priority+1. (3-digit max, positive is normal,
+// negative or 0 is RT, and meets the standard for PRI)
+//
+
+// legal as UNIX "PRI"
+// "priority" (was -20..20, now -100..39)
+static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){ /* -20..20 */
+ return snprintf(outbuf, COLWID, "%ld", pp->priority);
+}
+
+// legal as UNIX "PRI"
+// "intpri" and "opri" (was 39..79, now -40..99)
+static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 39..79 */
+ return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority);
+}
+
+// legal as UNIX "PRI"
+// "pri_foo" -- match up w/ nice values of sleeping processes (-120..19)
+static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority - 20);
+}
+
+// legal as UNIX "PRI"
+// "pri_bar" -- makes RT pri show as negative (-99..40)
+static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority + 1);
+}
+
+// legal as UNIX "PRI"
+// "pri_baz" -- the kernel's ->prio value, as of Linux 2.6.8 (1..140)
+static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority + 100);
+}
+
+
+// not legal as UNIX "PRI"
+// "pri" (was 20..60, now 0..139)
+static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 20..60 */
+ return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
+}
+
+// not legal as UNIX "PRI"
+// "pri_api" -- match up w/ RT API (-40..99)
+static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority);
+}
+
+static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->nice);
+}
+
+// HP-UX "cls": RT RR RR2 ???? HPUX FIFO KERN
+// Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant)
+// FIFO+RR share RT; FIFO has Inf quant
+// IA=interactive; FX=fixed; TS=timeshare; SYS=system
+// FSS=fairshare; INTS=interrupts
+// Tru64 "policy": FF RR TS
+// IRIX "class": RT TS B BC WL GN
+// RT=real-time; TS=time-share; B=batch; BC=batch-critical
+// WL=weightless; GN=gang-scheduled
+// see miser(1) for this; PRI has some letter codes too
+static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){
+ switch(pp->sched){
+ case -1: return snprintf(outbuf, COLWID, "-"); // not reported
+ case 0: return snprintf(outbuf, COLWID, "TS"); // SCHED_OTHER SCHED_NORMAL
+ case 1: return snprintf(outbuf, COLWID, "FF"); // SCHED_FIFO
+ case 2: return snprintf(outbuf, COLWID, "RR"); // SCHED_RR
+ case 3: return snprintf(outbuf, COLWID, "B"); // SCHED_BATCH
+ case 4: return snprintf(outbuf, COLWID, "ISO"); // reserved for SCHED_ISO (Con Kolivas)
+ case 5: return snprintf(outbuf, COLWID, "IDL"); // SCHED_IDLE
+ case 6: return snprintf(outbuf, COLWID, "#6"); //
+ case 7: return snprintf(outbuf, COLWID, "#7"); //
+ case 8: return snprintf(outbuf, COLWID, "#8"); //
+ case 9: return snprintf(outbuf, COLWID, "#9"); //
+ default: return snprintf(outbuf, COLWID, "?"); // unknown value
+ }
+}
+// Based on "type", FreeBSD would do:
+// REALTIME "real:%u", prio
+// NORMAL "normal"
+// IDLE "idle:%u", prio
+// default "%u:%u", type, prio
+// We just print the priority, and have other keywords for type.
+static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->rtprio);
+}
+static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->sched);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){
+/*
+ * Unix98 says "blank if running" and also "no blanks"! :-(
+ * Unix98 also says to use '-' if something is meaningless.
+ * Digital uses both '*' and '-', with undocumented differences.
+ * (the '*' for -1 (rare) and the '-' for 0)
+ * Sun claims to use a blank AND use '-', in the same man page.
+ * Perhaps "blank" should mean '-'.
+ *
+ * AIX uses '-' for running processes, the location when there is
+ * only one thread waiting in the kernel, and '*' when there is
+ * more than one thread waiting in the kernel.
+ *
+ * The output should be truncated to maximal columns width -- overflow
+ * is not supported for the "wchan".
+ */
+ const char *w;
+ size_t len;
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
+ w = lookup_wchan(pp->wchan, pp->XXXID);
+ len = strlen(w);
+ if(len>max_rightward) len=max_rightward;
+ memcpy(outbuf, w, len);
+ outbuf[len] = '\0';
+ return len;
+}
+
+static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){
+/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too.
+ *
+ * We use '-' for running processes, the location when there is
+ * only one thread waiting in the kernel, and '*' when there is
+ * more than one thread waiting in the kernel.
+ *
+ * The output should be truncated to maximal columns width -- overflow
+ * is not supported for the "wchan".
+ */
+ const char *w;
+ size_t len;
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ w = lookup_wchan(pp->wchan, pp->XXXID);
+ len = strlen(w);
+ if(len>max_rightward) len=max_rightward;
+ memcpy(outbuf, w, len);
+ outbuf[len] = '\0';
+ return len;
+}
+
+static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
+}
+
+/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */
+/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */
+static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+ return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS);
+}
+
+/* Unix98: format is unspecified, but must match that used by who(1). */
+static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+ return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV);
+}
+
+#if 0
+/* This BSD state display may contain spaces, which is illegal. */
+static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%s", status(pp));
+}
+#endif
+
+// This state display is Unix98 compliant and has lots of info like BSD.
+static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){
+ int end = 0;
+ outbuf[end++] = pp->state;
+// if(pp->rss==0 && pp->state!='Z') outbuf[end++] = 'W'; // useless "swapped out"
+ if(pp->nice < 0) outbuf[end++] = '<';
+ if(pp->nice > 0) outbuf[end++] = 'N';
+// In this order, NetBSD would add:
+// traced 'X'
+// systrace 'x'
+// exiting 'E' (not printed for zombies)
+// vforked 'V'
+// system 'K' (and do not print 'L' too)
+ if(pp->vm_lock) outbuf[end++] = 'L';
+ if(pp->session == pp->tgid) outbuf[end++] = 's'; // session leader
+ if(pp->nlwp > 1) outbuf[end++] = 'l'; // multi-threaded
+ if(pp->pgrp == pp->tpgid) outbuf[end++] = '+'; // in foreground process group
+ outbuf[end] = '\0';
+ return end;
+}
+
+/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */
+static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){
+ outbuf[0] = pp->state;
+ outbuf[1] = '\0';
+ return 1;
+}
+
+static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){
+ /* Unix98 requires octal flags */
+ /* this user-hostile and volatile junk gets 1 character */
+ return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>6U)&0x7U);
+}
+
+// plus these: euid,ruid,egroup,rgroup (elsewhere in this file)
+
+/*********** non-standard ***********/
+
+/*** BSD
+sess session pointer
+(SCO has:Process session leader ID as a decimal value. (SESSION))
+jobc job control count
+cpu short-term cpu usage factor (for scheduling)
+sl sleep time (in seconds; 127 = infinity)
+re core residency time (in seconds; 127 = infinity)
+pagein pageins (same as majflt)
+lim soft memory limit
+tsiz text size (in Kbytes)
+***/
+
+static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack));
+}
+
+static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp));
+}
+
+static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip));
+}
+
+/* This function helps print old-style time formats */
+static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) {
+ if(!t) return snprintf(dst, COLWID, " -");
+ if(t == ~0ULL) return snprintf(dst, COLWID, " xx");
+ if((long long)(t-=rel) < 0) t=0ULL;
+ if(t>9999ULL) return snprintf(dst, COLWID, "%5Lu", t/100ULL);
+ else return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U);
+}
+
+static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long t;
+ unsigned u;
+ t = pp->utime + pp->stime;
+ if(include_dead_children) t += (pp->cutime + pp->cstime);
+ u = t / Hertz;
+ return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U);
+}
+
+static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t start;
+ time_t seconds_ago;
+ start = time_of_boot + pp->start_time / Hertz;
+ seconds_ago = seconds_since_1970 - start;
+ if(seconds_ago < 0) seconds_ago=0;
+ if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4);
+ else strcpy(outbuf, ctime(&start)+10);
+ outbuf[6] = '\0';
+ return 6;
+}
+
+static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){
+ return old_time_helper(outbuf, pp->alarm, 0ULL);
+}
+
+/* HP-UX puts this in pages and uses "vsz" for kB */
+static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024));
+}
+
+
+/*
+ * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss
+ * I suspect some/all of those are broken. They seem to have been
+ * inherited by Linux and AIX from early BSD systems. FreeBSD only
+ * retains tsiz. The prefixed versions come from Debian.
+ * Sun and Digital have none of this crap. The code here comes
+ * from an old Linux ps, and might not be correct for ELF executables.
+ *
+ * AIX TRS size of resident-set (real memory) of text
+ * AIX TSIZ size of text (shared-program) image
+ * FreeBSD tsiz text size (in Kbytes)
+ * 4.3BSD NET/2 trss text resident set size (in Kbytes)
+ * 4.3BSD NET/2 tsiz text size (in Kbytes)
+ */
+
+/* kB data size. See drs, tsiz & trs. */
+static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){
+ long dsiz = 0;
+ if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", dsiz);
+}
+
+/* kB text (code) size. See trs, dsiz & drs. */
+static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){
+ long tsiz = 0;
+ if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", tsiz);
+}
+
+/* kB _resident_ data size. See dsiz, tsiz & trs. */
+static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){
+ long drs = 0;
+ if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", drs);
+}
+
+/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */
+static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){
+ long trs = 0;
+ if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", trs);
+}
+
+/* approximation to: kB of address space that could end up in swap */
+static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack);
+}
+
+/* nasty old Debian thing */
+static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->size);
+}
+
+
+static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){
+ long flt = pp->min_flt;
+ if(include_dead_children) flt += pp->cmin_flt;
+ return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){
+ long flt = pp->maj_flt;
+ if(include_dead_children) flt += pp->cmaj_flt;
+ return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->rss_rlim == RLIM_INFINITY){
+ outbuf[0] = 'x';
+ outbuf[1] = 'x';
+ outbuf[2] = '\0';
+ return 2;
+ }
+ return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10);
+}
+
+/* should print leading tilde ('~') if process is bound to the CPU */
+static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->processor);
+}
+
+static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
+}
+
+/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
+static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long pmem = 0;
+ pmem = pp->vm_rss * 1000ULL / kb_main_total;
+ if (pmem > 999) pmem = 999;
+ return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
+}
+
+static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ t = time_of_boot + pp->start_time / Hertz;
+ return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
+}
+
+/* Unix98 specifies a STIME header for a column that shows the start
+ * time of the process, but does not specify a format or format specifier.
+ * From the general Unix98 rules, we know there must not be any spaces.
+ * Most systems violate that rule, though the Solaris documentation
+ * claims to print the column without spaces. (NOT!)
+ *
+ * So this isn't broken, but could be renamed to u98_std_stime,
+ * as long as it still shows as STIME when using the -f option.
+ */
+static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){
+ struct tm *proc_time;
+ struct tm *our_time;
+ time_t t;
+ const char *fmt;
+ int tm_year;
+ int tm_yday;
+ our_time = localtime(&seconds_since_1970); /* not reentrant */
+ tm_year = our_time->tm_year;
+ tm_yday = our_time->tm_yday;
+ t = time_of_boot + pp->start_time / Hertz;
+ proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
+ fmt = "%H:%M"; /* 03:02 23:59 */
+ if(tm_yday != proc_time->tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */
+ if(tm_year != proc_time->tm_year) fmt = "%Y"; /* 1991 2001 */
+ return strftime(outbuf, 42, fmt, proc_time);
+}
+
+static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ char *str;
+ t = time_of_boot + pp->start_time / Hertz;
+ str = ctime(&t);
+ if(str[8]==' ') str[8]='0';
+ if(str[11]==' ') str[11]='0';
+ if((unsigned long)t+60*60*24 > seconds_since_1970)
+ return snprintf(outbuf, COLWID, "%8.8s", str+11);
+ return snprintf(outbuf, COLWID, " %6.6s", str+4);
+}
+
+
+#ifdef SIGNAL_STRING
+static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){
+ long len = 0;
+ len = strlen(sig);
+ if(wide_signals){
+ if(len>8) return snprintf(outbuf, COLWID, "%s", sig);
+ return snprintf(outbuf, COLWID, "00000000%s", sig);
+ }
+ if(len-strspn(sig,"0") > 8)
+ return snprintf(outbuf, COLWID, "<%s", sig+len-8);
+ return snprintf(outbuf, COLWID, "%s", sig+len-8);
+}
+#else
+static int help_pr_sig(unsigned long long sig){
+ if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig);
+ if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL);
+ return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL);
+}
+#endif
+
+// This one is always thread-specific pending. (from Dragonfly BSD)
+static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->_sigpnd);
+}
+// This one is (wrongly?) thread-specific when printing thread lines,
+// but process-pending otherwise.
+static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->signal);
+}
+static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->blocked);
+}
+static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->sigignore);
+}
+static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->sigcatch);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * internal terms: ruid euid suid fuid
+ * kernel vars: uid euid suid fsuid
+ * command args: ruid uid svuid n/a
+ */
+
+static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->egid);
+}
+static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->rgid);
+}
+static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->sgid);
+}
+static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->fgid);
+}
+
+static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->euid);
+}
+static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->ruid);
+}
+static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->suid);
+}
+static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->fuid);
+}
+
+static int pr_supgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ if (pp->nsupgid == 0) return snprintf(outbuf,2,"-");
+ int rest = COLWID;
+ int i = 0;
+ for (i = 0; i < pp->nsupgid && rest > 5; i++)
+ rest-= snprintf(outbuf+COLWID-rest, rest, "%d ", pp->supgid[i]);
+ return COLWID-rest;
+}
+
+static int pr_supgrp(char *restrict const outbuf, const proc_t *restrict const pp){
+ if (pp->nsupgid == 0) return snprintf(outbuf,2,"-");
+ int rest = COLWID;
+ int i = 0;
+ for (i = 0; i < pp->nsupgid && rest > sizeof( pp->supgrp[i] ) + 1; i++)
+ rest-= snprintf(outbuf+COLWID-rest, rest, "%s ", pp->supgrp[i]);
+ return COLWID-rest;
+}
+
+// The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
+// requires that user and group names print as decimal numbers if there is
+// not enough room in the column, so tough luck if you don't like it.
+//
+// The UNIX and POSIX way to change column width is to rename it:
+// ps -o pid,user=CumbersomeUserNames -o comm
+// The easy way is to directly specify the desired width:
+// ps -o pid,user:19,comm
+//
+static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){
+ if(!user_is_number){
+ int rightward = OUTBUF_SIZE; /* max cells */
+ int len; /* real cells */
+
+ escape_str(outbuf, name, OUTBUF_SIZE, &rightward);
+ len = OUTBUF_SIZE-rightward;
+
+ if(len <= (int)max_rightward)
+ return len; /* returns number of cells */
+ }
+ return snprintf(outbuf, COLWID, "%u", u);
+}
+
+static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->ruser, pp->ruid);
+}
+static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->euser, pp->euid);
+}
+static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->fuser, pp->fuid);
+}
+static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->suser, pp->suid);
+}
+
+static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->egroup, pp->egid);
+}
+static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->rgroup, pp->rgid);
+}
+static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->fgroup, pp->fgid);
+}
+static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->sgroup, pp->sgid);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// TID tid LWP lwp SPID spid
+static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->tid);
+}
+// thcount THCNT
+static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->nlwp);
+}
+
+static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->session);
+}
+static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->tpgid);
+}
+
+
+/* SGI uses "cpu" to print the processor ID with header "P" */
+static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){ /* FIXME */
+ if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor);
+ return snprintf(outbuf, COLWID, "*");
+}
+
+
+/****************** FLASK & seLinux security stuff **********************/
+// move the bulk of this to libproc sometime
+
+static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
+ char filename[48];
+ size_t len;
+ ssize_t num_read;
+ int fd;
+
+// wchan file is suitable for testing
+//snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid);
+snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid);
+
+ fd = open(filename, O_RDONLY, 0);
+ if(likely(fd==-1)) goto fail;
+ num_read = read(fd, outbuf, 666);
+ close(fd);
+ if(unlikely(num_read<=0)) goto fail;
+ outbuf[num_read] = '\0';
+
+ len = 0;
+ while(outbuf[len]>' ' && outbuf[len]<='~') len++;
+ outbuf[len] = '\0';
+ if(len) return len;
+
+fail:
+ outbuf[0] = '-';
+ outbuf[1] = '\0';
+ return 1;
+}
+
+#if 0
+// This needs more study, considering:
+// 1. the static linking option (maybe disable this in that case)
+// 2. the -z and -Z option issue
+// 3. width of output
+static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
+ static int (*ps_getpidcon)(pid_t pid, char **context) = 0;
+ static int tried_load = 0;
+ size_t len;
+ char *context;
+
+ if(!ps_getpidcon && !tried_load){
+ void *handle = dlopen("libselinux.so.1", RTLD_NOW);
+ if(handle){
+ dlerror();
+ ps_getpidcon = dlsym(handle, "getpidcon");
+ if(dlerror())
+ ps_getpidcon = 0;
+ }
+ tried_load++;
+ }
+ if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){
+ size_t max_len = OUTBUF_SIZE-1;
+ len = strlen(context);
+ if(len > max_len) len = max_len;
+ memcpy(outbuf, context, len);
+ outbuf[len] = '\0';
+ free(context);
+ }else{
+ outbuf[0] = '-';
+ outbuf[1] = '\0';
+ len = 1;
+ }
+ return len;
+}
+#endif
+
+
+////////////////////////////// Test code /////////////////////////////////
+
+// like "args"
+static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"[123456789-12345] <defunct>","ps","123456789-123456"};
+ (void)pp;
+ snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]);
+ return strlen(outbuf);
+}
+static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"unlimited", "[123456789-12345] <defunct>","ps","123456789-123456"};
+ (void)pp;
+ snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]);
+ return strlen(outbuf);
+}
+
+// like "etime"
+static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
+}
+static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]);
+}
+
+// like "tty"
+static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]);
+}
+static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
+}
+
+/***************************************************************************/
+/*************************** other stuff ***********************************/
+
+/*
+ * Old header specifications.
+ *
+ * short Up " PID TTY STAT TIME COMMAND"
+ * long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND
+ * user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND
+ * jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
+ * sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND
+ * vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND
+ * m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND
+ * regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND
+ */
+
+/*
+ * Unix98 requires that the heading for tty is TT, though XPG4, Digital,
+ * and BSD use TTY. The Unix98 headers are:
+ * args,comm,etime,group,nice,pcpu,pgid
+ * pid,ppid,rgroup,ruser,time,tty,user,vsz
+ *
+ * BSD c: "command" becomes accounting name ("comm" or "ucomm")
+ * BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number)
+ */
+
+/* Justification control for flags field. */
+#define USER CF_USER // left if text, right if numeric
+#define LEFT CF_LEFT
+#define RIGHT CF_RIGHT
+#define UNLIMITED CF_UNLIMITED
+#define WCHAN CF_WCHAN // left if text, right if numeric
+#define SIGNAL CF_SIGNAL // right in 9, or 16 if room
+#define PIDMAX CF_PIDMAX
+#define TO CF_PRINT_THREAD_ONLY
+#define PO CF_PRINT_PROCESS_ONLY
+#define ET CF_PRINT_EVERY_TIME
+#define AN CF_PRINT_AS_NEEDED // no idea
+
+/* short names to save space */
+#define MEM PROC_FILLMEM /* read statm */
+#define ARG PROC_FILLARG /* read cmdline (cleared if c option) */
+#define COM PROC_FILLCOM /* read cmdline (cleared if not -f option) */
+#define ENV PROC_FILLENV /* read environ */
+#define USR PROC_FILLUSR /* uid_t -> user names */
+#define GRP PROC_FILLGRP /* gid_t -> group names */
+#define WCH PROC_FILLWCHAN /* do WCHAN lookup */
+#define SUPGRP PROC_FILLSUPGRP /* supgid -> supplementary group names */
+
+#define CGRP PROC_FILLCGROUP /* read cgroup */
+/* TODO
+ * pull out annoying BSD aliases into another table (to macro table?)
+ * add sorting functions here (to unify names)
+ */
+
+/* temporary hack -- mark new stuff grabbed from Debian ps */
+#define LNx LNX
+
+/* there are about 211 listed */
+
+/* Many of these are placeholders for unsupported options. */
+static const format_struct format_array[] = {
+/* code header print() sort() width need vendor flags */
+{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, ET|RIGHT}, /*pcpu*/
+{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, PO|RIGHT}, /*pmem*/
+{"_left", "LLLLLLLL", pr_t_left, sr_nop, 8, 0, TST, ET|LEFT},
+{"_left2", "L2L2L2L2", pr_t_left2, sr_nop, 8, 0, TST, ET|LEFT},
+{"_right", "RRRRRRRRRRR", pr_t_right, sr_nop, 11, 0, TST, ET|RIGHT},
+{"_right2", "R2R2R2R2R2R", pr_t_right2, sr_nop, 11, 0, TST, ET|RIGHT},
+{"_unlimited","U", pr_t_unlimited, sr_nop, 16, 0, TST, ET|UNLIMITED},
+{"_unlimited2","U2", pr_t_unlimited2, sr_nop, 16, 0, TST, ET|UNLIMITED},
+{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, /*acflg*/
+{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*acflag*/
+{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT},
+{"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT},
+{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT},
+{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/
+{"atime", "TIME", pr_time, sr_time, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/
+{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT},
+{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT},
+{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT},
+{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT},
+{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/
+{"cgroup", "CGROUP", pr_cgroup, sr_nop, 27, CGRP, LNX, PO|UNLIMITED},
+{"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT},
+{"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/
+{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT},
+{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/
+{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT},
+{"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT},
+{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/
+{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/
+{"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT},
+{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/
+{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
+{"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide!
+{"cputime", "TIME", pr_time, sr_time, 8, 0, DEC, ET|RIGHT}, /*time*/
+{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT},
+{"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts?
+{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, AN|RIGHT},
+{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, AN|LEFT},
+{"drs", "DRS", pr_drs, sr_drs, 5, MEM, LNX, PO|RIGHT},
+{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, PO|RIGHT},
+{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, ET|RIGHT},
+{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, GRP, LNX, ET|USER},
+{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, TO|RIGHT},
+{"emul", "EMUL", pr_nop, sr_nop, 13, 0, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */
+{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT},
+{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED},
+{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT},
+{"etime", "ELAPSED", pr_etime, sr_etime, 11, 0, U98, ET|RIGHT}, /* was 7 wide */
+{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT},
+{"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER},
+{"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/
+{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
+{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
+{"flag", "F", pr_flag, sr_flags, 1, 0, DEC, ET|RIGHT},
+{"flags", "F", pr_flag, sr_flags, 1, 0, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */
+{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, PO|LEFT},
+{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
+{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
+{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
+{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
+{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
+{"fuser", "FUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
+{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, ET|RIGHT},
+{"group", "GROUP", pr_egroup, sr_egroup, 8, GRP, U98, ET|USER},
+{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigignore*/
+{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/
+{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/
+{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT},
+{"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT},
+{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"label", "LABEL", pr_context, sr_nop, 31, 0, SGI, ET|LEFT},
+{"lastcpu", "C", pr_psr, sr_nop, 3, 0, BSD, TO|RIGHT}, // DragonFly
+{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT},
+{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */
+{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/
+{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
+{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, ET|RIGHT},
+{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, ET|RIGHT}, /* login ID */
+{"luser", "LUSER", pr_nop, sr_nop, 8, USR, LNX, ET|USER}, /* login USER */
+{"lwp", "LWP", pr_thread, sr_tid, 5, 0, SUN, TO|PIDMAX|RIGHT},
+{"m_drs", "DRS", pr_drs, sr_drs, 5, MEM, LNx, PO|RIGHT},
+{"m_dt", "DT", pr_nop, sr_dt, 4, MEM, LNx, PO|RIGHT},
+{"m_lrs", "LRS", pr_nop, sr_lrs, 5, MEM, LNx, PO|RIGHT},
+{"m_resident", "RES", pr_nop, sr_resident, 5,MEM, LNx, PO|RIGHT},
+{"m_share", "SHRD", pr_nop, sr_share, 5, MEM, LNx, PO|RIGHT},
+{"m_size", "SIZE", pr_size, sr_size, 5, MEM, LNX, PO|RIGHT},
+{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, PO|RIGHT},
+{"m_trs", "TRS", pr_trs, sr_trs, 5, MEM, LNx, PO|RIGHT},
+{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, AN|RIGHT},
+{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
+{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT},
+{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT},
+{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
+{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
+{"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */
+{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/
+{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/
+{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nlwp", "NLWP", pr_nlwp, sr_nlwp, 4, 0, SUN, PO|RIGHT},
+{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*nsigs*/
+{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*nsignals*/
+{"nswap", "NSWAP", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, TO|RIGHT},
+{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, TO|RIGHT},
+{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, PO|RIGHT},
+{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*oublock*/
+{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*oublk*/
+{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
+{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
+{"pagein", "PAGEIN", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
+{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, ET|RIGHT}, /*%cpu*/
+{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /*sig*/
+{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT},
+{"pid", "PID", pr_pid, sr_tgid, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, PO|RIGHT}, /*%mem*/
+{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT},
+{"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT},
+{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, TO|RIGHT},
+{"pri_api", "API", pr_pri_api, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_bar", "BAR", pr_pri_bar, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_baz", "BAZ", pr_pri_baz, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_foo", "FOO", pr_pri_foo, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, TO|RIGHT},
+{"prmgrp", "PRMGRP", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
+{"prmid", "PRMID", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
+{"project", "PROJECT", pr_nop, sr_nop, 12, 0, SUN, PO|LEFT}, // see prm* andctid
+{"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT},
+{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT},
+{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT},
+{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT},
+{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT},
+{"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT},
+{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT},
+{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */
+{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */
+{"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/
+{"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/
+{"rtprio", "RTPRIO", pr_rtprio, sr_rtprio, 6, 0, BSD, TO|RIGHT},
+{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, ET|RIGHT},
+{"ruser", "RUSER", pr_ruser, sr_ruser, 8, USR, U98, ET|USER},
+{"s", "S", pr_s, sr_state, 1, 0, SUN, TO|LEFT}, /*stat,state*/
+{"sched", "SCH", pr_sched, sr_sched, 3, 0, AIX, TO|RIGHT},
+{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, AN|RIGHT}, /* man page misspelling of scount? */
+{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */
+{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT},
+{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, PO|PIDMAX|RIGHT},
+{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, TO|RIGHT}, /* "cpu" number */
+{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, PO|LEFT}, /* SZ:RSS */
+{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, ET|RIGHT},
+{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
+{"share", "-", pr_nop, sr_share, 1, MEM, LNX, PO|RIGHT},
+{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */
+{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */
+{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, ET|SIGNAL},
+{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/
+{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/
+{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/
+{"size", "SIZE", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT},
+{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT},
+{"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT},
+{"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/
+{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, ET|RIGHT},
+{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, PO|RIGHT},
+{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*stackp*/
+{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, ET|RIGHT},
+{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, TO|LEFT}, /*state,s*/
+{"state", "S", pr_s, sr_state, 1, 0, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */
+{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */
+{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT},
+{"supgid", "SUPGID", pr_supgid, sr_supgid, 27, 0, LNX, PO|UNLIMITED},
+{"supgrp", "SUPGRP", pr_supgrp, sr_supgrp, 27, SUPGRP, LNX, PO|UNLIMITED},
+{"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER},
+{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT},
+{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
+{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, ET|RIGHT},
+{"svuser", "SVUSER", pr_suser, sr_suser, 8, USR, LNX, ET|USER},
+{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, ET|RIGHT},
+{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, PO|RIGHT},
+{"taskid", "TASKID", pr_nop, sr_nop, 5, 0, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID?
+{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT},
+{"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT},
+{"time", "TIME", pr_time, sr_time, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+{"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
+{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, PO|PIDMAX|RIGHT},
+{"trs", "TRS", pr_trs, sr_trs, 4, MEM, AIX, PO|RIGHT},
+{"trss", "TRSS", pr_trs, sr_trs, 4, MEM, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */
+{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
+{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, PO|PIDMAX|RIGHT},
+{"tsid", "TSID", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
+{"tsig", "PENDING", pr_tsig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */
+{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, PO|RIGHT},
+{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, PO|LEFT},
+{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */
+{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT},
+{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT},
+{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/
+{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/
+{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT},
+{"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER},
+{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT},
+{"uname", "USER", pr_euser, sr_euser, 8, USR, DEC, ET|USER}, /* man page misspelling of user? */
+{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/
+{"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */
+{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT},
+{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/
+{"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C"
+{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT},
+{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT},
+{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT},
+{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT},
+{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, PO|RIGHT},
+{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, PO|RIGHT},
+{"vsize", "VSZ", pr_vsz, sr_vsize, 6, 0, DEC, PO|RIGHT}, /*vsz*/
+{"vsz", "VSZ", pr_vsz, sr_vm_size, 6, 0, U98, PO|RIGHT}, /*vsize*/
+{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, WCH, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
+{"wname", "WCHAN", pr_wname, sr_nop, 6, WCH, SGI, TO|WCHAN}, /* opposite of nwchan */
+{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT},
+{"zone", "ZONE", pr_context, sr_nop, 31, 0, SUN, ET|LEFT}, // Solaris zone == Linux context?
+{"zoneid", "ZONEID", pr_nop, sr_nop, 31, 0, SUN, ET|RIGHT},// Linux only offers context names
+{"~", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT} /* NULL would ruin alphabetical order */
+};
+
+#undef USER
+#undef LEFT
+#undef RIGHT
+#undef UNLIMITED
+#undef WCHAN
+#undef SIGNAL
+#undef PIDMAX
+#undef PO
+#undef TO
+#undef AN
+#undef ET
+
+static const int format_array_count = sizeof(format_array)/sizeof(format_struct);
+
+
+/****************************** Macro formats *******************************/
+/* First X field may be NR, which is p->start_code>>26 printed with %2ld */
+/* That seems useless though, and Debian already killed it. */
+/* The ones marked "Digital" have the name defined, not just the data. */
+static const macro_struct macro_array[] = {
+{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */
+{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */
+{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */
+{"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */
+{"END_SYS5", "state,tname,time,command"}, /* trailer for -O */
+{"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */
+
+{"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */
+{"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */
+{"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */
+{"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */
+{"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */
+
+{"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */
+{"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */
+{"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */
+{"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */
+{"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */
+{"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */
+
+{"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */
+
+{"FLASK_context", "pid,context,command"}, /* Flask Linux context, --context */
+
+{"HP_", "pid,tty,time,comm"}, /* HP default */
+{"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */
+{"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */
+{"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */
+
+{"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */
+{"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */
+{"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */
+{"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */
+
+{"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */
+{"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */
+{"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */
+{"OL_m", "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */
+{"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */
+{"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */
+{"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */
+
+{"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */
+{"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */
+{"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */
+{"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */
+{"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */
+{"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */
+
+{"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */
+{"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */
+{"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */
+
+{"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */
+{"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */
+{"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */
+
+{"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */
+{"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */
+{"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */
+{"~", "~"} /* NULL would ruin alphabetical order */
+};
+
+static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);
+
+
+/*************************** AIX formats ********************/
+/* Convert AIX format codes to normal format specifiers. */
+static const aix_struct aix_array[] = {
+{'C', "pcpu", "%CPU"},
+{'G', "group", "GROUP"},
+{'P', "ppid", "PPID"},
+{'U', "user", "USER"},
+{'a', "args", "COMMAND"},
+{'c', "comm", "COMMAND"},
+{'g', "rgroup", "RGROUP"},
+{'n', "nice", "NI"},
+{'p', "pid", "PID"},
+{'r', "pgid", "PGID"},
+{'t', "etime", "ELAPSED"},
+{'u', "ruser", "RUSER"},
+{'x', "time", "TIME"},
+{'y', "tty", "TTY"},
+{'z', "vsz", "VSZ"},
+{'~', "~", "~"} /* NULL would ruin alphabetical order */
+};
+static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);
+
+
+/********************* sorting ***************************/
+/* Convert short sorting codes to normal format specifiers. */
+static const shortsort_struct shortsort_array[] = {
+{'C', "pcpu" },
+{'G', "tpgid" },
+{'J', "cstime" },
+/* {'K', "stime" }, */ /* conflict, system vs. start time */
+{'M', "maj_flt" },
+{'N', "cmaj_flt" },
+{'P', "ppid" },
+{'R', "resident" },
+{'S', "share" },
+{'T', "start_time" },
+{'U', "uid" }, /* euid */
+{'c', "cmd" },
+{'f', "flags" },
+{'g', "pgrp" },
+{'j', "cutime" },
+{'k', "utime" },
+{'m', "min_flt" },
+{'n', "cmin_flt" },
+{'o', "session" },
+{'p', "pid" },
+{'r', "rss" },
+{'s', "size" },
+{'t', "tty" },
+{'u', "user" },
+{'v', "vsize" },
+{'y', "priority" }, /* nice */
+{'~', "~" } /* NULL would ruin alphabetical order */
+};
+static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);
+
+
+/*********** print format_array **********/
+/* called by the parser in another file */
+void print_format_specifiers(void){
+ const format_struct *walk = format_array;
+ while(*(walk->spec) != '~'){
+ if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head);
+ walk++;
+ }
+}
+
+/************ comparison functions for bsearch *************/
+
+static int compare_format_structs(const void *a, const void *b){
+ return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec);
+}
+
+static int compare_macro_structs(const void *a, const void *b){
+ return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec);
+}
+
+/******** look up structs as needed by the sort & format parsers ******/
+
+const shortsort_struct *search_shortsort_array(const int findme){
+ const shortsort_struct *walk = shortsort_array;
+ while(walk->desc != '~'){
+ if(walk->desc == findme) return walk;
+ walk++;
+ }
+ return NULL;
+}
+
+const aix_struct *search_aix_array(const int findme){
+ const aix_struct *walk = aix_array;
+ while(walk->desc != '~'){
+ if(walk->desc == findme) return walk;
+ walk++;
+ }
+ return NULL;
+}
+
+const format_struct *search_format_array(const char *findme){
+ format_struct key;
+ key.spec = findme;
+ return bsearch(&key, format_array, format_array_count,
+ sizeof(format_struct), compare_format_structs
+ );
+}
+
+const macro_struct *search_macro_array(const char *findme){
+ macro_struct key;
+ key.spec = findme;
+ return bsearch(&key, macro_array, macro_array_count,
+ sizeof(macro_struct), compare_macro_structs
+ );
+}
+
+static unsigned int active_cols; /* some multiple of screen_cols */
+
+/***** Last chance, avoid needless trunctuation. */
+static void check_header_width(void){
+ format_node *walk = format_list;
+ unsigned int total = 0;
+ int was_normal = 0;
+ unsigned int i = 0;
+ unsigned int sigs = 0;
+ while(walk){
+ switch((walk->flags) & CF_JUST_MASK){
+ default:
+ total += walk->width;
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case CF_SIGNAL:
+ sigs++;
+ total += walk->width;
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case CF_UNLIMITED: /* could chop this a bit */
+ if(walk->next) total += walk->width;
+ else total += 3; /* not strlen(walk->name) */
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case 0: /* AIX */
+ total += walk->width;
+ was_normal = 0;
+ break;
+ }
+ walk = walk->next;
+ }
+ for(;;){
+ i++;
+ active_cols = screen_cols * i;
+ if(active_cols>=total) break;
+ if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
+ }
+ wide_signals = (total+sigs*7 <= active_cols);
+}
+
+
+/********** show one process (NULL proc prints header) **********/
+
+//#define SPACE_AMOUNT page_size
+#define SPACE_AMOUNT 144
+
+static char *saved_outbuf;
+
+void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){
+ /* unknown: maybe set correct & actual to 1, remove +/- 1 below */
+ int correct = 0; /* screen position we should be at */
+ int actual = 0; /* screen position we are at */
+ int amount = 0; /* amount of text that this data is */
+ int leftpad = 0; /* amount of space this column _could_ need */
+ int space = 0; /* amount of space we actually need to print */
+ int dospace = 0; /* previous column determined that we need a space */
+ int legit = 0; /* legitimately stolen extra space */
+ int sz = 0; /* real size of data in outbuffer */
+ int tmpspace = 0;
+ char *restrict const outbuf = saved_outbuf;
+ static int did_stuff = 0; /* have we ever printed anything? */
+
+ if(unlikely(-1==(long)p)){ /* true only once, at the end */
+ if(did_stuff) return;
+ /* have _never_ printed anything, but might need a header */
+ if(!--lines_to_next_header){
+ lines_to_next_header = header_gap;
+ show_one_proc(NULL,fmt);
+ }
+ /* fprintf(stderr, "No processes available.\n"); */ /* legal? */
+ exit(1);
+ }
+ if(likely(p)){ /* not header, maybe we should call ourselves for it */
+ if(unlikely(!--lines_to_next_header)){
+ lines_to_next_header = header_gap;
+ show_one_proc(NULL,fmt);
+ }
+ }
+ did_stuff = 1;
+ if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n");
+
+ /* print row start sequence */
+ for(;;){
+ legit = 0;
+ /* set width suggestion which might be ignored */
+// if(likely(fmt->next)) max_rightward = fmt->width;
+// else max_rightward = active_cols-((correct>actual) ? correct : actual);
+
+ if(likely(fmt->next)){
+ max_rightward = fmt->width;
+ tmpspace = 0;
+ }else{
+ tmpspace = correct-actual;
+ if (tmpspace<1){
+ tmpspace = dospace;
+ max_rightward = active_cols-actual-tmpspace;
+ }else{
+ max_rightward = active_cols - ( (correct>actual) ? correct : actual );
+ }
+ }
+ max_leftward = fmt->width + actual - correct; /* TODO check this */
+
+// fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n",
+// active_cols, max_rightward, max_leftward, actual, correct);
+
+ /* prepare data and calculate leftpad */
+ if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p);
+ else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
+
+ switch((fmt->flags) & CF_JUST_MASK){
+ case 0: /* for AIX, assigned outside this file */
+ leftpad = 0;
+ break;
+ case CF_LEFT: /* bad */
+ leftpad = 0;
+ break;
+ case CF_RIGHT: /* OK */
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ break;
+ case CF_SIGNAL:
+ /* if the screen is wide enough, use full 16-character output */
+ if(wide_signals){
+ leftpad = 16 - amount;
+ legit = 7;
+ }else{
+ leftpad = 9 - amount;
+ }
+ if(leftpad < 0) leftpad = 0;
+ break;
+ case CF_USER: /* bad */
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ if(!user_is_number) leftpad = 0;
+ break;
+ case CF_WCHAN: /* bad */
+ if(wchan_is_number){
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ break;
+ }else{
+ if ((active_cols-actual-tmpspace)<1)
+ outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
+ leftpad = 0;
+ break;
+ }
+ case CF_UNLIMITED:
+ {
+ if(active_cols-actual-tmpspace < 1)
+ outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
+ leftpad = 0;
+ break;
+ }
+ default:
+ fprintf(stderr, "bad alignment code\n");
+ break;
+ }
+ /* At this point:
+ *
+ * correct from previous column
+ * actual from previous column
+ * amount not needed (garbage due to chopping)
+ * leftpad left padding for this column alone (not make-up or gap)
+ * space not needed (will recalculate now)
+ * dospace if we require space between this and the prior column
+ * legit space we were allowed to steal, and thus did steal
+ */
+ space = correct - actual + leftpad;
+ if(space<1) space=dospace;
+ if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT; // only so much available
+
+ /* real size -- don't forget in 'amount' is number of cells */
+ sz = strlen(outbuf);
+
+ /* print data, set x position stuff */
+ if(unlikely(!fmt->next)){
+ /* Last column. Write padding + data + newline all together. */
+ outbuf[sz] = '\n';
+ fwrite(outbuf-space, space+sz+1, 1, stdout);
+ break;
+ }
+ /* Not the last column. Write padding + data together. */
+ fwrite(outbuf-space, space+sz, 1, stdout);
+ actual += space+amount;
+ correct += fmt->width;
+ correct += legit; /* adjust for SIGNAL expansion */
+ if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
+ correct++;
+ dospace = 1;
+ }else{
+ dospace = 0;
+ }
+ fmt = fmt->next;
+ /* At this point:
+ *
+ * correct screen position we should be at
+ * actual screen position we are at
+ * amount not needed
+ * leftpad not needed
+ * space not needed
+ * dospace if have determined that we need a space next time
+ * legit not needed
+ */
+ }
+}
+
+
+#ifdef TESTING
+static void sanity_check(void){
+ format_struct *fs = format_array;
+ while((fs->spec)[0] != '~'){
+ if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec);
+ fs++;
+ }
+}
+#endif
+
+
+void init_output(void){
+ int outbuf_pages;
+ char *outbuf;
+
+ switch(page_size){
+ case 65536: page_shift = 16; break;
+ case 32768: page_shift = 15; break;
+ case 16384: page_shift = 14; break;
+ case 8192: page_shift = 13; break;
+ default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
+ case 4096: page_shift = 12; break;
+ case 2048: page_shift = 11; break;
+ case 1024: page_shift = 10; break;
+ }
+
+ // add page_size-1 to round up
+ outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
+ outbuf = mmap(
+ 0,
+ page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0
+ );
+ memset(outbuf, ' ', SPACE_AMOUNT);
+ if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ);
+ mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page
+ saved_outbuf = outbuf + SPACE_AMOUNT;
+ // available space: page_size*outbuf_pages-SPACE_AMOUNT
+
+ seconds_since_1970 = time(NULL);
+ time_of_boot = seconds_since_1970 - seconds_since_boot;
+
+ meminfo();
+
+ check_header_width();
+}
diff --git a/smartt-top/ps/p b/smartt-top/ps/p
new file mode 100755
index 0000000..10064fa
--- /dev/null
+++ b/smartt-top/ps/p
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# Wow, using $* causes great pain with: ps "pid,user pcpu,pmem"
+# The "$@" won't break that into 2 arguments.
+#
+LD_LIBRARY_PATH=../proc exec ./ps "$@"
diff --git a/smartt-top/ps/parser.c b/smartt-top/ps/parser.c
new file mode 100644
index 0000000..5ad9035
--- /dev/null
+++ b/smartt-top/ps/parser.c
@@ -0,0 +1,1246 @@
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/* Ought to have debug print stuff like this:
+ * #define Print(fmt, args...) printf("Debug: " fmt, ## args)
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "../proc/version.h"
+
+#define ARG_GNU 0
+#define ARG_END 1
+#define ARG_PGRP 2
+#define ARG_SYSV 3
+#define ARG_PID 4
+#define ARG_BSD 5
+#define ARG_FAIL 6
+#define ARG_SESS 7
+
+static int w_count = 0;
+
+static int ps_argc; /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg; /* index into ps_argv */
+static char *flagptr; /* current location in ps_argv[thisarg] */
+static int not_pure_unix = 0; /* set by BSD and GNU options */
+static int force_bsd = 0; /* set when normal parsing fails */
+
+#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x))\
+ return "The " x " option is exclusive."
+
+
+/********** utility functions **********/
+
+/*
+ * Both "-Oppid" and "-O ppid" should be legal, though Unix98
+ * does not require it. BSD and Digital Unix allow both.
+ * Return the argument or NULL;
+ */
+static const char *get_opt_arg(void){
+ if(*(flagptr+1)){ /* argument is part of ps_argv[thisarg] */
+ not_pure_unix = 1;
+ return flagptr+1;
+ }
+ if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */
+ /* argument follows ps_argv[thisarg] */
+ if(*(ps_argv[thisarg+1]) == '\0') return NULL;
+ return ps_argv[++thisarg];
+}
+
+/********** parse lists (of UID, tty, GID, PID...) **********/
+
+static const char *parse_pid(char *str, sel_union *ret){
+ char *endp;
+ unsigned long num;
+ static const char pidrange[] = "Process ID out of range.";
+ static const char pidsyntax[] = "Process ID list syntax error.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0') return pidsyntax;
+ if(num<1) return pidrange;
+ if(num > 0x7fffffffUL) return pidrange;
+ ret->pid = num;
+ return 0;
+}
+
+static const char *parse_uid(char *str, sel_union *ret){
+ struct passwd *passwd_data;
+ char *endp;
+ unsigned long num;
+ static const char uidrange[] = "User ID out of range.";
+ static const char uidexist[] = "User name does not exist.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0'){ /* hmmm, try as login name */
+ passwd_data = getpwnam(str);
+ if(!passwd_data) return uidexist;
+ num = passwd_data->pw_uid;
+ }
+ if(num > 0xfffffffeUL) return uidrange;
+ ret->uid = num;
+ return 0;
+}
+
+static const char *parse_gid(char *str, sel_union *ret){
+ struct group *group_data;
+ char *endp;
+ unsigned long num;
+ static const char gidrange[] = "Group ID out of range.";
+ static const char gidexist[] = "Group name does not exist.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0'){ /* hmmm, try as login name */
+ group_data = getgrnam(str);
+ if(!group_data) return gidexist;
+ num = group_data->gr_gid;
+ }
+ if(num > 0xfffffffeUL) return gidrange;
+ ret->gid = num;
+ return 0;
+}
+
+static const char *parse_cmd(char *str, sel_union *ret){
+ strncpy(ret->cmd, str, sizeof ret->cmd); // strncpy pads to end
+ return 0;
+}
+
+static const char *parse_tty(char *str, sel_union *ret){
+ struct stat sbuf;
+ static const char missing[] = "TTY could not be found.";
+ static const char not_tty[] = "List member was not a TTY.";
+ char path[4096];
+ if(str[0]=='/'){
+ if(stat(str, &sbuf) >= 0) goto found_it;
+ return missing;
+ }
+#define lookup(p) \
+ snprintf(path,4096,p,str); \
+ if(stat(path, &sbuf) >= 0) goto found_it
+
+ lookup("/dev/pts/%s"); /* New Unix98 ptys go first */
+ lookup("/dev/%s");
+ lookup("/dev/tty%s");
+ lookup("/dev/pty%s");
+ lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */
+ if(!strcmp(str,"-")){ /* "-" means no tty (from AIX) */
+ ret->tty = 0; /* processes w/o tty */
+ return 0;
+ }
+ if(!strcmp(str,"?")){ /* "?" means no tty, which bash eats (Reno BSD?) */
+ ret->tty = 0; /* processes w/o tty */
+ return 0;
+ }
+ if(!*(str+1) && (stat(str,&sbuf)>=0)){ /* Kludge! Assume bash ate '?'. */
+ ret->tty = 0; /* processes w/o tty */
+ return 0;
+ }
+#undef lookup
+ return missing;
+found_it:
+ if(!S_ISCHR(sbuf.st_mode)) return not_tty;
+ ret->tty = sbuf.st_rdev;
+ return 0;
+}
+
+/*
+ * Used to parse lists in a generic way. (function pointers)
+ */
+static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){
+ selection_node *node;
+ char *buf; /* temp copy of arg to hack on */
+ char *sep_loc; /* separator location: " \t," */
+ char *walk;
+ int items;
+ int need_item;
+ const char *err; /* error code that could or did happen */
+ /*** prepare to operate ***/
+ node = malloc(sizeof(selection_node));
+ node->u = malloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */
+ node->n = 0;
+ buf = malloc(strlen(arg)+1);
+ strcpy(buf, arg);
+ /*** sanity check and count items ***/
+ need_item = 1; /* true */
+ items = 0;
+ walk = buf;
+ err = "Improper list.";
+ do{
+ switch(*walk){
+ case ' ': case ',': case '\t': case '\0':
+ if(need_item) goto parse_error;
+ need_item=1;
+ break;
+ default:
+ if(need_item) items++;
+ need_item=0;
+ }
+ } while (*++walk);
+ if(need_item) goto parse_error;
+ node->n = items;
+ /*** actually parse the list ***/
+ walk = buf;
+ while(items--){
+ sep_loc = strpbrk(walk," ,\t");
+ if(sep_loc) *sep_loc = '\0';
+ if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error;
+ walk = sep_loc + 1; /* point to next item, if any */
+ }
+ free(buf);
+ node->next = selection_list;
+ selection_list = node;
+ return NULL;
+parse_error:
+ free(buf);
+ free(node->u);
+ free(node);
+ return err;
+}
+
+/***************** parse SysV options, including Unix98 *****************/
+static const char *parse_sysv_option(void){
+ const char *arg;
+ const char *err;
+
+ flagptr = ps_argv[thisarg];
+ while(*++flagptr){
+ // Find any excuse to ignore stupid Unix98 misfeatures.
+ //
+ // This list of options is ONLY for those defined by the
+ // "IEEE Std 1003.1, 2004 Edition", "ISO/IEC 9945:2003",
+ // or "Version 2 of the Single Unix Specification".
+ //
+ // It may be time to re-think the existance of this list.
+ // In the meantime, please do not add to it. The list is
+ // intended to ONLY contain flags defined by the POSIX and UNIX
+ // standards published by The Open Group, IEEE, and ISO.
+ if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX
+
+ switch(*flagptr){
+ case 'A':
+ trace("-A selects all processes.\n");
+ all_processes = 1;
+ break;
+ case 'C': /* end */
+ trace("-C select by process name.\n"); /* Why only HP/UX and us? */
+ arg=get_opt_arg();
+ if(!arg) return "List of command names must follow -C.";
+ err=parse_list(arg, parse_cmd);
+ if(err) return err;
+ selection_list->typecode = SEL_COMM;
+ return NULL; /* can't have any more options */
+ case 'F': /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */
+ trace("-F does fuller listing\n");
+ format_modifiers |= FM_F;
+ format_flags |= FF_Uf;
+ unix_f_option = 1; /* does this matter? */
+ break;
+ case 'G': /* end */
+ trace("-G select by RGID (supports names)\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of real groups must follow -G.";
+ err=parse_list(arg, parse_gid);
+ if(err) return err;
+ selection_list->typecode = SEL_RGID;
+ return NULL; /* can't have any more options */
+ case 'H': /* another nice HP/UX feature */
+ trace("-H Process hierarchy (like ASCII art forest option)\n");
+ forest_type = 'u';
+ break;
+#if 0
+ case 'J': // specify list of job IDs in hex (IRIX) -- like HP "-R" maybe?
+ trace("-J select by job ID\n"); // want a JID ("jid") for "-j" too
+ arg=get_opt_arg();
+ if(!arg) return "List of jobs must follow -J.";
+ err=parse_list(arg, parse_jid);
+ if(err) return err;
+ selection_list->typecode = SEL_JID;
+ return NULL; /* can't have any more options */
+#endif
+ case 'L': /* */
+ /* In spite of the insane 2-level thread system, Sun appears to
+ * have made this option Linux-compatible. If a process has N
+ * threads, ps will produce N lines of output. (not N+1 lines)
+ * Zombies are the only exception, with NLWP==0 and 1 output line.
+ * SCO UnixWare uses -L too.
+ */
+ trace("-L Print LWP (thread) info.\n");
+ thread_flags |= TF_U_L;
+// format_modifiers |= FM_L;
+ break;
+ case 'M': // typically the SE Linux context
+ trace("-M Print security label for Mandatory Access Control.\n");
+ format_modifiers |= FM_M;
+ break;
+ case 'N':
+ trace("-N negates.\n");
+ negate_selection = 1;
+ break;
+ case 'O': /* end */
+ trace("-O is preloaded -o.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format or sort specification must follow -O.";
+ defer_sf_option(arg, SF_U_O);
+ return NULL; /* can't have any more options */
+ case 'P': /* SunOS 5 "psr" or unknown HP/UX feature */
+ trace("-P adds columns of PRM info (HP-UX), PSR (SunOS), or capabilities (IRIX)\n");
+ format_modifiers |= FM_P;
+ break;
+#if 0
+ case 'R': // unknown HP/UX feature, like IRIX "-J" maybe?
+ trace("-R select by PRM group\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of PRM groups must follow -R.";
+ err=parse_list(arg, parse_prm);
+ if(err) return err;
+ selection_list->typecode = SEL_PRM;
+ return NULL; /* can't have any more options */
+#endif
+ case 'T':
+ /* IRIX 6.5 docs suggest POSIX threads get shown individually.
+ * This would make -T be like -L, -m, and m. (but an extra column)
+ * Testing (w/ normal processes) shows 1 line/process, not 2.
+ * Also, testing shows PID==SPID for all normal processes.
+ */
+ trace("-T adds strange SPID column (old sproc() threads?)\n");
+ thread_flags |= TF_U_T;
+// format_modifiers |= FM_T;
+ break;
+ case 'U': /* end */
+ trace("-U select by RUID (supports names).\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of real groups must follow -U.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_RUID;
+ return NULL; /* can't have any more options */
+ case 'V': /* single */
+ trace("-V prints version.\n");
+ exclusive("-V");
+ display_version();
+ exit(0);
+ // This must be verified against SVR4-MP. (UnixWare or Powermax)
+ // Leave it undocumented until that problem is solved.
+ case 'Z': /* full Mandatory Access Control level info */
+ trace("-Z shows full MAC info\n");
+ format_modifiers |= FM_M;
+ break;
+ case 'a':
+ trace("-a select all with a tty, but omit session leaders.\n");
+ simple_select |= SS_U_a;
+ break;
+ case 'c':
+ /* HP-UX and SunOS 5 scheduling info modifier */
+ trace("-c changes scheduling info.\n");
+ format_modifiers |= FM_c;
+ break;
+ case 'd':
+ trace("-d select all, but omit session leaders.\n");
+ simple_select |= SS_U_d;
+ break;
+ case 'e':
+ trace("-e selects all processes.\n");
+ all_processes = 1;
+ break;
+ case 'f':
+ trace("-f does full listing\n");
+ format_flags |= FF_Uf;
+ unix_f_option = 1; /* does this matter? */
+ break;
+ case 'g': /* end */
+ trace("-g selects by session leader OR by group name\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of session leaders OR effective group names must follow -g.";
+ err=parse_list(arg, parse_pid);
+ if(!err){
+ selection_list->typecode = SEL_SESS;
+ return NULL; /* can't have any more options */
+ }
+ err=parse_list(arg, parse_gid);
+ if(!err){
+ selection_list->typecode = SEL_EGID;
+ return NULL; /* can't have any more options */
+ }
+ return "List of session leaders OR effective group IDs was invalid.";
+ case 'j':
+ trace("-j jobs format.\n");
+ /* old Debian used RD_j and Digital uses JFMT */
+ if(sysv_j_format) format_flags |= FF_Uj;
+ else format_modifiers |= FM_j;
+ break;
+ case 'l':
+ trace("-l long format.\n");
+ format_flags |= FF_Ul;
+ break;
+ case 'm':
+ trace("-m shows threads.\n");
+ /* note that AIX shows 2 lines for a normal process */
+ thread_flags |= TF_U_m;
+ break;
+ case 'n': /* end */
+ trace("-n sets namelist file.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Alternate System.map file must follow -n.";
+ namelist_file = arg;
+ return NULL; /* can't have any more options */
+ case 'o': /* end */
+ /* Unix98 has gross behavior regarding this. From the following: */
+ /* ps -o pid,nice=NICE,tty=TERMINAL,comm */
+ /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm" */
+ /* Yes, the second column has the name "NICE,tty=TERMINAL,comm" */
+ /* This parser looks for any excuse to ignore that braindamage. */
+ trace("-o user-defined format.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format specification must follow -o.";
+ not_pure_unix |= defer_sf_option(arg, SF_U_o);
+ return NULL; /* can't have any more options */
+ case 'p': /* end */
+ trace("-p select by PID.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of process IDs must follow -p.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL; /* can't have any more options */
+#if 0
+ case 'r':
+ trace("-r some Digital Unix thing about warnings...\n");
+ trace(" or SCO's option to chroot() for new /proc and /dev.\n");
+ return "The -r option is reserved.";
+ break;
+#endif
+ case 's': /* end */
+ trace("-s Select processes belonging to the sessions given.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of session IDs must follow -s.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_SESS;
+ return NULL; /* can't have any more options */
+ case 't': /* end */
+ trace("-t select by tty.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of terminals (pty, tty...) must follow -t.";
+ err=parse_list(arg, parse_tty);
+ if(err) return err;
+ selection_list->typecode = SEL_TTY;
+ return NULL; /* can't have any more options */
+ case 'u': /* end */
+ trace("-u select by user ID (the EUID?) (supports names).\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of users must follow -u.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_EUID;
+ return NULL; /* can't have any more options */
+ case 'w':
+ trace("-w wide output.\n");
+ w_count++;
+ break;
+ case 'x': /* behind personality until "ps -ax" habit is uncommon */
+ if(personality & PER_SVR4_x){
+ // Same as -y, but for System V Release 4 MP
+ trace("-x works like Sun Solaris & SCO Unixware -y option\n");
+ format_modifiers |= FM_y;
+ break;
+ }
+ if(personality & PER_HPUX_x){
+ trace("-x extends the command line\n");
+ w_count += 2;
+ unix_f_option = 1;
+ break;
+ }
+ return "Must set personality to get -x option.";
+ case 'y': /* Sun's -l hack (also: Irix "lnode" resource control info) */
+ trace("-y Print lnone info in UID/USER column or do Sun -l hack.\n");
+ format_modifiers |= FM_y;
+ break;
+#if 0
+ // This must be verified against SVR4-MP (UnixWare or Powermax)
+ case 'z': /* alias of Mandatory Access Control level info */
+ trace("-z shows aliased MAC info\n");
+ format_modifiers |= FM_M;
+ break;
+ // Solaris 10 does this
+ case 'z': /* select by zone */
+ trace("-z secects by zone\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of zones (contexts, labels, whatever?) must follow -z.";
+ err=parse_list(arg, parse_zone);
+ if(err) return err;
+ selection_list->typecode = SEL_ZONE;
+ return NULL; /* can't have any more options */
+#endif
+ case '-':
+ return "Embedded '-' among SysV options makes no sense.";
+ break;
+ case '\0':
+ return "Please report the \"SysV \\0 can't happen\" bug.";
+ break;
+ default:
+ return "Unsupported SysV option.";
+ } /* switch */
+ } /* while */
+ return NULL;
+}
+
+/************************* parse BSD options **********************/
+static const char *parse_bsd_option(void){
+ const char *arg;
+ const char *err;
+
+ flagptr = ps_argv[thisarg]; /* assume we _have_ a '-' */
+ if(flagptr[0]=='-'){
+ if(!force_bsd) return "Can't happen! Problem #1.";
+ }else{
+ flagptr--; /* off beginning, will increment before use */
+ if(personality & PER_FORCE_BSD){
+ if(!force_bsd) return "Can't happen! Problem #2.";
+ }else{
+ if(force_bsd) return "2nd chance parse failed, not BSD or SysV.";
+ }
+ }
+
+ while(*++flagptr){
+ switch(*flagptr){
+ case '0' ... '9': /* end */
+ trace("0..9 Old BSD-style select by process ID\n");
+ arg=flagptr;
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL; /* can't have any more options */
+#if 0
+ case 'A':
+ /* maybe this just does a larger malloc() ? */
+ trace("A Increases the argument space (Digital Unix)\n");
+ return "Option A is reserved.";
+ break;
+ case 'C':
+ /* should divide result by 1-(e**(foo*log(bar))) */
+ trace("C Use raw CPU time for %%CPU instead of decaying ave\n");
+ return "Option C is reserved.";
+ break;
+#endif
+ case 'H': // The FreeBSD way (NetBSD:s OpenBSD:k FreeBSD:H -- NIH???)
+ trace("H Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n");
+ thread_flags |= TF_B_H;
+ //format_modifiers |= FM_L; // FIXME: determine if we need something like this
+ break;
+ case 'L': /* single */
+ trace("L List all format specifiers\n");
+ exclusive("L");
+ print_format_specifiers();
+ exit(0);
+ case 'M': // undocumented for now: these are proliferating!
+ trace("M MacOS X thread display, like AIX/Tru64\n");
+ thread_flags |= TF_B_m;
+ break;
+ case 'N': /* end */
+ trace("N Specify namelist file\n");
+ arg=get_opt_arg();
+ if(!arg) return "Alternate System.map file must follow N.";
+ namelist_file = arg;
+ return NULL; /* can't have any more options */
+ case 'O': /* end */
+ trace("O Like o + defaults, add new columns after PID. Also sort.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format or sort specification must follow O.";
+ defer_sf_option(arg, SF_B_O);
+ return NULL; /* can't have any more options */
+ break;
+ case 'S':
+ trace("S include dead kids in sum\n");
+ include_dead_children = 1;
+ break;
+ case 'T':
+ trace("T Select all processes on this terminal\n");
+ /* put our tty on a tiny list */
+ {
+ selection_node *node;
+ node = malloc(sizeof(selection_node));
+ node->u = malloc(sizeof(sel_union));
+ node->u[0].tty = cached_tty;
+ node->typecode = SEL_TTY;
+ node->n = 1;
+ node->next = selection_list;
+ selection_list = node;
+ }
+ break;
+ case 'U': /* end */
+ trace("U Select processes for specified users.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of users must follow U.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_EUID;
+ return NULL; /* can't have any more options */
+ case 'V': /* single */
+ trace("V show version info\n");
+ exclusive("V");
+ display_version();
+ exit(0);
+ case 'W':
+ trace("W N/A get swap info from ... not /dev/drum.\n");
+ return "Obsolete W option not supported. (You have a /dev/drum?)";
+ break;
+ case 'X':
+ trace("X Old Linux i386 register format\n");
+ format_flags |= FF_LX;
+ break;
+ case 'Z': /* FreeBSD does MAC like SGI's Irix does it */
+ trace("Z Print security label for Mandatory Access Control.\n");
+ format_modifiers |= FM_M;
+ break;
+ case 'a':
+ trace("a Select all w/tty, including other users\n");
+ simple_select |= SS_B_a;
+ break;
+ case 'c':
+ trace("c true command name\n");
+ bsd_c_option = 1;
+ break;
+ case 'e':
+ trace("e environment\n");
+ bsd_e_option = 1;
+ break;
+ case 'f':
+ trace("f ASCII art forest\n");
+ forest_type = 'b';
+ break;
+ case 'g':
+ trace("g _all_, even group leaders!.\n");
+ simple_select |= SS_B_g;
+ break;
+ case 'h':
+ trace("h Repeat header... yow.\n");
+ if(header_type) return "Only one heading option may be specified.";
+ if(personality & PER_BSD_h) header_type = HEAD_MULTI;
+ else header_type = HEAD_NONE;
+ break;
+ case 'j':
+ trace("j job control format\n");
+ format_flags |= FF_Bj;
+ break;
+ case 'k':
+ // OpenBSD: don't hide "kernel threads" -- like the swapper?
+ // trace("k Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n");
+
+ // NetBSD, and soon (?) FreeBSD: sort-by-keyword
+ trace("k Specify sorting keywords.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Long sort specification must follow 'k'.";
+ defer_sf_option(arg, SF_G_sort);
+ return NULL; /* can't have any more options */
+ case 'l':
+ trace("l Display long format\n");
+ format_flags |= FF_Bl;
+ break;
+ case 'm':
+ trace("m all threads, sort on mem use, show mem info\n");
+ if(personality & PER_OLD_m){
+ format_flags |= FF_Lm;
+ break;
+ }
+ if(personality & PER_BSD_m){
+ defer_sf_option("pmem", SF_B_m);
+ break;
+ }
+ thread_flags |= TF_B_m;
+ break;
+ case 'n':
+ trace("n Numeric output for WCHAN, and USER replaced by UID\n");
+ wchan_is_number = 1;
+ user_is_number = 1;
+ /* TODO add tty_is_number too? */
+ break;
+ case 'o': /* end */
+ trace("o Specify user-defined format\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format specification must follow o.";
+ defer_sf_option(arg, SF_B_o);
+ return NULL; /* can't have any more options */
+ case 'p': /* end */
+ trace("p Select by process ID\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of process IDs must follow p.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL; /* can't have any more options */
+ case 'r':
+ trace("r Select running processes\n");
+ running_only = 1;
+ break;
+ case 's':
+ trace("s Display signal format\n");
+ format_flags |= FF_Bs;
+ break;
+ case 't': /* end */
+ trace("t Select by tty.\n");
+ /* List of terminals (tty, pty...) _should_ follow t. */
+ arg=get_opt_arg();
+ if(!arg){
+ /* Wow, obsolete BSD syntax. Put our tty on a tiny list. */
+ selection_node *node;
+ node = malloc(sizeof(selection_node));
+ node->u = malloc(sizeof(sel_union));
+ node->u[0].tty = cached_tty;
+ node->typecode = SEL_TTY;
+ node->n = 1;
+ node->next = selection_list;
+ selection_list = node;
+ return NULL;
+ }
+ err=parse_list(arg, parse_tty);
+ if(err) return err;
+ selection_list->typecode = SEL_TTY;
+ return NULL; /* can't have any more options */
+ case 'u':
+ trace("u Display user-oriented\n");
+ format_flags |= FF_Bu;
+ break;
+ case 'v':
+ trace("v Display virtual memory\n");
+ format_flags |= FF_Bv;
+ break;
+ case 'w':
+ trace("w wide output\n");
+ w_count++;
+ break;
+ case 'x':
+ trace("x Select processes without controlling ttys\n");
+ simple_select |= SS_B_x;
+ break;
+ case '-':
+ return "Embedded '-' among BSD options makes no sense.";
+ break;
+ case '\0':
+ return "Please report the \"BSD \\0 can't happen\" bug.";
+ break;
+ default:
+ return "Unsupported option (BSD syntax)";
+ } /* switch */
+ } /* while */
+ return NULL;
+}
+
+/*************** gnu long options **********************/
+
+/*
+ * Return the argument or NULL
+ */
+static const char *grab_gnu_arg(void){
+ switch(*flagptr){ /* argument is part of ps_argv[thisarg] */
+ default:
+ return NULL; /* something bad */
+ case '=': case ':':
+ if(*++flagptr) return flagptr; /* found it */
+ return NULL; /* empty '=' or ':' */
+ case '\0': /* try next argv[] */
+ ;
+ }
+ if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */
+ /* argument follows ps_argv[thisarg] */
+ if(*(ps_argv[thisarg+1]) == '\0') return NULL;
+ return ps_argv[++thisarg];
+}
+
+typedef struct gnu_table_struct {
+ const char *name; /* long option name */
+ const void *jump; /* See gcc extension info. :-) */
+} gnu_table_struct;
+
+static int compare_gnu_table_structs(const void *a, const void *b){
+ return strcmp(((const gnu_table_struct*)a)->name,((const gnu_table_struct*)b)->name);
+}
+
+/* Option arguments are after ':', after '=', or in argv[n+1] */
+static const char *parse_gnu_option(void){
+ const char *arg;
+ const char *err;
+ char *s;
+ size_t sl;
+ char buf[16];
+ gnu_table_struct findme = { buf, NULL};
+ gnu_table_struct *found;
+ static const gnu_table_struct gnu_table[] = {
+ {"Group", &&case_Group}, /* rgid */
+ {"User", &&case_User}, /* ruid */
+ {"cols", &&case_cols},
+ {"columns", &&case_columns},
+ {"context", &&case_context},
+ {"cumulative", &&case_cumulative},
+ {"deselect", &&case_deselect}, /* -N */
+ {"forest", &&case_forest}, /* f -H */
+ {"format", &&case_format},
+ {"group", &&case_group}, /* egid */
+ {"header", &&case_header},
+ {"headers", &&case_headers},
+ {"heading", &&case_heading},
+ {"headings", &&case_headings},
+ {"help", &&case_help},
+ {"info", &&case_info},
+ {"lines", &&case_lines},
+ {"no-header", &&case_no_header},
+ {"no-headers", &&case_no_headers},
+ {"no-heading", &&case_no_heading},
+ {"no-headings", &&case_no_headings},
+ {"noheader", &&case_noheader},
+ {"noheaders", &&case_noheaders},
+ {"noheading", &&case_noheading},
+ {"noheadings", &&case_noheadings},
+ {"pid", &&case_pid},
+ {"ppid", &&case_ppid},
+ {"rows", &&case_rows},
+ {"sid", &&case_sid},
+ {"sort", &&case_sort},
+ {"tty", &&case_tty},
+ {"user", &&case_user}, /* euid */
+ {"version", &&case_version},
+ {"width", &&case_width},
+ };
+ const int gnu_table_count = sizeof(gnu_table)/sizeof(gnu_table_struct);
+
+ s = ps_argv[thisarg]+2;
+ sl = strcspn(s,":=");
+ if(sl > 15) return "Unknown gnu long option.";
+ strncpy(buf, s, sl);
+ buf[sl] = '\0';
+ flagptr = s+sl;
+
+ found = bsearch(&findme, gnu_table, gnu_table_count,
+ sizeof(gnu_table_struct), compare_gnu_table_structs
+ );
+
+ if(!found) return "Unknown gnu long option.";
+
+ goto *(found->jump); /* See gcc extension info. :-) */
+
+ case_Group:
+ trace("--Group\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of real groups must follow --Group.";
+ err=parse_list(arg, parse_gid);
+ if(err) return err;
+ selection_list->typecode = SEL_RGID;
+ return NULL;
+ case_User:
+ trace("--User\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of real users must follow --User.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_RUID;
+ return NULL;
+ case_cols:
+ case_width:
+ case_columns:
+ trace("--cols\n");
+ arg = grab_gnu_arg();
+ if(arg && *arg){
+ long t;
+ char *endptr;
+ t = strtol(arg, &endptr, 0);
+ if(!*endptr && (t>0) && (t<2000000000)){
+ screen_cols = (int)t;
+ return NULL;
+ }
+ }
+ return "Number of columns must follow --cols, --width, or --columns.";
+ case_cumulative:
+ trace("--cumulative\n");
+ if(s[sl]) return "Option --cumulative does not take an argument.";
+ include_dead_children = 1;
+ return NULL;
+ case_deselect:
+ trace("--deselect\n");
+ if(s[sl]) return "Option --deselect does not take an argument.";
+ negate_selection = 1;
+ return NULL;
+ case_no_header:
+ case_no_headers:
+ case_no_heading:
+ case_no_headings:
+ case_noheader:
+ case_noheaders:
+ case_noheading:
+ case_noheadings:
+ trace("--noheaders\n");
+ if(s[sl]) return "Option --no-heading does not take an argument.";
+ if(header_type) return "Only one heading option may be specified.";
+ header_type = HEAD_NONE;
+ return NULL;
+ case_header:
+ case_headers:
+ case_heading:
+ case_headings:
+ trace("--headers\n");
+ if(s[sl]) return "Option --heading does not take an argument.";
+ if(header_type) return "Only one heading option may be specified.";
+ header_type = HEAD_MULTI;
+ return NULL;
+ case_forest:
+ trace("--forest\n");
+ if(s[sl]) return "Option --forest does not take an argument.";
+ forest_type = 'g';
+ return NULL;
+ case_format:
+ trace("--format\n");
+ arg=grab_gnu_arg();
+ if(!arg) return "Format specification must follow --format.";
+ defer_sf_option(arg, SF_G_format);
+ return NULL;
+ case_group:
+ trace("--group\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of effective groups must follow --group.";
+ err=parse_list(arg, parse_gid);
+ if(err) return err;
+ selection_list->typecode = SEL_EGID;
+ return NULL;
+ case_help:
+ trace("--help\n");
+ exclusive("--help");
+ fwrite(help_message,1,strlen(help_message),stdout);
+ exit(0);
+ return NULL;
+ case_info:
+ trace("--info\n");
+ exclusive("--info");
+ self_info();
+ exit(0);
+ return NULL;
+ case_pid:
+ trace("--pid\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of process IDs must follow --pid.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL;
+ case_ppid:
+ trace("--ppid\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of process IDs must follow --ppid.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PPID;
+ return NULL;
+ case_rows:
+ case_lines:
+ trace("--rows\n");
+ arg = grab_gnu_arg();
+ if(arg && *arg){
+ long t;
+ char *endptr;
+ t = strtol(arg, &endptr, 0);
+ if(!*endptr && (t>0) && (t<2000000000)){
+ screen_rows = (int)t;
+ return NULL;
+ }
+ }
+ return "Number of rows must follow --rows or --lines.";
+ case_sid:
+ trace("--sid\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "Some sid thing(s) must follow --sid.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_SESS;
+ return NULL;
+ case_sort:
+ trace("--sort\n");
+ arg=grab_gnu_arg();
+ if(!arg) return "Long sort specification must follow --sort.";
+ defer_sf_option(arg, SF_G_sort);
+ return NULL;
+ case_tty:
+ trace("--tty\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of ttys must follow --tty.";
+ err=parse_list(arg, parse_tty);
+ if(err) return err;
+ selection_list->typecode = SEL_TTY;
+ return NULL;
+ case_user:
+ trace("--user\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of effective users must follow --user.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_EUID;
+ return NULL;
+ case_version:
+ trace("--version\n");
+ exclusive("--version");
+ display_version();
+ exit(0);
+ return NULL;
+ case_context:
+ trace("--context\n");
+ format_flags |= FF_Fc;
+ return NULL;
+}
+
+/*************** process trailing PIDs **********************/
+static const char *parse_trailing_pids(void){
+ selection_node *pidnode; /* pid */
+ selection_node *grpnode; /* process group */
+ selection_node *sidnode; /* session */
+ char **argp; /* pointer to pointer to text of PID */
+ const char *err; /* error code that could or did happen */
+ int i;
+
+ i = ps_argc - thisarg; /* how many trailing PIDs, SIDs, PGRPs?? */
+ argp = ps_argv + thisarg;
+ thisarg = ps_argc - 1; /* we must be at the end now */
+
+ pidnode = malloc(sizeof(selection_node));
+ pidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+ pidnode->n = 0;
+
+ grpnode = malloc(sizeof(selection_node));
+ grpnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+ grpnode->n = 0;
+
+ sidnode = malloc(sizeof(selection_node));
+ sidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+ sidnode->n = 0;
+
+ while(i--){
+ char *data;
+ data = *(argp++);
+ switch(*data){
+ default: err = parse_pid( data, pidnode->u + pidnode->n++); break;
+ case '-': err = parse_pid(++data, grpnode->u + grpnode->n++); break;
+ case '+': err = parse_pid(++data, sidnode->u + sidnode->n++); break;
+ }
+ if(err) return err; /* the node gets freed with the list */
+ }
+
+ if(pidnode->n){
+ pidnode->next = selection_list;
+ selection_list = pidnode;
+ selection_list->typecode = SEL_PID;
+ } /* else free both parts */
+
+ if(grpnode->n){
+ grpnode->next = selection_list;
+ selection_list = grpnode;
+ selection_list->typecode = SEL_PGRP;
+ } /* else free both parts */
+
+ if(sidnode->n){
+ sidnode->next = selection_list;
+ selection_list = sidnode;
+ selection_list->typecode = SEL_SESS;
+ } /* else free both parts */
+
+ return NULL;
+}
+
+/************** misc stuff ***********/
+
+static void reset_parser(void){
+ w_count = 0;
+}
+
+static int arg_type(const char *str){
+ int tmp = str[0];
+ if((tmp>='a') && (tmp<='z')) return ARG_BSD;
+ if((tmp>='A') && (tmp<='Z')) return ARG_BSD;
+ if((tmp>='0') && (tmp<='9')) return ARG_PID;
+ if(tmp=='+') return ARG_SESS;
+ if(tmp!='-') return ARG_FAIL;
+ tmp = str[1];
+ if((tmp>='a') && (tmp<='z')) return ARG_SYSV;
+ if((tmp>='A') && (tmp<='Z')) return ARG_SYSV;
+ if((tmp>='0') && (tmp<='9')) return ARG_PGRP;
+ if(tmp!='-') return ARG_FAIL;
+ tmp = str[2];
+ if((tmp>='a') && (tmp<='z')) return ARG_GNU;
+ if((tmp>='A') && (tmp<='Z')) return ARG_GNU;
+ if(tmp=='\0') return ARG_END;
+ return ARG_FAIL;
+}
+
+/* First assume sysv, because that is the POSIX and Unix98 standard. */
+static const char *parse_all_options(void){
+ const char *err = NULL;
+ int at;
+ while(++thisarg < ps_argc){
+ trace("parse_all_options calling arg_type for \"%s\"\n", ps_argv[thisarg]);
+ at = arg_type(ps_argv[thisarg]);
+ trace("ps_argv[thisarg] is %s\n", ps_argv[thisarg]);
+ if(at != ARG_SYSV) not_pure_unix = 1;
+ switch(at){
+ case ARG_GNU:
+ err = parse_gnu_option();
+ break;
+ case ARG_SYSV:
+ if(!force_bsd){ /* else go past case ARG_BSD */
+ err = parse_sysv_option();
+ break;
+ case ARG_BSD:
+ if(force_bsd && !(personality & PER_FORCE_BSD)) return "way bad";
+ }
+ prefer_bsd_defaults = 1;
+ err = parse_bsd_option();
+ break;
+ case ARG_PGRP:
+ case ARG_SESS:
+ case ARG_PID:
+ prefer_bsd_defaults = 1;
+ err = parse_trailing_pids();
+ break;
+ case ARG_END:
+ case ARG_FAIL:
+ trace(" FAIL/END on [%s]\n",ps_argv[thisarg]);
+ return "Garbage option.";
+ break;
+ default:
+ printf(" ? %s\n",ps_argv[thisarg]);
+ return "Something broke.";
+ } /* switch */
+ if(err) return err;
+ } /* while */
+ return NULL;
+}
+
+static void choose_dimensions(void){
+ if(w_count && (screen_cols<132)) screen_cols=132;
+ if(w_count>1) screen_cols=OUTBUF_SIZE;
+ /* perhaps --html and --null should set unlimited width */
+}
+
+static const char *thread_option_check(void){
+ if(!thread_flags){
+ thread_flags = TF_show_proc;
+ return NULL;
+ }
+
+ if(forest_type){
+ return "Thread display conflicts with forest display.";
+ }
+ //thread_flags |= TF_no_forest;
+
+ if((thread_flags&TF_B_H) && (thread_flags&(TF_B_m|TF_U_m)))
+ return "Thread flags conflict; can't use H with m or -m.";
+ if((thread_flags&TF_B_m) && (thread_flags&TF_U_m))
+ return "Thread flags conflict; can't use both m and -m.";
+ if((thread_flags&TF_U_L) && (thread_flags&TF_U_T))
+ return "Thread flags conflict; can't use both -L and -T.";
+
+ if(thread_flags&TF_B_H) thread_flags |= (TF_show_proc|TF_loose_tasks);
+ if(thread_flags&(TF_B_m|TF_U_m)) thread_flags |= (TF_show_proc|TF_show_task|TF_show_both);
+
+ if(thread_flags&(TF_U_T|TF_U_L)){
+ if(thread_flags&(TF_B_m|TF_U_m|TF_B_H)){
+ // Got a thread style, so format modification is a requirement?
+ // Maybe -T/-L has H thread style though. (sorting interaction?)
+ //return "Huh? Tell procps-feedback@lists.sf.net what you expected.";
+ thread_flags |= TF_must_use;
+ }else{
+ // using -L/-T thread style, so format from elsewhere is OK
+ thread_flags |= TF_show_task; // or like the H option?
+ //thread_flags |= TF_no_sort;
+ }
+ }
+
+ return NULL;
+}
+
+int arg_parse(int argc, char *argv[]){
+ const char *err = NULL;
+ const char *err2 = NULL;
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+
+ if(personality & PER_FORCE_BSD) goto try_bsd;
+
+ err = parse_all_options();
+ if(err) goto try_bsd;
+ err = thread_option_check();
+ if(err) goto try_bsd;
+ err = process_sf_options(!not_pure_unix);
+ if(err) goto try_bsd;
+ err = select_bits_setup();
+ if(err) goto try_bsd;
+
+ choose_dimensions();
+ return 0;
+
+try_bsd:
+ trace("--------- now try BSD ------\n");
+
+ reset_global();
+ reset_parser();
+ reset_sortformat();
+ format_flags = 0;
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+ /* no need to reset flagptr */
+ not_pure_unix=1;
+ force_bsd=1;
+ prefer_bsd_defaults=1;
+ if(!( (PER_OLD_m|PER_BSD_m) & personality )) /* if default m setting... */
+ personality |= PER_OLD_m; /* Prefer old Linux over true BSD. */
+ /* Do not set PER_FORCE_BSD! It is tested below. */
+
+ err2 = parse_all_options();
+ if(err2) goto total_failure;
+ err2 = thread_option_check();
+ if(err2) goto total_failure;
+ err2 = process_sf_options(!not_pure_unix);
+ if(err2) goto total_failure;
+ err2 = select_bits_setup();
+ if(err2) goto total_failure;
+
+ // Feel a need to patch this out? First of all, read the FAQ.
+ // Second of all, talk to me. Without this warning, people can
+ // get seriously confused. Ask yourself if users would freak out
+ // about "ps -aux" suddenly changing behavior if a user "x" were
+ // added to the system.
+ //
+ // Also, a "-x" option is coming. It's already there in fact,
+ // for some non-default personalities. So "ps -ax" will parse
+ // as SysV options... and you're screwed if you've been patching
+ // out the friendly warning. Cut-over is likely to be in 2005.
+ if(!(personality & PER_FORCE_BSD))
+ fprintf(stderr, "Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html\n");
+ // Remember: contact albert@users.sf.net or procps-feedback@lists.sf.net
+ // if you should feel tempted. Be damn sure you understand all
+ // the issues. The same goes for other stuff too, BTW. Please ask.
+ // I'm happy to justify various implementation choices.
+
+ choose_dimensions();
+ return 0;
+
+total_failure:
+ reset_parser();
+ if(personality & PER_FORCE_BSD) fprintf(stderr, "ERROR: %s\n", err2);
+ else fprintf(stderr, "ERROR: %s\n", err);
+ fwrite(help_message,1,strlen(help_message),stderr);
+ exit(1);
+ /* return 1; */ /* useless */
+}
diff --git a/smartt-top/ps/ps.1 b/smartt-top/ps/ps.1
new file mode 100644
index 0000000..f07dc82
--- /dev/null
+++ b/smartt-top/ps/ps.1
@@ -0,0 +1,1541 @@
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" Man page for ps.
+.\" Quick hack conversion by Albert Cahalan, 1998.
+.\" Licensed under version 2 of the Gnu General Public License.
+.\"
+.TH PS 1 "February 25, 2010" "Linux" "Linux User's Manual"
+.\"
+.\" To render this page:
+.\" groff -t -b -man -X -P-resolution -P100 -Tps ps.1 &
+.\" groff -t -b -man -X -TX100 ps.1 &
+.\" tbl ps.1 | troff -Ww -man -z
+.\" groff -t -man -Tps ps.1 | ps2pdf - - > ps.pdf
+.\"
+.\" The '70s called. They want their perfect justification,
+.\" hyphenation, and double-spaced sentences back.
+.na
+.nh
+.if n .ss 12 0
+.\"
+.\"
+.\" ColSize is used for the format spec table.
+.\" It's the left margin, minus the right, minus
+.\" the space needed for the 1st two columns.
+.\" Making it messy: inches, ens, points, scaled points...
+.\"
+.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-26u)n)
+.\"
+.\" This is for command options
+.nr OptSize (16u)
+.\"
+.\" l=\n(.l
+.\" i=\n(.i
+.\" o=\n(.o
+.\" H=\n(.H
+.\" s=\n(.s
+.\" ColSize=\n[ColSize]
+.\"
+.\" Macro for easy option formatting: .opt \-x
+.de opt
+. TP \\n[OptSize]
+. BI \\$*
+..
+.\"
+.SH NAME
+ps \- report a snapshot of the current processes.
+.SH SYNOPSIS
+\fBps\fR [\fIoptions\fR]
+.PP
+.PP
+.SH DESCRIPTION
+.B ps
+displays information about a selection of the active processes.
+If you want a repetitive update of the selection and the
+displayed information, use\ \fItop\fR(1) instead.
+.P
+This version of \fBps\fR accepts several kinds of options:
+.PD 0
+.IP 1 4
+UNIX options, which may be grouped and must be preceded by a dash.
+.IP 2 4
+BSD options, which may be grouped and must not be used with a dash.
+.IP 3 4
+GNU long options, which are preceded by two dashes.
+.PD
+.PP
+Options of different types may be freely mixed, but conflicts can appear.
+There are some synonymous options, which are functionally identical, due
+to the many standards and \fBps\fR implementations that this \fBps\fR is
+compatible with.
+.P
+Note that "\fBps\ \-aux\fR" is distinct from "\fBps\ aux\fR".
+The POSIX and UNIX standards require that "\fBps\ \-aux\fR" print all
+processes owned by a user named "x", as well as printing all processes
+that would be selected by the \fB\-a\fR option. If the user named "x" does
+not exist, this \fBps\fR may interpret the command as "\fBps\ aux\fR"
+instead and print a warning. This behavior is intended to aid in
+transitioning old scripts and habits. It is fragile, subject to change,
+and thus should not be relied upon.
+.P
+By default, \fBps\fR selects all processes
+with the same effective user ID (euid=EUID) as the current user
+and
+associated with the same terminal as the invoker.
+It displays the process ID (pid=PID),
+the terminal associated with the process (tname=TTY),
+the cumulated CPU time in [dd\-]hh:mm:ss format (time=TIME),
+and the executable name (ucmd=CMD).
+Output is unsorted by default.
+.P
+The use of BSD\-style options will add process state (stat=STAT) to the
+default display and show the command args (args=COMMAND) instead of the
+executable name. You can override this with the \fBPS_FORMAT\fR
+environment variable. The use of BSD\-style options will also change the
+process selection to include processes on other terminals (TTYs) that
+are owned by you; alternately, this may be described as setting the
+selection to be the set of all processes filtered to exclude
+processes owned by other users or not on a terminal. These effects
+are not considered when options are described as being "identical" below,
+so \fB\-M\fR will be considered identical to \fBZ\fR and so on.
+.P
+Except as described below, process selection options are additive.
+The default selection is discarded, and then the selected processes
+are added to the set of processes to be displayed.
+A\ process will thus be shown if it meets any of the given
+selection criteria.
+.PP
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH "EXAMPLES"
+.TP 3
+To see every process on the system using standard syntax:
+.B ps\ \-e
+.br
+.B ps\ \-ef
+.br
+.B ps\ \-eF
+.br
+.B ps\ \-ely
+.TP
+To see every process on the system using BSD syntax:
+.B ps\ ax
+.br
+.B ps\ axu
+.TP
+To print a process tree:
+.B ps\ -ejH
+.br
+.B ps\ axjf
+.TP
+To get info about threads:
+.B ps\ -eLf
+.br
+.B ps\ axms
+.TP
+To get security info:
+.B ps\ -eo euser,ruser,suser,fuser,f,comm,label
+.br
+.B ps\ axZ
+.br
+.B ps\ -eM
+.TP
+To see every process running as root (real\ &\ effective\ ID) in user format:
+.B ps\ \-U\ root\ \-u\ root\ u
+.TP
+To see every process with a user\-defined format:
+.B ps\ \-eo\ pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm
+.br
+.B ps\ axo\ stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
+.br
+.B ps\ \-eopid,tt,user,fname,tmout,f,wchan
+.TP
+Print only the process IDs of syslogd:
+.B ps\ \-C\ syslogd\ \-o\ pid=
+.TP
+Print only the name of PID 42:
+.B ps\ \-p\ 42\ \-o\ comm=
+.PP
+.PP
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH "SIMPLE PROCESS SELECTION"
+
+.opt a
+Lift the BSD\-style "only yourself" restriction, which is imposed upon
+the set of all processes when some BSD\-style (without\ "\-") options
+are used or when the \fBps\fR personality setting is BSD\-like.
+The set of processes selected in this manner is
+in addition to the set of processes selected by other means.
+An alternate description is that this option causes \fBps\fR to
+list all processes with a terminal (tty),
+or to list all processes when used together with the \fBx\fR option.
+
+.opt \-A
+Select all processes. Identical to \fB\-e\fR.
+
+.opt \-a
+Select all processes except both session leaders (see \fIgetsid\fR(2)) and
+processes not associated with a terminal.
+
+.opt \-d
+Select all processes except session leaders.
+
+.opt \-\-deselect
+Select all processes except those that fulfill the specified conditions.
+(negates the selection) Identical to \fB\-N\fR.
+
+.opt \-e
+Select all processes. Identical to \fB\-A\fR.
+
+.\" Current "g" behavior: add in the session leaders, which would
+.\" be excluded in the sunos4 personality. Supposed "g" behavior:
+.\" add in the group leaders -- at least according to the SunOS 4
+.\" man page on the FreeBSD site. Uh oh. I think I had tested SunOS
+.\" though, so maybe the code is correct.
+
+.opt g
+Really all, even session leaders. This flag is obsolete and may be
+discontinued in a future release. It is normally implied by the \fBa\fR flag,
+and is only useful when operating in the sunos4 personality.
+
+.opt \-N
+Select all processes except those that fulfill the specified conditions.
+(negates the selection) Identical to \fB\-\-deselect\fR.
+
+.opt T
+Select all processes associated with this terminal. Identical to the
+\fBt\fR option without any argument.
+
+.opt r
+Restrict the selection to only running processes.
+
+.opt x
+Lift the BSD\-style "must have a tty" restriction, which is imposed upon
+the set of all processes when some BSD\-style (without\ "\-") options
+are used or when the \fBps\fR personality setting is BSD\-like.
+The set of processes selected in this manner is
+in addition to the set of processes selected by other means.
+An alternate description is that this option causes \fBps\fR to
+list all processes owned by you (same EUID as \fBps\fR),
+or to list all processes when used together with the \fBa\fR option.
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "PROCESS SELECTION BY LIST"
+These options accept a single argument in the form of a blank\-separated
+or comma\-separated list. They can be used multiple times.
+For\ example:\ \fBps\ \-p\ "1\ 2"\ \-p\ 3,4\fR
+.P
+
+.opt \-\fI123\fR
+Identical to \fB\-\-sid\ \fI123\fR.
+
+.opt \fI123\fR
+Identical to \fB\-\-pid\ \fI123\fR.
+
+.opt \-C \ cmdlist
+Select by command name.
+.br
+This selects the processes whose executable name is given in
+\fIcmdlist\fR.
+
+.opt \-G \ grplist
+Select by real group ID (RGID) or name.
+.br
+This selects the processes whose real group name or ID is in the
+\fIgrplist\fR list. The real group ID identifies the group of the user
+who created the process, see \fIgetgid\fR(2).
+
+.opt \-g \ grplist
+Select by session OR by effective group name.
+.br
+Selection by session is specified by many standards,
+but selection by effective group is the logical behavior that
+several other operating systems use.
+This \fBps\fR will select by session when the list
+is completely numeric (as\ sessions\ are).
+Group ID numbers will work only when some group names are also specified.
+See the \fB\-s\fR and \fB\-\-group\fR options.
+
+.opt \-\-Group \ grplist
+Select by real group ID (RGID) or name. Identical to \fB\-G\fR.
+
+.opt \-\-group \ grplist
+Select by effective group ID (EGID) or name.
+.br
+This selects the processes whose effective group name or ID is in
+\fIgrouplist\fR. The effective group ID describes the group whose file
+access permissions are used by the process (see\ \fIgeteuid\fR(2)).
+The \fB\-g\fR option is often an alternative to\ \fB\-\-group\fR.
+
+.opt p \ pidlist
+Select by process ID. Identical to \fB\-p\fR and\ \fB\-\-pid\fR.
+
+.opt \-p \ pidlist
+Select by PID.
+.br
+This selects the processes whose process ID numbers appear in
+\fIpidlist\fR. Identical to \fBp\fR and\ \fB\-\-pid\fR.
+
+.opt \-\-pid \ pidlist
+Select by process\ ID. Identical to \fB\-p\fR\ and\ \fBp\fR.
+
+.opt \-\-ppid \ pidlist
+Select by parent process\ ID.
+This selects the processes
+with a parent\ process\ ID in \fRpidlist\fR.
+That\ is, it selects processes that are children
+of those listed in \fRpidlist\fR.
+
+.opt \-s \ sesslist
+Select by session ID.
+.br
+This selects the processes
+with a session ID specified in\ \fIsesslist\fR.
+
+.opt \-\-sid \ sesslist
+Select by session\ ID. Identical to\ \fB\-s\fR.
+
+.opt t \ ttylist
+Select by tty. Nearly identical to \fB\-t\fR and \fB\-\-tty\fR,
+but can also be used with an empty \fIttylist\fR to indicate
+the terminal associated with \fBps\fR.
+Using the \fBT\fR option is considered cleaner than using \fBT\fR with
+an\ empty\ \fIttylist\fR.
+
+.opt \-t \ ttylist
+Select by tty.
+.br
+This selects the processes associated with the terminals
+given in \fIttylist\fR.
+Terminals (ttys, or screens for text output) can be specified in several
+forms: /dev/ttyS1, ttyS1, S1.
+A\ plain "\-" may be used to select processes not attached to any terminal.
+
+.opt \-\-tty \ ttylist
+Select by terminal. Identical to \fB\-t\fR and\ \fBt\fR.
+
+.opt U \ userlist
+Select by effective user ID (EUID) or name.
+.br
+This selects the processes whose effective user name
+or ID is in \fIuserlist\fR.
+The effective user\ ID describes the user whose file
+access permissions are used by the process
+(see\ \fIgeteuid\fR(2)).
+Identical to \fB\-u\fR and\ \fB\-\-user\fR.
+
+.opt \-U \ userlist
+select by real user ID (RUID) or name.
+.br
+It selects the processes whose real user name or ID is in the
+\fIuserlist\fR list.
+The real user ID identifies the user who created the process,
+see\ \fIgetuid\fR(2).
+
+.opt \-u \ userlist
+Select by effective user ID (EUID) or name.
+.br
+This selects the processes whose effective user name or ID is in
+\fIuserlist\fR. The effective user ID describes the user whose file
+access permissions are used by the process (see\ \fIgeteuid\fR(2)).
+Identical to \fBU\fR and \fB\-\-user\fR.
+
+.opt \-\-User \ userlist
+Select by real user ID (RUID) or name. Identical to \fB\-U\fR.
+
+.opt \-\-user \ userlist
+Select by effective user ID (EUID) or name.
+Identical to \fB\-u\fR and\ \fBU\fR.
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "OUTPUT FORMAT CONTROL"
+These options are used to choose the information displayed by \fBps\fR.
+The output may differ by personality.
+.PP
+
+.opt \-c
+Show different scheduler information for the \fB\-l\fR option.
+
+.opt \-\-context
+Display security context format. (for\ SE\ Linux)
+
+.opt \-f
+does full\-format listing. This option can be combined with many
+other UNIX\-style options to add additional columns. It also causes
+the command arguments to be printed. When used with \fB\-L\fR, the
+NLWP (number of threads) and LWP (thread ID) columns will be added.
+See the \fBc\fR option, the format keyword \fBargs\fR, and the
+format keyword \fBcomm\fR.
+
+.opt \-F
+extra full format. See the \fB\-f\fR option, which \fB\-F\fR implies.
+
+.opt \-\-format \ format
+user\-defined format. Identical to \fB\-o\fR and \fBo\fR.
+
+.opt j
+BSD job control format.
+
+.opt \-j
+jobs format
+
+.opt l
+display BSD long format.
+
+.opt \-l
+long format. The \fB\-y\fR option is often useful with this.
+
+.opt \-M
+Add a column of security data. Identical to \fBZ\fR. (for\ SE\ Linux)
+
+.opt O \ format
+is preloaded \fBo\fR (overloaded).
+.br
+The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
+format with some common fields predefined) or can be used to specify
+sort order. Heuristics are used to determine the behavior of this
+option. To ensure that the desired behavior is obtained (sorting or
+formatting), specify the option in some other way
+(e.g. with \fB\-O\fR or \fB\-\-sort\fR).
+When used as a formatting option, it is identical to \fB\-O\fR, with the
+BSD\ personality.
+
+.opt \-O \ format
+is like \fB\-o\fR, but preloaded with some default columns.
+Identical to \fB\-o\ pid,\fIformat\fB,state,tname,time,command\fR
+or \fB\-o\ pid,\fIformat\fB,tname,time,cmd\fR, see\ \fB\-o\fR\ below.
+
+.opt o \ format
+specify user\-defined format. Identical to \fB\-o\fR and
+\fB\-\-format\fR.
+
+.opt \-o \ format
+user\-defined format.
+.br
+\fIformat\fR is a single argument in the form of a
+blank\-separated or comma\-separated list, which offers
+a way to specify individual output columns.
+The recognized keywords are described in the \fBSTANDARD FORMAT
+SPECIFIERS\fR section below.
+Headers may be
+renamed (\fBps\ \-o\ pid,ruser=RealUser\ \-o\ comm=Command\fR) as desired.
+If all column headers are empty (\fBps\ \-o\ pid=\ \-o\ comm=\fR) then the
+header line will not be output. Column width will increase as
+needed for wide headers; this may be used to widen up columns
+such as WCHAN (\fBps\ \-o\ pid,wchan=WIDE\-WCHAN\-COLUMN\ \-o\ comm\fR).
+Explicit width control (\fBps\ opid,wchan:42,cmd\fR) is offered too.
+The behavior of \fBps\ \-o\ pid=X,comm=Y\fR varies with personality;
+output may be one column named "X,comm=Y" or two columns
+named "X" and "Y". Use multiple \fB\-o\fR options when in doubt.
+Use the \fBPS_FORMAT\fR environment variable to specify a default
+as desired; DefSysV and DefBSD are macros that may be used to
+choose the default UNIX or BSD columns.
+
+.opt s
+display signal format
+
+.opt u
+display user\-oriented format
+
+.opt v
+display virtual memory format
+
+.opt X
+Register format.
+
+.opt \-y
+Do not show flags; show rss in place of addr.
+This option can only be used with \fB\-l\fR.
+
+.opt Z
+Add a column of security data. Identical to \fB\-M\fR. (for\ SE\ Linux)
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "OUTPUT MODIFIERS"
+
+.\" .TP
+.\" .B C
+.\" use raw CPU time for %CPU instead of decaying average
+
+.opt c
+Show the true command name. This is derived from the name of the
+executable file, rather than from the argv value. Command arguments
+and any modifications to them are
+thus not shown. This option
+effectively turns the \fBargs\fR format keyword into the \fBcomm\fR
+format keyword; it is useful with the \fB\-f\fR format option and with
+the various BSD\-style format options, which all normally
+display the command arguments.
+See the \fB\-f\fR option, the format keyword \fBargs\fR, and the
+format keyword \fBcomm\fR.
+
+.opt \-\-cols \ n
+set screen width
+
+.opt \-\-columns \ n
+set screen width
+
+.opt \-\-cumulative
+include some dead child process data (as a sum with the parent)
+
+.opt e
+Show the environment after the command.
+
+.opt f
+ASCII\-art process hierarchy (forest)
+
+.opt \-\-forest
+ASCII art process tree
+
+.opt h
+No header. (or, one header per screen in the BSD personality)
+.br
+The \fBh\fR option is problematic. Standard BSD \fBps\fR uses
+this option to print a header on each page of output, but older
+Linux \fBps\fR uses this option to totally disable the header.
+This version of \fBps\fR follows the Linux usage of not printing
+the header unless the BSD personality has been selected, in which
+case it prints a header on each page of output. Regardless of the
+current personality, you can use the long options \fB\-\-headers\fR
+and \fB\-\-no\-headers\fR to enable printing headers each page or
+disable headers entirely, respectively.
+
+.opt \-H
+show process hierarchy (forest)
+
+.opt \-\-headers
+repeat header lines, one per page of output
+
+.opt k \ spec
+specify sorting order. Sorting syntax is
+[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]]
+Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section.
+The\ "+" is optional since default direction is increasing numerical or
+lexicographic order. Identical to \fB\-\-sort\fR. Examples:
+.br
+\fBps\ jaxkuid,\-ppid,+pid\fR
+.br
+\fBps\ axk\ comm\ o\ comm,args\fR
+.br
+\fBps\ kstart_time\ \-ef\fR
+
+.opt \-n \ namelist
+set namelist file. Identical to \fBN\fR.
+.br
+The namelist file is needed for a proper WCHAN display, and must match
+the current Linux kernel exactly for correct output.
+Without this option, the default search path for the namelist is:
+
+ $PS_SYSMAP
+.br
+ $PS_SYSTEM_MAP
+.br
+ /proc/*/wchan
+.br
+ /boot/System.map\-\`uname\ \-r\`
+.br
+ /boot/System.map
+.br
+ /lib/modules/\`uname\ \-r\`/System.map
+.br
+ /usr/src/linux/System.map
+.br
+ /System.map
+
+.opt \-\-lines \ n
+set screen height
+
+.opt n
+Numeric output for WCHAN and USER. (including all types of UID and GID)
+
+.opt N \ namelist
+Specify namelist file. Identical to \fB\-n\fR, see \fB\-n\fR above.
+
+.opt O \ order
+Sorting order. (overloaded)
+.br
+The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
+format with some common fields predefined) or can be used to specify
+sort order. Heuristics are used to determine the behavior of this
+option. To ensure that the desired behavior is obtained (sorting or
+formatting), specify the option in some other way (e.g. with \fB\-O\fR
+or \fB\-\-sort\fR).
+
+For sorting, obsolete BSD \fBO\fR option syntax is
+\fBO\fR[\fB+\fR|\fB\-\fR]\fIk1\fR[,[\fB+\fR|\fB\-\fR]\fIk2\fR[,...]].
+It orders the processes listing according to the multilevel sort specified by
+the sequence of one\-letter short keys \fIk1\fR, \fIk2\fR, ... described
+in the \fBOBSOLETE SORT KEYS\fR section below.
+The\ "+" is currently optional,
+merely re\-iterating the default direction on a key,
+but may help to distinguish an \fBO\fR sort from an \fBO\fR format.
+The\ "\-" reverses direction only on the key it precedes.
+
+.opt \-\-no\-headers
+print no header line at all. \-\-no\-heading is an alias for this
+option.
+
+.opt \-\-rows \ n
+set screen height
+
+.opt S
+Sum up some information, such as CPU usage, from dead child processes
+into their parent. This is useful for examining a system where a
+parent process repeatedly forks off short\-lived children to do work.
+
+.opt \-\-sort \ spec
+specify sorting order. Sorting syntax is
+[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]]
+Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section.
+The\ "+" is optional since default direction is increasing numerical or
+lexicographic order. Identical to\ \fBk\fR.
+For example: \fBps\ jax\ \-\-sort=uid,\-ppid,+pid\fR
+
+.opt w
+Wide output. Use this option twice for unlimited width.
+
+.opt \-w
+Wide output. Use this option twice for unlimited width.
+
+.opt \-\-width \ n
+set screen width
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "THREAD DISPLAY"
+.PD 0
+
+.opt H
+Show threads as if they were processes
+
+.opt \-L
+Show threads, possibly with LWP and NLWP columns
+
+.opt m
+Show threads after processes
+
+.opt \-m
+Show threads after processes
+
+.opt \-T
+Show threads, possibly with SPID column
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "OTHER INFORMATION"
+.PD 0
+
+.opt \-\-help
+Print a help message.
+
+.opt \-\-info
+Print debugging info.
+
+.opt L
+List all format specifiers.
+
+.opt V
+Print the procps version.
+
+.opt \-V
+Print the procps version.
+
+.opt \-\-version
+Print the procps version.
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH NOTES
+This \fBps\fR works by reading the virtual files in\ /proc.
+This \fBps\fR does not need to be setuid kmem or have any privileges to run.
+Do not give this \fBps\fR any special permissions.
+
+This \fBps\fR needs access to namelist data for proper WCHAN display.
+For kernels prior to 2.6, the System.map file must be installed.
+
+CPU usage is currently expressed as the percentage of time spent
+running during the entire lifetime of a process.
+This is not ideal, and\ it does not conform to the
+standards that \fBps\fR otherwise conforms\ to.
+CPU\ usage is unlikely to add up to exactly\ 100%.
+
+The SIZE and RSS fields don't count some parts of a process including the
+page tables, kernel stack, struct thread_info, and struct task_struct.
+This is usually at least 20\ KiB of memory that is always resident.
+SIZE is the virtual size of the process (code+data+stack).
+
+Processes marked <defunct> are dead processes (so\-called\ "zombies") that
+remain because their parent has not destroyed them properly. These processes
+will be destroyed by \fIinit\fR(8) if the parent process exits.
+
+
+.SH "PROCESS FLAGS"
+The sum of these values is displayed in the "F" column,
+which is provided by the \fBflags\fR output specifier.
+.PD 0
+.TP 5
+1
+forked but didn't exec
+.TP
+4
+used super\-user privileges
+.PD
+.PP
+.SH "PROCESS STATE CODES"
+Here are the different values that the \fBs\fR, \fBstat\fR and
+\fBstate\fR output specifiers (header\ "STAT"\ or\ "S") will display to
+describe the state of a process.
+.PD 0
+.TP 5
+D
+Uninterruptible sleep (usually\ IO)
+.TP
+R
+Running or runnable (on\ run\ queue)
+.TP
+S
+Interruptible sleep (waiting for an event to complete)
+.TP
+T
+Stopped, either by a job control signal or because it is being traced.
+.TP
+W
+paging (not valid since the 2.6.xx kernel)
+.TP
+X
+dead (should never be seen)
+.TP
+Z
+Defunct ("zombie") process, terminated but not reaped by its parent.
+.PD
+.PP
+For BSD formats and when the \fBstat\fR keyword is used, additional
+characters may be displayed:
+.PD 0
+.TP 5
+<
+high\-priority (not nice to other users)
+.TP
+N
+low\-priority (nice to other users)
+.TP
+L
+has pages locked into memory (for real\-time and custom\ IO)
+.TP
+s
+is a session leader
+.TP
+l
+is multi-threaded (using CLONE_THREAD, like NPTL pthreads\ do)
+.TP
++
+is in the foreground process group
+.PD
+.PP
+.PP
+.SH "OBSOLETE SORT KEYS"
+These keys are used by the BSD \fBO\fR option (when it is used for
+sorting). The GNU \fB\-\-sort\fR option doesn't use these keys, but the
+specifiers described below in the \fBSTANDARD FORMAT SPECIFIERS\fR
+section. Note that the values used in sorting are the internal
+values \fBps\fR uses and not the "cooked" values used in some of
+the output format fields (e.g. sorting on tty will sort into
+device number, not according to the terminal name displayed).
+Pipe \fBps\fR output into the \fIsort\fR(1) command if you want
+to sort the cooked values.
+
+.TS
+l l lw(3i).
+\fBKEY LONG DESCRIPTION\fR
+c cmd simple name of executable
+C pcpu cpu utilization
+f flags flags as in long format F field
+g pgrp process group ID
+G tpgid controlling tty process group ID
+j cutime cumulative user time
+J cstime cumulative system time
+k utime user time
+m min_flt number of minor page faults
+M maj_flt number of major page faults
+n cmin_flt cumulative minor page faults
+N cmaj_flt cumulative major page faults
+o session session ID
+p pid process ID
+P ppid parent process ID
+r rss resident set size
+R resident resident pages
+s size memory size in kilobytes
+S share amount of shared pages
+t tty the device number of the controlling tty
+T start_time time process was started
+U uid user ID number
+u user user name
+v vsize total VM size in kB
+y priority kernel scheduling priority
+.\"K stime system time (conflict, system vs. start time)
+.TE
+.PP
+.PP
+.SH "AIX FORMAT DESCRIPTORS"
+This \fBps\fR supports AIX format descriptors, which work somewhat like the
+formatting codes of \fIprintf\fR(1) and \fIprintf\fR(3). For example, the normal
+default output can be produced with this: \fBps\ \-eo\ "%p\ %y\ %x\ %c"\fR.
+The\ \fBNORMAL\fR codes are described in the next section.
+.TS
+l l l.
+\fBCODE NORMAL HEADER\fR
+%C pcpu %CPU
+%G group GROUP
+%P ppid PPID
+%U user USER
+%a args COMMAND
+%c comm COMMAND
+%g rgroup RGROUP
+%n nice NI
+%p pid PID
+%r pgid PGID
+%t etime ELAPSED
+%u ruser RUSER
+%x time TIME
+%y tty TTY
+%z vsz VSZ
+.TE
+
+.SH "STANDARD FORMAT SPECIFIERS"
+Here are the different keywords that may be used to control the output
+format (e.g. with option \fB\-o\fR) or to sort the selected processes
+with the GNU\-style \fB\-\-sort\fR option.
+
+For example: \fBps\ \-eo\ pid,user,args\ \-\-sort\ user\fR
+
+This version of \fBps\fR tries to recognize most of the keywords used in
+other implementations of \fBps\fR.
+
+The following user\-defined format specifiers may contain
+spaces: \fBargs\fR, \fBcmd\fR, \fBcomm\fR, \fBcommand\fR, \fBfname\fR,
+\fBucmd\fR, \fBucomm\fR,
+\fBlstart\fR, \fBbsdstart\fR, \fBstart\fR.
+
+Some keywords may not be available for sorting.
+
+.\" #######################################################################
+.\" lB1 lB1 lB1 lB1 s s s
+.\" lB1 l1 l1 l1 s s s.
+.\"
+.\" lB1 lB1 lBw(5.5i)
+.\" lB1 l1 l.
+.\"
+.TS
+expand;
+lB1 lB1 lBw(\n[ColSize]n)
+lB1 l1 l.
+CODE HEADER DESCRIPTION
+
+%cpu %CPU T{
+cpu utilization of the process in "##.#" format. Currently, it is the CPU time
+used divided by the time the process has been running (cputime/realtime
+ratio), expressed as a percentage. It will not add up to 100% unless you
+are lucky. (alias\ \fBpcpu\fR).
+T}
+
+%mem %MEM T{
+ratio of the process's resident set size to the physical memory on
+the machine, expressed as a percentage. (alias\ \fBpmem\fR).
+T}
+
+args COMMAND T{
+command with all its arguments as a string. Modifications to the arguments
+may be shown. The output in this column may contain spaces.
+A\ process marked <defunct> is partly dead, waiting
+to be fully destroyed by its parent. Sometimes the process args
+will be unavailable; when this happens, \fBps\fR will instead
+print the executable name in brackets.
+(alias\ \fBcmd\fR,\ \fBcommand\fR). See also the \fBcomm\fR format
+keyword, the \fB\-f\fR option, and the \fBc\fR option.
+.br
+When specified last, this column will extend to the edge of the display.
+If \fBps\fR can not determine display width, as when output is redirected
+(piped) into a file or another command, the output width is undefined.
+(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on)
+The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may
+be used to exactly determine the width in this case.
+The \fBw\fR or \fB\-w\fR option may be also be used to adjust width.
+T}
+
+blocked BLOCKED T{
+mask of the blocked signals, see \fIsignal\fR(7).
+According to the width of the field,
+a\ 32\-bit or 64\-bit mask in hexadecimal format is displayed.
+(alias\ \fBsig_block\fR,\ \fBsigmask\fR).
+T}
+
+bsdstart START T{
+time the command started. If the process was started less
+than 24 hours ago, the output format is "\ HH:MM",
+else it is "mmm\ dd"
+(where mmm is the three letters of the month).
+See also \fBlstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR.
+T}
+
+bsdtime TIME T{
+accumulated cpu time, user\ +\ system. The display format is usually
+"MMM:SS", but can be shifted to the right if the process used more than 999
+minutes of cpu time.
+T}
+
+c C T{
+processor utilization. Currently, this is the integer value of
+the percent usage over the lifetime of the process. (see\ \fB%cpu\fR).
+T}
+
+caught CAUGHT T{
+mask of the caught signals, see \fIsignal\fR(7). According to the
+width of the field, a 32 or 64 bits mask in hexadecimal format is
+displayed. (alias\ \fBsig_catch\fR,\ \fBsigcatch\fR).
+T}
+
+class CLS T{
+scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBcls\fR).
+Field's possible values are:
+.br
+\- not reported
+.br
+TS SCHED_OTHER
+.br
+FF SCHED_FIFO
+.br
+RR SCHED_RR
+.br
+B SCHED_BATCH
+.br
+ISO SCHED_ISO
+.br
+IDL SCHED_IDLE
+.br
+? unknown value
+T}
+
+cls CLS T{
+scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBclass\fR).
+Field's possible values are:
+.br
+\- not reported
+.br
+TS SCHED_OTHER
+.br
+FF SCHED_FIFO
+.br
+RR SCHED_RR
+.br
+B SCHED_BATCH
+.br
+ISO SCHED_ISO
+.br
+IDL SCHED_IDLE
+.br
+? unknown value
+T}
+
+cmd CMD T{
+see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcommand\fR).
+T}
+
+comm COMMAND T{
+command name (only\ the executable\ name). Modifications to the command
+name will not be shown. A\ process marked <defunct> is partly dead, waiting
+to be fully destroyed by its parent. The output in this
+column may contain spaces. (alias\ \fBucmd\fR,\ \fBucomm\fR).
+See also the \fBargs\fR format
+keyword, the \fB\-f\fR option, and the \fBc\fR option.
+.br
+When specified last, this column will extend to the edge of the display.
+If \fBps\fR can not determine display width, as when output is redirected
+(piped) into a file or another command, the output width is undefined.
+(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on)
+The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may
+be used to exactly determine the width in this case.
+The \fBw\fR or \fB\-w\fR option may be also be used to adjust width.
+T}
+
+command COMMAND T{
+see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcmd\fR).
+T}
+
+cp CP T{
+per\-mill (tenths of a percent) CPU usage. (see\ \fB%cpu\fR).
+T}
+
+cputime TIME T{
+cumulative CPU time, "[dd\-]hh:mm:ss" format. (alias\ \fBtime\fR).
+T}
+
+egid EGID T{
+effective group ID number of the process as a decimal integer.
+(alias\ \fBgid\fR).
+T}
+
+egroup EGROUP T{
+effective group ID of the process. This will be the textual group ID,
+if it can be obtained and the field width permits, or a decimal
+representation otherwise. (alias\ \fBgroup\fR).
+T}
+
+eip EIP T{
+instruction pointer.
+T}
+
+esp ESP T{
+stack pointer.
+T}
+
+etime ELAPSED T{
+elapsed time since the process was started,
+in\ the form\ [[dd\-]hh:]mm:ss.
+T}
+
+euid EUID T{
+effective user\ ID. (alias\ \fBuid\fR).
+T}
+
+euser EUSER T{
+effective user\ name. This will be the textual
+user\ ID, if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+The\ \fBn\fR\ option can be used
+to force the decimal representation.
+(alias\ \fBuname\fR,\ \fBuser\fR).
+T}
+
+f F T{
+flags associated with the process, see the \fBPROCESS FLAGS\fR section.
+(alias\ \fBflag\fR,\ \fBflags\fR).
+T}
+
+fgid FGID T{
+filesystem access group\ ID. (alias\ \fBfsgid\fR).
+T}
+
+fgroup FGROUP T{
+filesystem access group\ ID.
+This will be the textual user\ ID, if\ it can be obtained
+and the field width permits,
+or\ a\ decimal representation otherwise.
+(alias\ \fBfsgroup\fR).
+T}
+
+flag F T{
+see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflags\fR).
+T}
+
+flags F T{
+see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflag\fR).
+T}
+
+fname COMMAND T{
+first 8 bytes of the base name of the process's executable file.
+The output in this column may contain spaces.
+T}
+
+fuid FUID T{
+filesystem access user\ ID. (alias\ \fBfsuid\fR).
+T}
+
+fuser FUSER T{
+filesystem access user\ ID. This will be the textual user\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+gid GID T{
+see\ \fBegid\fR. (alias\ \fBegid\fR).
+T}
+
+group GROUP T{
+see\ \fBegroup\fR. (alias\ \fBegroup\fR).
+T}
+
+ignored IGNORED T{
+mask of the ignored signals, see \fIsignal\fR(7). According to the
+width of the field, a\ 32\-bit or 64\-bit mask in hexadecimal format
+is displayed. (alias \fBsig_ignore\fR, \fBsigignore\fR).
+T}
+
+label LABEL T{
+security label, most commonly used for SE\ Linux context data.
+This is for the \fIMandatory Access Control\fR ("MAC") found on
+high\-security systems.
+T}
+
+lstart STARTED T{
+time the command started.
+See also \fBbsdstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR.
+T}
+
+lwp LWP T{
+lwp (light weight process, or thread) ID of the lwp being reported.
+(alias\ \fBspid\fR,\ \fBtid\fR).
+T}
+
+maj_flt MAJFLT T{
+The number of major page faults that have occured with this process.
+T}
+
+min_flt MINFLT T{
+The number of minor page faults that have occured with this process.
+T}
+
+ni NI T{
+nice value. This ranges from 19 (nicest) to \-20 (not\ nice to\ others),
+see\ \fInice\fR(1). (alias\ \fBnice\fR).
+T}
+
+nice NI T{
+see\ \fBni\fR. (alias\ \fBni\fR).
+T}
+
+nlwp NLWP T{
+number of lwps (threads) in the process. (alias\ \fBthcount\fR).
+T}
+
+nwchan WCHAN T{
+address of the kernel function where the process is sleeping
+(use \fBwchan\fR if you want the kernel function name).
+Running tasks will display a dash ('\-') in this column.
+T}
+
+pcpu %CPU T{
+see\ \fB%cpu\fR. (alias\ \fB%cpu\fR).
+T}
+
+pending PENDING T{
+mask of the pending signals. See\ \fIsignal\fR(7). Signals pending on
+the process are distinct from signals pending on individual threads.
+Use the \fBm\fR option or the \fB\-m\fR option to see both.
+According to the width of the field, a\ 32\-bit or 64\-bit mask in
+hexadecimal format is displayed. (alias\ \fBsig\fR).
+T}
+
+pgid PGID T{
+process group\ ID or, equivalently, the process\ ID of the
+process group leader. (alias\ \fBpgrp\fR).
+T}
+
+pgrp PGRP T{
+see\ \fBpgid\fR. (alias\ \fBpgid\fR).
+T}
+
+pid PID T{
+process\ ID number of the process.
+T}
+
+pmem %MEM T{
+see\ \fB%mem\fR. (alias\ \fB%mem\fR).
+T}
+
+policy POL T{
+scheduling class of the process. (alias\ \fBclass\fR,\ \fBcls\fR).
+Possible values are:
+.br
+\- not reported
+.br
+TS SCHED_OTHER
+.br
+FF SCHED_FIFO
+.br
+RR SCHED_RR
+.br
+B SCHED_BATCH
+.br
+ISO SCHED_ISO
+.br
+IDL SCHED_IDLE
+.br
+? unknown value
+T}
+
+ppid PPID T{
+parent process ID.
+T}
+
+pri PRI T{
+priority of the process. Higher number means lower priority
+T}
+
+psr PSR T{
+processor that process is currently assigned to.
+T}
+
+rgid RGID T{
+real group ID.
+T}
+
+rgroup RGROUP T{
+real group name. This will be the textual group\ ID, if\ it can be
+obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+rss RSS T{
+resident set size, the non\-swapped physical memory that
+a task has used (in\ kiloBytes).
+(alias\ \fBrssize\fR,\ \fBrsz\fR).
+T}
+
+rssize RSS T{
+see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrsz\fR).
+T}
+
+rsz RSZ T{
+see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrssize\fR).
+T}
+
+rtprio RTPRIO T{
+realtime priority.
+T}
+
+ruid RUID T{
+real user\ ID.
+T}
+
+ruser RUSER T{
+real user\ ID. This will be the textual user\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+s S T{
+minimal state display (one\ character).
+See\ section \fBPROCESS STATE CODES\fR for the different values.
+See\ also \fBstat\fR if you want additional
+information displayed. (alias\ \fBstate\fR).
+T}
+
+sched SCH T{
+scheduling policy of the process. The policies SCHED_OTHER (SCHED_NORMAL),
+SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_ISO, and SCHED_IDLE are respectively
+displayed as 0,\ 1,\ 2,\ 3,\ 4,\ and\ 5.
+T}
+
+sess SESS T{
+session\ ID or, equivalently, the process\ ID of the session\ leader.
+(alias\ \fBsession\fR,\ \fBsid\fR).
+T}
+
+sgi_p P T{
+processor that the process is currently executing on.
+Displays "*" if the process is not currently running or runnable.
+T}
+
+sgid SGID T{
+saved group\ ID.
+(alias\ \fBsvgid\fR).
+T}
+
+sgroup SGROUP T{
+saved group\ name. This will be the textual group\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+sid SID T{
+see\ \fBsess\fR. (alias\ \fBsess\fR,\ \fBsession\fR).
+T}
+
+sig PENDING T{
+see\ \fBpending\fR. (alias\ \fBpending\fR,\ \fBsig_pend\fR).
+T}
+
+sigcatch CAUGHT T{
+see\ \fBcaught\fR. (alias\ \fBcaught\fR,\ \fBsig_catch\fR).
+T}
+
+sigignore IGNORED T{
+see\ \fBignored\fR. (alias\ \fBignored\fR,\ \fBsig_ignore\fR).
+T}
+
+sigmask BLOCKED T{
+see\ \fBblocked\fR. (alias\ \fBblocked\fR,\ \fBsig_block\fR).
+T}
+
+size SIZE T{
+approximate amount of swap space that would be required
+if the process were to dirty all writable pages and then
+be swapped out.
+This number is very\ rough!
+T}
+
+spid SPID T{
+see \fBlwp\fR. (alias\ \fBlwp\fR,\ \fBtid\fR).
+T}
+
+stackp STACKP T{
+address of the bottom (start) of stack for the process.
+T}
+
+start STARTED T{
+time the command started.
+If the process was started less than 24 hours ago,
+the output format is "HH:MM:SS",
+else it is "\ \ mmm\ dd"
+(where mmm is a three\-letter month\ name).
+See also \fBlstart\fR, \fBbsdstart\fR, \fBstart_time\fR, and \fBstime\fR.
+T}
+
+start_time START T{
+starting time or date of the process.
+Only the year will be displayed if the process was not
+started the same year \fBps\fR was invoked,
+or\ "mmmdd" if\ it was not started the same day,
+or\ "HH:MM" otherwise.
+See also \fBbsdstart\fR, \fBstart\fR, \fBlstart\fR, and \fBstime\fR.
+T}
+
+stat STAT T{
+multi\-character process state.
+See\ section \fBPROCESS STATE CODES\fR
+for the different values meaning.
+See also \fBs\fR and \fBstate\fR if you just want
+the first character displayed.
+T}
+
+state S T{
+see\ \fBs\fR. (alias\ \fBs\fR).
+T}
+
+suid SUID T{
+saved user\ ID. (alias\ \fBsvuid\fR).
+T}
+
+supgid SUPGID T{
+gid of supplementary groups, see
+.BR getgroups (2).
+T}
+
+supgrp SUPGRP T{
+names of supplementary groups, see
+.BR getgroups (2).
+T}
+
+suser SUSER T{
+saved user name. This will be the textual user\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+(alias\ \fBsvuser\fR).
+T}
+
+svgid SVGID T{
+see\ \fBsgid\fR. (alias\ \fBsgid\fR).
+T}
+
+svuid SVUID T{
+see\ \fBsuid\fR. (alias\ \fBsuid\fR).
+T}
+
+sz SZ T{
+size in physical pages of the core image of the process.
+This includes text, data, and stack space.
+Device mappings are currently excluded; this is subject to change.
+See \fBvsz\fR and \fBrss\fR.
+T}
+
+thcount THCNT T{
+see \fBnlwp\fR. (alias\ \fBnlwp\fR).
+number of kernel threads owned by the process.
+T}
+
+tid TID T{
+see\ \fBlwp\fR. (alias\ \fBlwp\fR).
+T}
+
+time TIME T{
+cumulative CPU\ time, "[dd\-]hh:mm:ss" format. (alias\ \fBcputime\fR).
+T}
+
+tname TTY T{
+controlling tty (terminal).
+(alias\ \fBtt\fR,\ \fBtty\fR).
+T}
+
+tpgid TPGID T{
+ID of the foreground process group on the tty (terminal) that
+the process is connected to, or \-1 if the process is not connected
+to a tty.
+T}
+
+tt TT T{
+controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtty\fR).
+T}
+
+tty TT T{
+controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtt\fR).
+T}
+
+ucmd CMD T{
+see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucomm\fR).
+T}
+
+ucomm COMMAND T{
+see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucmd\fR).
+T}
+
+uid UID T{
+see \fBeuid\fR. (alias\ \fBeuid\fR).
+T}
+
+uname USER T{
+see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuser\fR).
+T}
+
+user USER T{
+see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuname\fR).
+T}
+
+vsize VSZ T{
+see \fBvsz\fR. (alias\ \fBvsz\fR).
+T}
+
+vsz VSZ T{
+virtual memory size of the process in KiB (1024\-byte\ units).
+Device mappings are currently excluded; this is subject to change.
+(alias\ \fBvsize\fR).
+T}
+
+wchan WCHAN T{
+name of the kernel function in which the process is sleeping,
+a\ "\-"\ if the process is running,
+or a "*"\ if the process is multi\-threaded and
+\fBps\fR is not displaying threads.
+T}
+.TE
+.\" #######################################################################
+.PP
+.PP
+.SH "ENVIRONMENT VARIABLES"
+The following environment variables could affect \fBps\fR:
+.TP 3
+.B COLUMNS
+Override default display width.
+.TP
+.B LINES
+Override default display height.
+.TP
+.B PS_PERSONALITY
+Set to one of posix, old, linux, bsd, sun, digital...
+(see\ section\ \fBPERSONALITY\fR\ below).
+.TP
+.B CMD_ENV
+Set to one of posix, old, linux, bsd, sun, digital...
+(see\ section\ \fBPERSONALITY\fR\ below).
+.TP
+.B I_WANT_A_BROKEN_PS
+Force obsolete command line interpretation.
+.TP
+.B LC_TIME
+Date format.
+.TP
+.B PS_COLORS
+Not currently supported.
+.TP
+.B PS_FORMAT
+Default output format override. You may set this to a format
+string of the type used for the \fB\-o\fR option.
+The \fBDefSysV\fR and \fBDefBSD\fR values are particularly useful.
+.TP
+.B PS_SYSMAP
+Default namelist (System.map) location.
+.TP
+.B PS_SYSTEM_MAP
+Default namelist (System.map) location.
+.TP
+.B POSIXLY_CORRECT
+Don't find excuses to ignore bad "features".
+.TP
+.B POSIX2
+When set to "on", acts as \fBPOSIXLY_CORRECT\fR.
+.TP
+.B UNIX95
+Don't find excuses to ignore bad "features".
+.TP
+.B _XPG
+Cancel \fBCMD_ENV\fI=irix\fR non\-standard behavior.
+.PP
+In general, it\ is a bad idea to set these variables.
+The one exception is \fBCMD_ENV\fR or \fBPS_PERSONALITY\fR,
+which could be set to Linux for normal systems.
+Without that setting,
+\fBps\fR follows the useless and bad parts of the Unix98 standard.
+.PP
+.PP
+.SH "PERSONALITY"
+.TS
+l l.
+390 like the S/390 OpenEdition \fBps\fR
+aix like AIX \fBps\fR
+bsd like FreeBSD \fBps\fR (totally\ non\-standard)
+compaq like Digital Unix \fBps\fR
+debian like the old Debian \fBps\fR
+digital like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR
+gnu like the old Debian \fBps\fR
+hp like HP\-UX \fBps\fR
+hpux like HP\-UX \fBps\fR
+irix like Irix \fBps\fR
+linux ***** RECOMMENDED *****
+old like the original Linux \fBps\fR (totally\ non\-standard)
+os390 like OS/390 Open Edition \fBps\fR
+posix standard
+s390 like OS/390 Open Edition \fBps\fR
+sco like SCO \fBps\fR
+sgi like Irix \fBps\fR
+solaris2 like Solaris 2+ (SunOS 5) \fBps\fR
+sunos4 like SunOS 4 (Solaris 1) \fBps\fR (totally\ non\-standard)
+svr4 standard
+sysv standard
+tru64 like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR
+unix standard
+unix95 standard
+unix98 standard
+.TE
+.PP
+.PP
+.SH "SEE ALSO"
+\fItop\fR(1), \fIpgrep\fR(1), \fIpstree\fR(1), \fIproc\fR(5).
+.PP
+.PP
+.SH STANDARDS
+This \fBps\fR conforms to:
+.PP
+.PD 0
+.IP 1 4
+Version 2 of the Single Unix Specification
+.IP 2 4
+The Open Group Technical Standard Base Specifications, Issue\ 6
+.IP 3 4
+IEEE Std 1003.1, 2004\ Edition
+.IP 4 4
+X/Open System Interfaces Extension [UP\ XSI]
+.IP 5 4
+ISO/IEC 9945:2003
+.PD
+.PP
+.SH AUTHOR
+\fBps\fR was originally written by Branko Lankester <lankeste@fwi.uva.nl>. Michael
+K. Johnson <johnsonm@redhat.com> re\-wrote it significantly to use the proc
+filesystem, changing a few things in the process. Michael Shields
+<mjshield@nyx.cs.du.edu> added the pid\-list feature. Charles Blake
+<cblake@bbn.com> added multi\-level sorting, the dirent\-style library, the
+device name\-to\-number mmaped database, the approximate binary search
+directly on System.map, and many code and documentation cleanups. David
+Mossberger\-Tang wrote the generic BFD support for psupdate. Albert Cahalan
+<albert@users.sf.net> rewrote ps for full Unix98 and BSD support, along with
+some ugly hacks for obsolete and foreign syntax.
+
+Please send bug reports to <procps\-feedback@lists.sf.net>.
+No\ subscription is required or suggested.
diff --git a/smartt-top/ps/regression b/smartt-top/ps/regression
new file mode 100644
index 0000000..c71c826
--- /dev/null
+++ b/smartt-top/ps/regression
@@ -0,0 +1,26 @@
+-u 500 -o pid,ppid,fname,comm,args # right margin trouble
+-u 500 -o pid,ppid,fname,comm,args,wchan,wchan,wchan,wchan,wchan,nice,wchan
+-u 500 -o pid,pid,pid,pid,user,user,user,args # had trouble
+-u 500 -o user,user,user,pid,pid,pid,pid,args # no trouble!
+
+Test with each type of field (RIGHT,LEFT,UNLIMITED...) hanging off the
+edge of the screen and each type of field to the left of the one that
+hangs off the edge.
+
+Test "ps ef" as _both_ normal user and root. Especially after su!
+
+On a 108-col screen, try "ps alx" and "ps alx | cat"
+
+These ought to be the same:
+CMD_ENV=old ps -m
+CMD_ENV=old ps m
+
+These ought to be the same:
+CMD_ENV=old ps -X
+CMD_ENV=old ps X
+ps X
+ps -X # needs to be a non-SysV option
+
+This should fail:
+ps x -x
+
diff --git a/smartt-top/ps/select.c b/smartt-top/ps/select.c
new file mode 100644
index 0000000..2c52d02
--- /dev/null
+++ b/smartt-top/ps/select.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "../proc/readproc.h"
+#include "../proc/procps.h"
+
+//#define process_group_leader(p) ((p)->pgid == (p)->tgid)
+//#define some_other_user(p) ((p)->euid != cached_euid)
+#define has_our_euid(p) ((unsigned)(p)->euid == (unsigned)cached_euid)
+#define on_our_tty(p) ((unsigned)(p)->tty == (unsigned)cached_tty)
+#define running(p) (((p)->state=='R')||((p)->state=='D'))
+#define session_leader(p) ((p)->session == (p)->tgid)
+#define without_a_tty(p) (!(p)->tty)
+
+static unsigned long select_bits = 0;
+
+/***** prepare select_bits for use */
+const char *select_bits_setup(void){
+ int switch_val = 0;
+ /* don't want a 'g' screwing up simple_select */
+ if(!simple_select && !prefer_bsd_defaults){
+ select_bits = 0xaa00; /* the STANDARD selection */
+ return NULL;
+ }
+ /* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */
+ if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) )
+ switch_val = simple_select|SS_B_g;
+ else
+ switch_val = simple_select;
+ switch(switch_val){
+ /* UNIX options */
+ case SS_U_a | SS_U_d: select_bits = 0x3f3f; break; /* 3333 or 3f3f */
+ case SS_U_a: select_bits = 0x0303; break; /* 0303 or 0f0f */
+ case SS_U_d: select_bits = 0x3333; break;
+ /* SunOS 4 only (others have 'g' enabled all the time) */
+ case 0: select_bits = 0x0202; break;
+ case SS_B_a: select_bits = 0x0303; break;
+ case SS_B_x : select_bits = 0x2222; break;
+ case SS_B_x | SS_B_a: select_bits = 0x3333; break;
+ /* General BSD options */
+ case SS_B_g : select_bits = 0x0a0a; break;
+ case SS_B_g | SS_B_a: select_bits = 0x0f0f; break;
+ case SS_B_g | SS_B_x : select_bits = 0xaaaa; break;
+ case SS_B_g | SS_B_x | SS_B_a: /* convert to -e instead of using 0xffff */
+ all_processes = 1;
+ simple_select = 0;
+ break;
+ default:
+ return "Process selection options conflict.";
+ break;
+ }
+ return NULL;
+}
+
+/***** selected by simple option? */
+static int table_accept(proc_t *buf){
+ unsigned proc_index;
+ proc_index = (has_our_euid(buf) <<0)
+ | (session_leader(buf) <<1)
+ | (without_a_tty(buf) <<2)
+ | (on_our_tty(buf) <<3);
+ return (select_bits & (1<<proc_index));
+}
+
+/***** selected by some kind of list? */
+static int proc_was_listed(proc_t *buf){
+ selection_node *sn = selection_list;
+ int i;
+ if(!sn) return 0;
+ while(sn){
+ switch(sn->typecode){
+ default:
+ printf("Internal error in ps! Please report this bug.\n");
+
+#define return_if_match(foo,bar) \
+ i=sn->n; while(i--) \
+ if((unsigned)(buf->foo) == (unsigned)(*(sn->u+i)).bar) \
+ return 1
+
+ break; case SEL_RUID: return_if_match(ruid,uid);
+ break; case SEL_EUID: return_if_match(euid,uid);
+ break; case SEL_SUID: return_if_match(suid,uid);
+ break; case SEL_FUID: return_if_match(fuid,uid);
+
+ break; case SEL_RGID: return_if_match(rgid,gid);
+ break; case SEL_EGID: return_if_match(egid,gid);
+ break; case SEL_SGID: return_if_match(sgid,gid);
+ break; case SEL_FGID: return_if_match(fgid,gid);
+
+ break; case SEL_PGRP: return_if_match(pgrp,pid);
+ break; case SEL_PID : return_if_match(tgid,pid);
+ break; case SEL_PPID: return_if_match(ppid,ppid);
+ break; case SEL_TTY : return_if_match(tty,tty);
+ break; case SEL_SESS: return_if_match(session,pid);
+
+ break; case SEL_COMM: i=sn->n; while(i--)
+ if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 15 )) return 1;
+
+
+
+#undef return_if_match
+
+ }
+ sn = sn->next;
+ }
+ return 0;
+}
+
+
+/***** This must satisfy Unix98 and as much BSD as possible */
+int want_this_proc(proc_t *buf){
+ int accepted_proc = 1; /* assume success */
+ /* elsewhere, convert T to list, U sets x implicitly */
+
+ /* handle -e -A */
+ if(all_processes) goto finish;
+
+ /* use table for -a a d g x */
+ if((simple_select || !selection_list))
+ if(table_accept(buf)) goto finish;
+
+ /* search lists */
+ if(proc_was_listed(buf)) goto finish;
+
+ /* fail, fall through to loose ends */
+ accepted_proc = 0;
+
+ /* do r N */
+finish:
+ if(running_only && !running(buf)) accepted_proc = 0;
+ if(negate_selection) return !accepted_proc;
+ return accepted_proc;
+}
diff --git a/smartt-top/ps/sortformat.c b/smartt-top/ps/sortformat.c
new file mode 100644
index 0000000..80427e5
--- /dev/null
+++ b/smartt-top/ps/sortformat.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright 1998-2004 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "common.h"
+
+static sf_node *sf_list = NULL; /* deferred sorting and formatting */
+static int broken; /* use gross Unix98 parsing? */
+static int have_gnu_sort = 0; /* if true, "O" must be format */
+static int already_parsed_sort = 0; /* redundantly set in & out of fn */
+static int already_parsed_format = 0;
+
+
+/**************** Parse single format specifier *******************/
+static format_node *do_one_spec(const char *spec, const char *override){
+ const format_struct *fs;
+ const macro_struct *ms;
+
+ fs = search_format_array(spec);
+ if(fs){
+ int w1, w2;
+ format_node *thisnode;
+ thisnode = malloc(sizeof(format_node));
+ if(fs->flags & CF_PIDMAX){
+ w1 = (int)get_pid_digits();
+ w2 = strlen(fs->head);
+ if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing
+ }else{
+ w1 = fs->width;
+ }
+ if(override){
+ w2 = strlen(override);
+ thisnode->width = (w1>w2)?w1:w2;
+ thisnode->name = malloc(strlen(override)+1);
+ strcpy(thisnode->name, override);
+ }else{
+ thisnode->width = w1;
+ thisnode->name = malloc(strlen(fs->head)+1);
+ strcpy(thisnode->name, fs->head);
+ }
+ thisnode->pr = fs->pr;
+ thisnode->need = fs->need;
+ thisnode->vendor = fs->vendor;
+ thisnode->flags = fs->flags;
+ thisnode->next = NULL;
+ return thisnode;
+ }
+
+ /* That failed, so try it as a macro. */
+ ms = search_macro_array(spec);
+ if(ms){
+ format_node *list = NULL;
+ format_node *newnode;
+ const char *walk;
+ int dist;
+ char buf[16]; /* trust strings will be short (from above, not user) */
+ walk = ms->head;
+ while(*walk){
+ dist = strcspn(walk, ", ");
+ strncpy(buf,walk,dist);
+ buf[dist] = '\0';
+ newnode = do_one_spec(buf,override); /* call self, assume success */
+ newnode->next = list;
+ list = newnode;
+ walk += dist;
+ if(*walk) walk++;
+ }
+ return list;
+ }
+ return NULL; /* bad, spec not found */
+}
+
+
+/************ must wrap user format in default *************/
+static void O_wrap(sf_node *sfn, int otype){
+ format_node *fnode;
+ format_node *endp;
+ const char *trailer;
+
+ trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
+
+ fnode = do_one_spec("pid",NULL);
+ if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
+ endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */
+ endp->next = fnode;
+
+ fnode = do_one_spec(trailer,NULL);
+ if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
+ endp = fnode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->f_cooked;
+ sfn->f_cooked = fnode;
+}
+
+/******************************************************************
+ * Used to parse option AIX field descriptors.
+ * Put each completed format_node onto the list starting at ->f_cooked
+ */
+static const char *aix_format_parse(sf_node *sfn){
+ char *buf; /* temp copy of arg to hack on */
+ char *walk;
+ int items;
+
+ /*** sanity check and count items ***/
+ items = 0;
+ walk = sfn->sf;
+ /* state machine */ {
+ int c;
+ initial:
+ c = *walk++;
+ if(c=='%') goto get_desc;
+ if(!c) goto looks_ok;
+ /* get_text: */
+ items++;
+ get_more_text:
+ c = *walk++;
+ if(c=='%') goto get_desc;
+ if(c) goto get_more_text;
+ goto looks_ok;
+ get_desc:
+ items++;
+ c = *walk++;
+ if(c) goto initial;
+ return "Improper AIX field descriptor.";
+ looks_ok:
+ ;
+ }
+
+ /*** sanity check passed ***/
+ buf = malloc(strlen(sfn->sf)+1);
+ strcpy(buf, sfn->sf);
+ walk = sfn->sf;
+
+ while(items--){
+ format_node *fnode; /* newly allocated */
+ format_node *endp; /* for list manipulation */
+
+ if(*walk == '%'){
+ const aix_struct *aix;
+ walk++;
+ if(*walk == '%') goto double_percent;
+ aix = search_aix_array(*walk);
+ walk++;
+ if(!aix){
+ free(buf);
+ return "Unknown AIX field descriptor.";
+ }
+ fnode = do_one_spec(aix->spec, aix->head);
+ if(!fnode){
+ free(buf);
+ return "AIX field descriptor processing bug.";
+ }
+ } else {
+ int len;
+ len = strcspn(walk, "%");
+ memcpy(buf,walk,len);
+ if(0){
+double_percent:
+ len = 1;
+ buf[0] = '%';
+ }
+ buf[len] = '\0';
+ walk += len;
+ fnode = malloc(sizeof(format_node));
+ fnode->width = len;
+ fnode->name = malloc(len+1);
+ strcpy(fnode->name, buf);
+ fnode->pr = NULL; /* checked for */
+ fnode->need = 0;
+ fnode->vendor = AIX;
+ fnode->flags = CF_PRINT_EVERY_TIME;
+ fnode->next = NULL;
+ }
+
+ endp = fnode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->f_cooked;
+ sfn->f_cooked = fnode;
+ }
+ free(buf);
+ already_parsed_format = 1;
+ return NULL;
+}
+
+/***************************************************************
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ * Put each completed format_node onto the list starting at ->f_cooked
+ */
+static const char *format_parse(sf_node *sfn){
+ char *buf; /* temp copy of arg to hack on */
+ char *sep_loc; /* separator location: " \t,\n" */
+ char *walk;
+ const char *err; /* error code that could or did happen */
+ format_node *fnode;
+ int items;
+ int need_item;
+ static char errbuf[80]; /* for variable-text error message */
+
+ /*** prepare to operate ***/
+ buf = malloc(strlen(sfn->sf)+1);
+ strcpy(buf, sfn->sf);
+
+ /*** sanity check and count items ***/
+ need_item = 1; /* true */
+ items = 0;
+ walk = buf;
+ do{
+ switch(*walk){
+ case ' ': case ',': case '\t': case '\n': case '\0':
+ /* Linux extension: allow \t and \n as delimiters */
+ if(need_item){
+ free(buf);
+ goto improper;
+ }
+ need_item=1;
+ break;
+ case '=':
+ if(broken) goto out;
+ /* fall through */
+ default:
+ if(need_item) items++;
+ need_item=0;
+ }
+ } while (*++walk);
+out:
+ if(!items){
+ free(buf);
+ goto empty;
+ }
+#ifdef STRICT_LIST
+ if(need_item){ /* can't have trailing deliminator */
+ free(buf);
+ goto improper;
+ }
+#else
+ if(need_item){ /* allow 1 trailing deliminator */
+ *--walk='\0'; /* remove the trailing deliminator */
+ }
+#endif
+ /*** actually parse the list ***/
+ walk = buf;
+ while(items--){
+ format_node *endp;
+ char *equal_loc;
+ char *colon_loc;
+ sep_loc = strpbrk(walk," ,\t\n");
+ /* if items left, then sep_loc is not in header override */
+ if(items && sep_loc) *sep_loc = '\0';
+ equal_loc = strpbrk(walk,"=");
+ if(equal_loc){ /* if header override */
+ *equal_loc = '\0';
+ equal_loc++;
+ }
+ colon_loc = strpbrk(walk,":");
+ if(colon_loc){ /* if width override */
+ *colon_loc = '\0';
+ colon_loc++;
+ if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc){
+ free(buf);
+ goto badwidth;
+ }
+ }
+ fnode = do_one_spec(walk,equal_loc);
+ if(!fnode){
+ if(!*errbuf){ /* if didn't already create an error string */
+ snprintf(
+ errbuf,
+ sizeof(errbuf),
+ "Unknown user-defined format specifier \"%s\".",
+ walk
+ );
+ }
+ free(buf);
+ goto unknown;
+ }
+ if(colon_loc){
+ if(fnode->next){
+ free(buf);
+ goto notmacro;
+ }
+ // FIXME: enforce signal width to 8, 9, or 16 (grep: SIGNAL wide_signals)
+ fnode->width = atoi(colon_loc); // already verified to be a number
+ }
+ endp = fnode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->f_cooked;
+ sfn->f_cooked = fnode;
+ walk = sep_loc + 1; /* point to next item, if any */
+ }
+ free(buf);
+ already_parsed_format = 1;
+ return NULL;
+
+ /* errors may cause a retry looking for AIX format codes */
+ if(0) unknown: err=errbuf;
+ if(0) empty: err="Empty format list.";
+ if(0) improper: err="Improper format list.";
+ if(0) badwidth: err="Column widths must be unsigned decimal numbers.";
+ if(0) notmacro: err="Can't set width for a macro (multi-column) format specifier.";
+ if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn);
+ return err;
+}
+
+/**************** Parse single sort specifier *******************/
+static sort_node *do_one_sort_spec(const char *spec){
+ const format_struct *fs;
+ int reverse = 0;
+ if(*spec == '-'){
+ reverse = 1;
+ spec++;
+ } else if(*spec == '+'){
+ spec++;
+ }
+ fs = search_format_array(spec);
+ if(fs){
+ sort_node *thisnode;
+ thisnode = malloc(sizeof(sort_node));
+ thisnode->sr = fs->sr;
+ thisnode->need = fs->need;
+ thisnode->reverse = reverse;
+ thisnode->next = NULL;
+ return thisnode;
+ }
+ return NULL; /* bad, spec not found */
+}
+
+
+/**************************************************************
+ * Used to parse long sorting options.
+ * Put each completed sort_node onto the list starting at ->s_cooked
+ */
+static const char *long_sort_parse(sf_node *sfn){
+ char *buf; /* temp copy of arg to hack on */
+ char *sep_loc; /* separator location: " \t,\n" */
+ char *walk;
+ sort_node *snode;
+ int items;
+ int need_item;
+
+ /*** prepare to operate ***/
+ buf = malloc(strlen(sfn->sf)+1);
+ strcpy(buf, sfn->sf);
+
+ /*** sanity check and count items ***/
+ need_item = 1; /* true */
+ items = 0;
+ walk = buf;
+ do{
+ switch(*walk){
+ case ' ': case ',': case '\t': case '\n': case '\0':
+ if(need_item){
+ free(buf);
+ return "Improper sort list";
+ }
+ need_item=1;
+ break;
+ default:
+ if(need_item) items++;
+ need_item=0;
+ }
+ } while (*++walk);
+ if(!items){
+ free(buf);
+ return "Empty sort list.";
+ }
+#ifdef STRICT_LIST
+ if(need_item){ /* can't have trailing deliminator */
+ free(buf);
+ return "Improper sort list.";
+ }
+#else
+ if(need_item){ /* allow 1 trailing deliminator */
+ *--walk='\0'; /* remove the trailing deliminator */
+ }
+#endif
+ /*** actually parse the list ***/
+ walk = buf;
+ while(items--){
+ sort_node *endp;
+ sep_loc = strpbrk(walk," ,\t\n");
+ if(sep_loc) *sep_loc = '\0';
+ snode = do_one_sort_spec(walk);
+ if(!snode){
+ free(buf);
+ return "Unknown sort specifier.";
+ }
+ endp = snode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->s_cooked;
+ sfn->s_cooked = snode;
+ walk = sep_loc + 1; /* point to next item, if any */
+ }
+ free(buf);
+ already_parsed_sort = 1;
+ return NULL;
+}
+
+
+
+
+
+
+/************ pre-parse short sorting option *************/
+/* Errors _must_ be detected so that the "O" option can try to
+ * reparse as formatting codes.
+ */
+static const char *verify_short_sort(const char *arg){
+ const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
+ char checkoff[256];
+ int i;
+ const char *walk;
+ int tmp;
+ if(strspn(arg,all) != strlen(arg)) return "Bad sorting code.";
+ for(i=256; i--;) checkoff[i] = 0;
+ walk = arg;
+ for(;;){
+ tmp = *walk;
+ switch(tmp){
+ case '\0':
+ return NULL; /* looks good */
+ case '+':
+ case '-':
+ tmp = *(walk+1);
+ if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code.";
+ break;
+ case 'P':
+ if(forest_type) return "PPID sort and forest output conflict.";
+ /* fall through */
+ default:
+ if(checkoff[tmp]) return "Bad sorting code."; /* repeated */
+ /* ought to check against already accepted sort options */
+ checkoff[tmp] = 1;
+ break;
+ }
+ walk++;
+ }
+}
+
+
+
+/************ parse short sorting option *************/
+static const char *short_sort_parse(sf_node *sfn){
+ int direction = 0;
+ const char *walk;
+ int tmp;
+ sort_node *snode;
+ sort_node *endp;
+ const struct shortsort_struct *ss;
+ walk = sfn->sf;
+ for(;;){
+ tmp = *walk;
+ switch(tmp){
+ case '\0':
+ already_parsed_sort = 1;
+ return NULL;
+ case '+':
+ direction = 0;
+ break;
+ case '-':
+ direction = 1;
+ break;
+ default:
+ ss = search_shortsort_array(tmp);
+ if(!ss) return "Unknown sort specifier.";
+ snode = do_one_sort_spec(ss->spec);
+ if(!snode) return "Unknown sort specifier.";
+ snode->reverse = direction;
+ endp = snode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->s_cooked;
+ sfn->s_cooked = snode;
+ direction = 0;
+ break;
+ }
+ walk++;
+ }
+}
+
+/******************* high-level below here *********************/
+
+
+/*
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ * Recursion is to preserve original order.
+ */
+static const char *parse_O_option(sf_node *sfn){
+ const char *err; /* error code that could or did happen */
+
+ if(sfn->next){
+ err = parse_O_option(sfn->next);
+ if(err) return err;
+ }
+
+ switch(sfn->sf_code){
+ case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/
+ err = format_parse(sfn);
+ if(!err) already_parsed_format = 1;
+ break;
+ case SF_U_O: /*** format ***/
+ /* Can have -l -f f u... set already_parsed_format like DEC does */
+ if(already_parsed_format) return "option -O can not follow other format options.";
+ err = format_parse(sfn);
+ if(err) return err;
+ already_parsed_format = 1;
+ O_wrap(sfn,'u'); /* must wrap user format in default */
+ break;
+ case SF_B_O: /*** both ***/
+ if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options.";
+ else err = verify_short_sort(sfn->sf);
+ if(!err){ /* success as sorting code */
+ short_sort_parse(sfn);
+ already_parsed_sort = 1;
+ return NULL;
+ }
+ if(already_parsed_format){
+ err = "option O is neither first format nor sort order.";
+ break;
+ }
+ if(!format_parse(sfn)){ /* if success as format code */
+ already_parsed_format = 1;
+ O_wrap(sfn,'b'); /* must wrap user format in default */
+ return NULL;
+ }
+ break;
+ case SF_G_sort: case SF_B_m: /*** sort ***/
+ if(already_parsed_sort) err = "Multiple sort options.";
+ else err = long_sort_parse(sfn);
+ already_parsed_sort = 1;
+ break;
+ default: /*** junk ***/
+ return "Bug: parse_O_option got weirdness!";
+ }
+ return err; /* could be NULL */
+}
+
+
+/************ Main parser calls this to save lists for later **********/
+/* store data for later and return 1 if arg looks non-standard */
+int defer_sf_option(const char *arg, int source){
+ sf_node *sfn;
+ char buf[16];
+ int dist;
+ const format_struct *fs;
+ int need_item = 1;
+
+ sfn = malloc(sizeof(sf_node));
+ sfn->sf = malloc(strlen(arg)+1);
+ strcpy(sfn->sf, arg);
+ sfn->sf_code = source;
+ sfn->s_cooked = NULL;
+ sfn->f_cooked = NULL;
+ sfn->next = sf_list;
+ sf_list = sfn;
+
+ if(source == SF_G_sort) have_gnu_sort = 1;
+
+ /* Now try to find an excuse to ignore broken Unix98 parsing. */
+ if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */
+ do{
+ switch(*arg){
+ case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */
+ if(need_item) return 1; /* something wrong */
+ need_item=1;
+ break;
+ case '=':
+ if(need_item) return 1; /* something wrong */
+ return 0; /* broken Unix98 parsing is required */
+ default:
+ if(!need_item) break;
+ need_item=0;
+ dist = strcspn(arg,", =");
+ if(dist>15) return 1; /* something wrong, sort maybe? */
+ strncpy(buf,arg,dist); /* no '\0' on end */
+ buf[dist] = '\0'; /* fix that problem */
+ fs = search_format_array(buf);
+ if(!fs) return 1; /* invalid spec, macro or sort maybe? */
+ if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */
+ }
+ } while (*++arg);
+
+ return 0; /* boring, Unix98 is no change */
+}
+
+/***** Since ps is not long-lived, the memory leak can be ignored. ******/
+void reset_sortformat(void){
+ sf_list = NULL; /* deferred sorting and formatting */
+ format_list = NULL; /* digested formatting options */
+ sort_list = NULL; /* digested sorting options (redundant?) */
+ have_gnu_sort = 0;
+ already_parsed_sort = 0;
+ already_parsed_format = 0;
+}
+
+
+/***** Search format_list for findme, then insert putme after findme. ****/
+static int fmt_add_after(const char *findme, format_node *putme){
+ format_node *walk;
+ if(!strcmp(format_list->name, findme)){
+ putme->next = format_list->next;
+ format_list->next = putme;
+ return 1; /* success */
+ }
+ walk = format_list;
+ while(walk->next){
+ if(!strcmp(walk->next->name, findme)){
+ putme->next = walk->next->next;
+ walk->next->next = putme;
+ return 1; /* success */
+ }
+ walk = walk->next;
+ }
+ return 0; /* fail */
+}
+
+/******* Search format_list for findme, then delete it. ********/
+static int fmt_delete(const char *findme){
+ format_node *walk;
+ format_node *old;
+ if(!strcmp(format_list->name, findme)){
+ old = format_list;
+ format_list = format_list->next;
+ free(old);
+ return 1; /* success */
+ }
+ walk = format_list;
+ while(walk->next){
+ if(!strcmp(walk->next->name, findme)){
+ old = walk->next;
+ walk->next = walk->next->next;
+ free(old);
+ return 1; /* success */
+ }
+ walk = walk->next;
+ }
+ return 0; /* fail */
+}
+
+
+/************ Build a SysV format backwards. ***********/
+#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn)
+static const char *generate_sysv_list(void){
+ format_node *fn;
+ if((format_modifiers & FM_y) && !(format_flags & FF_Ul))
+ return "Modifier -y without format -l makes no sense.";
+ if(prefer_bsd_defaults){
+ if(format_flags) PUSH("cmd");
+ else PUSH("args");
+ PUSH("bsdtime");
+ if(!(format_flags & FF_Ul)) PUSH("stat");
+ }else{
+ if(format_flags & FF_Uf) PUSH("cmd");
+ else PUSH("ucmd");
+ PUSH("time");
+ }
+ PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */
+ if(format_flags & FF_Uf) PUSH("stime");
+ /* avoid duplicate columns from -FP and -Fly */
+ if(format_modifiers & FM_F){
+ /* if -FP take the Sun-style column instead (sorry about "sgi_p") */
+ if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */
+ /* if -Fly take the ADDR-replacement RSS instead */
+ if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss");
+ }
+ if(format_flags & FF_Ul){
+ PUSH("wchan");
+ }
+ /* since FM_y adds RSS anyway, don't do this hack when that is true */
+ if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){
+ if(personality & PER_IRIX_l){ /* add "rss" then ':' here */
+ PUSH("sgi_rss");
+ fn = malloc(sizeof(format_node));
+ fn->width = 1;
+ fn->name = malloc(2);
+ strcpy(fn->name, ":");
+ fn->pr = NULL; /* checked for */
+ fn->need = 0;
+ fn->vendor = AIX; /* yes, for SGI weirdness */
+ fn->flags = CF_PRINT_EVERY_TIME;
+ fn->next = format_list;
+ format_list=fn;
+ }
+ }
+ if((format_modifiers & FM_F) || (format_flags & FF_Ul)){
+ PUSH("sz");
+ }
+ if(format_flags & FF_Ul){
+ if(format_modifiers & FM_y) PUSH("rss");
+ else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p");
+ else PUSH("addr_1");
+ }
+ if(format_modifiers & FM_c){
+ PUSH("pri"); PUSH("class");
+ }else if(format_flags & FF_Ul){
+ PUSH("ni");
+ if(personality & PER_IRIX_l) PUSH("priority");
+ else /* is this good? */ PUSH("opri");
+ }
+
+ // FIXME TODO XXX -- this is a serious problem
+ // These somehow got flipped around.
+ // The bug is in procps-3.1.1, procps-990211, prior too?
+ if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp");
+ if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
+
+ if(format_modifiers & FM_P) PUSH("psr");
+ if(thread_flags & TF_U_L) PUSH("lwp");
+ if(format_modifiers & FM_j){
+ PUSH("sid");
+ PUSH("pgid");
+ }
+ if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
+ if(thread_flags & TF_U_T) PUSH("spid");
+ PUSH("pid");
+ if(format_flags & FF_Uf){
+ if(personality & PER_SANE_USER) PUSH("user");
+ else PUSH("uid_hack");
+ }else if(format_flags & FF_Ul){
+ PUSH("uid");
+ }
+ if(format_flags & FF_Ul){
+ PUSH("s");
+ if(!(format_modifiers & FM_y)) PUSH("f");
+ }
+ if(format_modifiers & FM_M){
+ PUSH("label"); /* Mandatory Access Control */
+ }
+ return NULL;
+}
+
+
+/**************************************************************************
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ */
+const char *process_sf_options(int localbroken){
+ sf_node *sf_walk;
+
+ if(personality & PER_BROKEN_o) localbroken = 1;
+ if(personality & PER_GOOD_o) localbroken = 0;
+ broken = localbroken;
+ if(sf_list){
+ const char *err;
+ err = parse_O_option(sf_list);
+ if(err) return err;
+ }
+
+ if(format_list) printf("Bug: must reset the list first!\n");
+
+ /* merge formatting info of sf_list into format_list here */
+ sf_walk = sf_list;
+ while(sf_walk){
+ format_node *fmt_walk;
+ fmt_walk = sf_walk->f_cooked;
+ sf_walk->f_cooked = NULL;
+ while(fmt_walk){ /* put any nodes onto format_list in opposite way */
+ format_node *travler;
+ travler = fmt_walk;
+ fmt_walk = fmt_walk->next;
+ travler->next = format_list;
+ format_list = travler;
+ }
+ sf_walk = sf_walk->next;
+ }
+
+ /* merge sorting info of sf_list into sort_list here */
+ sf_walk = sf_list;
+ while(sf_walk){
+ sort_node *srt_walk;
+ srt_walk = sf_walk->s_cooked;
+ sf_walk->s_cooked = NULL;
+ while(srt_walk){ /* put any nodes onto sort_list in opposite way */
+ sort_node *travler;
+ travler = srt_walk;
+ srt_walk = srt_walk->next;
+ travler->next = sort_list;
+ sort_list = travler;
+ }
+ sf_walk = sf_walk->next;
+ }
+
+ // Get somebody to explain how -L/-T is supposed to interact
+ // with sorting. Do the threads remain grouped, with sorting
+ // by process, or do the threads get sorted by themselves?
+ if(sort_list && (thread_flags&TF_no_sort)){
+ return "Tell procps-feedback@lists.sf.net what you expected.";
+ }
+
+ // If nothing else, try to use $PS_FORMAT before the default.
+ if(!format_flags && !format_modifiers && !format_list){
+ char *tmp;
+ tmp = getenv("PS_FORMAT"); /* user override kills default */
+ if(tmp && *tmp){
+ const char *err;
+ sf_node sfn;
+ if(thread_flags&TF_must_use) return "Tell procps-feedback@sf.net what you want. (-L/-T, -m/m/H, and $PS_FORMAT)";
+ sfn.sf = tmp;
+ sfn.f_cooked = NULL;
+ err = format_parse(&sfn);
+ if(!err){
+ format_node *fmt_walk;
+ fmt_walk = sfn.f_cooked;
+ while(fmt_walk){ /* put any nodes onto format_list in opposite way */
+ format_node *travler;
+ travler = fmt_walk;
+ fmt_walk = fmt_walk->next;
+ travler->next = format_list;
+ format_list = travler;
+ }
+ return NULL;
+ }
+ // FIXME: prove that this won't be hit on valid bogus-BSD options
+ fprintf(stderr, "Warning: $PS_FORMAT ignored. (%s)\n", err);
+ }
+ }
+
+ if(format_list){
+ if(format_flags) return "Conflicting format options.";
+ if(format_modifiers) return "Can't use output modifiers with user-defined output";
+ if(thread_flags&TF_must_use) return "-L/-T with H/m/-m and -o/-O/o/O is nonsense";
+ return NULL;
+ }
+
+ do{
+ const char *spec;
+ switch(format_flags){
+
+ default: return "Conflicting format options.";
+
+ /* These can be NULL, which enables SysV list generation code. */
+ case 0: spec=NULL; break;
+ case FF_Uf | FF_Ul: spec=sysv_fl_format; break;
+ case FF_Uf: spec=sysv_f_format; break;
+ case FF_Ul: spec=sysv_l_format; break;
+
+ /* These are NOT REACHED for normal -j processing. */
+ case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */
+ case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */
+ case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */
+
+ /* These are true BSD options. */
+ case FF_Bj: spec=bsd_j_format; break;
+ case FF_Bl: spec=bsd_l_format; break;
+ case FF_Bs: spec=bsd_s_format; break;
+ case FF_Bu: spec=bsd_u_format; break;
+ case FF_Bv: spec=bsd_v_format; break;
+
+ /* These are old Linux options. Option m is overloaded. */
+ case FF_LX: spec="OL_X"; break;
+ case FF_Lm: spec="OL_m"; break;
+
+ /* This is the sole FLASK security option. */
+ case FF_Fc: spec="FLASK_context"; break;
+
+ } /* end switch(format_flags) */
+
+ // not just for case 0, since sysv_l_format and such may be NULL
+ if(!spec) return generate_sysv_list();
+
+ do{
+ format_node *fmt_walk;
+ fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */
+ while(fmt_walk){ /* put any nodes onto format_list in opposite way */
+ format_node *travler;
+ travler = fmt_walk;
+ fmt_walk = fmt_walk->next;
+ travler->next = format_list;
+ format_list = travler;
+ }
+ }while(0);
+ }while(0);
+
+ do{
+ format_node *fn;
+ if(format_modifiers & FM_j){
+ fn = do_one_spec("pgid", NULL);
+ if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
+ return "Internal error, no PID or PPID for -j option.";
+ fn = do_one_spec("sid", NULL);
+ if(!fmt_add_after("PGID", fn)) return "Lost my PGID!";
+ }
+ if(format_modifiers & FM_y){
+ /* TODO: check for failure to do something, and complain if so */
+ fmt_delete("F");
+ fn = do_one_spec("rss", NULL);
+ if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
+ }
+ if(format_modifiers & FM_c){
+ fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C");
+ fmt_delete("NI");
+ fn = do_one_spec("class", NULL);
+ if(!fmt_add_after("PRI", fn))
+ return "Internal error, no PRI for -c option.";
+ fmt_delete("PRI"); /* we want a different one */
+ fn = do_one_spec("pri", NULL);
+ if(!fmt_add_after("CLS", fn)) return "Lost my CLS!";
+ }
+ if(thread_flags & TF_U_T){
+ fn = do_one_spec("spid", NULL);
+ if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use))
+ return "-T with H/-m/m but no PID for SPID to follow";
+ }
+ if(thread_flags & TF_U_L){
+ fn = do_one_spec("lwp", NULL);
+ if(fmt_add_after("SID", fn)) goto did_lwp;
+ if(fmt_add_after("SESS", fn)) goto did_lwp;
+ if(fmt_add_after("PGID", fn)) goto did_lwp;
+ if(fmt_add_after("PGRP", fn)) goto did_lwp;
+ if(fmt_add_after("PPID", fn)) goto did_lwp;
+ if(fmt_add_after("PID", fn)) goto did_lwp;
+ if(thread_flags&TF_must_use)
+ return "-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow";
+did_lwp:
+ fn = do_one_spec("nlwp", NULL);
+ fmt_add_after("%CPU", fn);
+ }
+ if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style
+ fn = do_one_spec("label", NULL);
+ fn->next=format_list;
+ format_list=fn;
+ }
+ /* Do personality-specific translations not covered by format_flags.
+ * Generally, these only get hit when personality overrides unix output.
+ * That (mostly?) means the Digital and Debian personalities.
+ */
+ if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){
+ fn = do_one_spec("sgi_p", NULL);
+ if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
+ }
+ if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){
+ fn = do_one_spec("user", NULL);
+ if(fmt_add_after("UID", fn)) fmt_delete("UID");
+ }
+ }while(0);
+
+ return NULL;
+}
+
diff --git a/smartt-top/pwdx.1 b/smartt-top/pwdx.1
new file mode 100644
index 0000000..728157d
--- /dev/null
+++ b/smartt-top/pwdx.1
@@ -0,0 +1,37 @@
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for pwdx
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Copyright 2004 Nicholas Miell.
+.\" Based on the pmap(1) man page by Albert Cahalan.
+.\"
+.TH PWDX 1 "September 8, 2004" "Linux" "Linux User's Manual"
+.SH NAME
+pwdx \- report current working directory of a process
+
+.SH SYNOPSIS
+.nf
+pwdx pids...
+pwdx -V
+.fi
+
+.SH DESCRIPTION
+The pwdx command reports the current working directory of a process or
+processes.
+
+.SH "GENERAL OPTIONS"
+.TS
+l l l.
+-V show version Displays version of program.
+.TE
+
+.SH "SEE ALSO"
+ps(1) pgrep(1)
+
+.SH STANDARDS
+No standards apply, but pwdx looks an awful lot like a SunOS command.
+
+.SH AUTHOR
+Nicholas Miell <nmiell@gmail.com> wrote pwdx in 2004. Please send bug
+reports to <procps-feedback@lists.sf.net>.
diff --git a/smartt-top/pwdx.c b/smartt-top/pwdx.c
new file mode 100644
index 0000000..3e88f3c
--- /dev/null
+++ b/smartt-top/pwdx.c
@@ -0,0 +1,107 @@
+// Copyright 2004 Nicholas Miell
+//
+// This file may be used subject to the terms and conditions of the
+// GNU Library General Public License Version 2 as published by the
+// Free Software Foundation.This program is distributed in the hope
+// that it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU Library General Public License for more
+// details.
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "proc/version.h"
+
+static void die(const char *msg) NORETURN;
+static void die(const char *msg)
+{
+ fputs(msg, stderr);
+ exit(1);
+}
+
+static void version(void) NORETURN;
+static void version(void)
+{
+ printf("pwdx (%s)\n", procps_version);
+ exit(0);
+}
+
+int main(int argc, char* argv[])
+{
+ regex_t re;
+ int i;
+
+ if (argc < 2)
+ die("Usage: pwdx pid...\n");
+
+ // Allowed on the command line:
+ //
+ // --version
+ // -V
+ // /proc/nnnn
+ // nnnn
+ //
+ // where nnnn is any number that doesn't begin with 0.
+ //
+ // If --version or -V are present, further arguments are ignored
+ // completely.
+
+ regcomp(&re, "^((/proc/+)?[1-9][0-9]*|-V|--version)$",
+ REG_EXTENDED|REG_NOSUB);
+
+ for (i = 1; i < argc; i++) {
+ if (regexec(&re, argv[i], 0, NULL, 0) != 0) {
+ char buf[27 + strlen (argv[i]) + 1]; // Constant 27 is the length of the error string "pwdx: ... "
+ snprintf(buf, sizeof buf, "pwdx: invalid process id: %s\n", argv[i]);
+ die(buf);
+ }
+ if (!strcmp("-V", argv[i]) || !strcmp("--version", argv[i]))
+ version();
+ }
+
+ regfree(&re);
+
+ int alloclen = 128;
+ char *pathbuf = malloc(alloclen);
+
+ for (i = 1; i < argc; i++) {
+ char * s;
+ int len;
+ char buf[10 + strlen(argv[i]) + 1]; // Constant 10 is the length of strings "/proc/" + "/cwd" + 1
+
+ // At this point, all arguments are in the form /proc/nnnn
+ // or nnnn, so a simple check based on the first char is
+ // possible
+ if (argv[i][0] != '/')
+ snprintf(buf, sizeof buf, "/proc/%s/cwd", argv[i]);
+ else
+ snprintf(buf, sizeof buf, "%s/cwd", argv[i]);
+
+ // buf contains /proc/nnnn/cwd symlink name on entry, the
+ // target of that symlink on return
+ while ((len = readlink(buf, pathbuf, alloclen)) == alloclen) {
+ alloclen *= 2;
+ pathbuf = realloc(pathbuf, alloclen);
+ }
+
+ if (len < 0) {
+ s = strerror(errno == ENOENT ? ESRCH : errno);
+ } else {
+ pathbuf[len] = 0;
+ s = pathbuf;
+ }
+
+ printf("%s: %s\n", argv[i], s);
+ }
+
+ free(pathbuf);
+
+ return 0;
+}
diff --git a/smartt-top/skill.1 b/smartt-top/skill.1
new file mode 100644
index 0000000..a56b969
--- /dev/null
+++ b/smartt-top/skill.1
@@ -0,0 +1,128 @@
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for skill and snice.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan, converted to a man page by
+.\" Michael K. Johnson
+.\"
+.TH SKILL 1 "March 12, 1999" "Linux" "Linux User's Manual"
+.SH NAME
+skill, snice \- send a signal or report process status
+
+.SH SYNOPSIS
+.B skill
+.RI [ "signal to send" ]
+.RI [ options ]
+.I process selection criteria
+.br
+.B snice
+.RI [ "new priority" ]
+.RI [ options ]
+.I process selection criteria
+
+.SH DESCRIPTION
+These tools are probably obsolete and unportable. The command
+syntax is poorly defined. Consider using the killall, pkill,
+and pgrep commands instead.
+
+The default signal for skill is TERM. Use \-l or \-L to list available signals.
+Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+Alternate signals may be specified in three ways: \-9 \-SIGKILL \-KILL.
+
+The default priority for snice is +4. (snice +4 ...)
+Priority numbers range from +20 (slowest) to \-20 (fastest).
+Negative priority numbers are restricted to administrative users.
+
+.SH "GENERAL OPTIONS"
+.TS
+lB l l.
+\-f fast mode This is not currently useful.
+\-i interactive use You will be asked to approve each action.
+\-v verbose output Display information about selected processes.
+\-w warnings enabled This is not currently useful.
+\-n no action This only displays the process ID.
+\-V show version Displays version of program.
+.TE
+
+.SH "PROCESS SELECTION OPTIONS"
+Selection criteria can be: terminal, user, pid, command.
+The options below may be used to ensure correct interpretation.
+Do not blame Albert for this interesting interface.
+.TS
+lB l.
+\-t The next argument is a terminal (tty or pty).
+\-u The next argument is a username.
+\-p The next argument is a process ID number.
+\-c The next argument is a command name.
+.TE
+
+.SH SIGNALS
+The signals listed below may be available for use with skill.
+When known, numbers and default behavior are shown.
+.TS
+lB rB lB lB
+lfCW r l l.
+Name Num Action Description
+0 0 n/a exit code indicates if a signal may be sent
+ALRM 14 exit
+HUP 1 exit
+INT 2 exit
+KILL 9 exit this signal may not be blocked
+PIPE 13 exit
+POLL exit
+PROF exit
+TERM 15 exit
+USR1 exit
+USR2 exit
+VTALRM exit
+STKFLT exit may not be implemented
+PWR ignore may exit on some systems
+WINCH ignore
+CHLD ignore
+URG ignore
+TSTP stop may interact with the shell
+TTIN stop may interact with the shell
+TTOU stop may interact with the shell
+STOP stop this signal may not be blocked
+CONT restart continue if stopped, otherwise ignore
+ABRT 6 core
+FPE 8 core
+ILL 4 core
+QUIT 3 core
+SEGV 11 core
+TRAP 5 core
+SYS core may not be implemented
+EMT core may not be implemented
+BUS core core dump may fail
+XCPU core core dump may fail
+XFSZ core core dump may fail
+.TE
+
+.SH EXAMPLES
+.TS
+lB lB
+lfCW l.
+Command Description
+snice seti crack +7 Slow down seti and crack
+skill \-KILL \-v /dev/pts/* Kill users on new-style PTY devices
+skill \-STOP viro lm davem Stop 3 users
+snice \-17 root bash Give priority to root's shell
+.TE
+
+.SH "SEE ALSO"
+.BR killall (1),
+.BR pkill (1),
+.BR kill (1),
+.BR renice (1),
+.BR nice(1),
+.BR kill(2),
+.BR signal(7)
+
+.SH STANDARDS
+No standards apply.
+
+.SH AUTHOR
+Albert Cahalan <albert@users.sf.net> wrote skill and snice in 1999 as a
+replacement for a non-free version, and is the current maintainer of the
+procps collection. Please send bug reports to <procps-feedback@lists.sf.net>.
diff --git a/smartt-top/skill.c b/smartt-top/skill.c
new file mode 100644
index 0000000..2d90ad0
--- /dev/null
+++ b/smartt-top/skill.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <fcntl.h>
+#include <pwd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "proc/pwcache.h"
+#include "proc/sig.h"
+#include "proc/devname.h"
+#include "proc/procps.h" /* char *user_from_uid(uid_t uid) */
+#include "proc/version.h" /* procps_version */
+
+static int f_flag, i_flag, v_flag, w_flag, n_flag;
+
+static int tty_count, uid_count, cmd_count, pid_count;
+static int *ttys;
+static uid_t *uids;
+static const char **cmds;
+static int *pids;
+
+#define ENLIST(thing,addme) do{ \
+if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \
+if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \
+thing##s[thing##_count++] = addme; \
+}while(0)
+
+static int my_pid;
+static int saved_argc;
+
+static int sig_or_pri;
+
+static int program;
+#define PROG_GARBAGE 0 /* keep this 0 */
+#define PROG_KILL 1
+#define PROG_SKILL 2
+/* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */
+#define PROG_SNICE 4
+
+
+/********************************************************************/
+
+static void display_kill_version(void){
+ switch(program) {
+ case PROG_KILL:
+ fprintf(stdout, "kill (%s)\n",procps_version);
+ return;
+ case PROG_SKILL:
+ fprintf(stdout, "skill (%s)\n",procps_version);
+ return;
+ case PROG_SNICE:
+ fprintf(stdout, "snice (%s)\n",procps_version);
+ return;
+ default:
+ fprintf(stdout, "unknown (%s)\n",procps_version);
+ return;
+ }
+}
+
+/***** kill or nice a process */
+static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd){
+ int failed;
+ int saved_errno;
+ char dn_buf[1000];
+ dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV);
+ if(i_flag){
+ char buf[8];
+ fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ",
+ (char*)dn_buf,user_from_uid(uid),pid,cmd
+ );
+ if(!fgets(buf,7,stdin)){
+ printf("\n");
+ exit(0);
+ }
+ if(*buf!='y' && *buf!='Y') return;
+ }
+ /* do the actual work */
+ if(program==PROG_SKILL) failed=kill(pid,sig_or_pri);
+ else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri);
+ saved_errno = errno;
+ if(w_flag && failed){
+ fprintf(stderr, "%-8s %-8s %5d %-16.16s ",
+ (char*)dn_buf,user_from_uid(uid),pid,cmd
+ );
+ errno = saved_errno;
+ perror("");
+ return;
+ }
+ if(i_flag) return;
+ if(v_flag){
+ printf("%-8s %-8s %5d %-16.16s\n",
+ (char*)dn_buf,user_from_uid(uid),pid,cmd
+ );
+ return;
+ }
+ if(n_flag){
+ printf("%d\n",pid);
+ return;
+ }
+}
+
+
+/***** check one process */
+static void check_proc(int pid){
+ char buf[128];
+ struct stat statbuf;
+ char *tmp;
+ int tty;
+ int fd;
+ int i;
+ if(pid==my_pid) return;
+ sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */
+ fd = open(buf,O_RDONLY);
+ if(fd==-1){ /* process exited maybe */
+ if(pids && w_flag) printf("WARNING: process %d could not be found.\n",pid);
+ return;
+ }
+ fstat(fd, &statbuf);
+ if(uids){ /* check the EUID */
+ i=uid_count;
+ while(i--) if(uids[i]==statbuf.st_uid) break;
+ if(i==-1) goto closure;
+ }
+ read(fd,buf,128);
+ buf[127] = '\0';
+ tmp = strrchr(buf, ')');
+ *tmp++ = '\0';
+ i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */
+ tty = atoi(tmp);
+ if(ttys){
+ i=tty_count;
+ while(i--) if(ttys[i]==tty) break;
+ if(i==-1) goto closure;
+ }
+ tmp = strchr(buf, '(') + 1;
+ if(cmds){
+ i=cmd_count;
+ /* fast comparison trick -- useful? */
+ while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break;
+ if(i==-1) goto closure;
+ }
+ /* This is where we kill/nice something. */
+/* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
+ pid, statbuf.st_uid, tty>>8, tty&0xf, tmp
+ );
+*/
+ hurt_proc(tty, statbuf.st_uid, pid, tmp);
+closure:
+ close(fd); /* kill/nice _first_ to avoid PID reuse */
+}
+
+
+/***** debug function */
+#if 0
+static void show_lists(void){
+ int i;
+
+ fprintf(stderr, "%d TTY: ", tty_count);
+ if(ttys){
+ i=tty_count;
+ while(i--){
+ fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n');
+ }
+ }else fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d UID: ", uid_count);
+ if(uids){
+ i=uid_count;
+ while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n');
+ }else fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d PID: ", pid_count);
+ if(pids){
+ i=pid_count;
+ while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n');
+ }else fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d CMD: ", cmd_count);
+ if(cmds){
+ i=cmd_count;
+ while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n');
+ }else fprintf(stderr, "\n");
+}
+#endif
+
+
+/***** iterate over all PIDs */
+static void iterate(void){
+ int pid;
+ DIR *d;
+ struct dirent *de;
+ if(pids){
+ pid = pid_count;
+ while(pid--) check_proc(pids[pid]);
+ return;
+ }
+#if 0
+ /* could setuid() and kill -1 to have the kernel wipe out a user */
+ if(!ttys && !cmds && !pids && !i_flag){
+ }
+#endif
+ d = opendir("/proc");
+ if(!d){
+ perror("/proc");
+ exit(1);
+ }
+ while(( de = readdir(d) )){
+ if(de->d_name[0] > '9') continue;
+ if(de->d_name[0] < '1') continue;
+ pid = atoi(de->d_name);
+ if(pid) check_proc(pid);
+ }
+ closedir (d);
+}
+
+/***** kill help */
+static void kill_usage(void) NORETURN;
+static void kill_usage(void){
+ fprintf(stderr,
+ "Usage:\n"
+ " kill pid ... Send SIGTERM to every process listed.\n"
+ " kill signal pid ... Send a signal to every process listed.\n"
+ " kill -s signal pid ... Send a signal to every process listed.\n"
+ " kill -l List all signal names.\n"
+ " kill -L List all signal names in a nice table.\n"
+ " kill -l signal Convert between signal numbers and names.\n"
+ );
+ exit(1);
+}
+
+/***** kill */
+static void kill_main(int argc, const char *restrict const *restrict argv) NORETURN;
+static void kill_main(int argc, const char *restrict const *restrict argv){
+ const char *sigptr;
+ int signo = SIGTERM;
+ int exitvalue = 0;
+ if(argc<2) kill_usage();
+ if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
+ display_kill_version();
+ exit(0);
+ }
+ if(argv[1][0]!='-'){
+ argv++;
+ argc--;
+ goto no_more_args;
+ }
+
+ /* The -l option prints out signal names. */
+ if(argv[1][1]=='l' && argv[1][2]=='\0'){
+ if(argc==2){
+ unix_print_signals();
+ exit(0);
+ }
+ /* at this point, argc must be 3 or more */
+ if(argc>128 || argv[2][0] == '-') kill_usage();
+ exit(print_given_signals(argc-2, argv+2, 80));
+ }
+
+ /* The -L option prints out signal names in a nice table. */
+ if(argv[1][1]=='L' && argv[1][2]=='\0'){
+ if(argc==2){
+ pretty_print_signals();
+ exit(0);
+ }
+ kill_usage();
+ }
+ if(argv[1][1]=='-' && argv[1][2]=='\0'){
+ argv+=2;
+ argc-=2;
+ goto no_more_args;
+ }
+ if(argv[1][1]=='-') kill_usage(); /* likely --help */
+ // FIXME: "kill -sWINCH $$" not handled
+ if(argv[1][2]=='\0' && (argv[1][1]=='s' || argv[1][1]=='n')){
+ sigptr = argv[2];
+ argv+=3;
+ argc-=3;
+ }else{
+ sigptr = argv[1]+1;
+ argv+=2;
+ argc-=2;
+ }
+ signo = signal_name_to_number(sigptr);
+ if(signo<0){
+ fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr);
+ kill_usage();
+ }
+no_more_args:
+ if(!argc) kill_usage(); /* nothing to kill? */
+ while(argc--){
+ long pid;
+ char *endp;
+ pid = strtol(argv[argc],&endp,10);
+ if(!*endp && (endp != argv[argc])){
+ if(!kill((pid_t)pid,signo)) continue;
+ // The UNIX standard contradicts itself. If at least one process
+ // is matched for each PID (as if processes could share PID!) and
+ // "the specified signal was successfully processed" (the systcall
+ // returned zero?) for at least one of those processes, then we must
+ // exit with zero. Note that an error might have also occured.
+ // The standard says we return non-zero if an error occurs. Thus if
+ // killing two processes gives 0 for one and EPERM for the other,
+ // we are required to return both zero and non-zero. Quantum kill???
+ perror("kill");
+ exitvalue = 1;
+ continue;
+ }
+ fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]);
+ kill_usage();
+ }
+ exit(exitvalue);
+}
+
+/***** skill/snice help */
+static void skillsnice_usage(void) NORETURN;
+static void skillsnice_usage(void){
+ if(program==PROG_SKILL){
+ fprintf(stderr,
+ "Usage: skill [signal to send] [options] process selection criteria\n"
+ "Example: skill -KILL -v pts/*\n"
+ "\n"
+ "The default signal is TERM. Use -l or -L to list available signals.\n"
+ "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
+ "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"
+ );
+ }else{
+ fprintf(stderr,
+ "Usage: snice [new priority] [options] process selection criteria\n"
+ "Example: snice +7 netscape crack \n"
+ "\n"
+ "The default priority is +4. (snice +4 ...)\n"
+ "Priority numbers range from +20 (slowest) to -20 (fastest).\n"
+ "Negative priority numbers are restricted to administrative users.\n"
+ );
+ }
+ fprintf(stderr,
+ "\n"
+ "General options:\n"
+ "-f fast mode This is not currently useful.\n"
+ "-i interactive use You will be asked to approve each action.\n"
+ "-v verbose output Display information about selected processes.\n"
+ "-w warnings enabled This is not currently useful.\n"
+ "-n no action This only displays the process ID.\n"
+ "\n"
+ "Selection criteria can be: terminal, user, pid, command.\n"
+ "The options below may be used to ensure correct interpretation.\n"
+ "-t The next argument is a terminal (tty or pty).\n"
+ "-u The next argument is a username.\n"
+ "-p The next argument is a process ID number.\n"
+ "-c The next argument is a command name.\n"
+ );
+ exit(1);
+}
+
+#if 0
+static void _skillsnice_usage(int line){
+ fprintf(stderr,"Something at line %d.\n", line);
+ skillsnice_usage();
+}
+#define skillsnice_usage() _skillsnice_usage(__LINE__)
+#endif
+
+#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL)
+
+/***** common skill/snice argument parsing code */
+#define NO_PRI_VAL ((int)0xdeafbeef)
+static void skillsnice_parse(int argc, const char *restrict const *restrict argv){
+ int signo = -1;
+ int prino = NO_PRI_VAL;
+ int force = 0;
+ int num_found = 0;
+ const char *restrict argptr;
+ if(argc<2) skillsnice_usage();
+ if(argc==2 && argv[1][0]=='-'){
+ if(!strcmp(argv[1],"-L")){
+ pretty_print_signals();
+ exit(0);
+ }
+ if(!strcmp(argv[1],"-l")){
+ unix_print_signals();
+ exit(0);
+ }
+ if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
+ display_kill_version();
+ exit(0);
+ }
+ skillsnice_usage();
+ }
+ NEXTARG;
+ /* Time for serious parsing. What does "skill -int 123 456" mean? */
+ while(argc){
+ if(force && !num_found){ /* if forced, _must_ find something */
+ fprintf(stderr,"ERROR: -%c used with bad data.\n", force);
+ skillsnice_usage();
+ }
+ force = 0;
+ if(program==PROG_SKILL && signo<0 && *argptr=='-'){
+ signo = signal_name_to_number(argptr+1);
+ if(signo>=0){ /* found a signal */
+ if(!NEXTARG) break;
+ continue;
+ }
+ }
+ if(program==PROG_SNICE && prino==NO_PRI_VAL
+ && (*argptr=='+' || *argptr=='-') && argptr[1]){
+ long val;
+ char *endp;
+ val = strtol(argptr,&endp,10);
+ if(!*endp && val<=999 && val>=-999){
+ prino=val;
+ if(!NEXTARG) break;
+ continue;
+ }
+ }
+ /* If '-' found, collect any flags. (but lone "-" is a tty) */
+ if(*argptr=='-' && argptr[1]){
+ argptr++;
+ do{
+ switch(( force = *argptr++ )){
+ default: skillsnice_usage();
+ case 't':
+ case 'u':
+ case 'p':
+ case 'c':
+ if(!*argptr){ /* nothing left here, *argptr is '\0' */
+ if(!NEXTARG){
+ fprintf(stderr,"ERROR: -%c with nothing after it.\n", force);
+ skillsnice_usage();
+ }
+ }
+ goto selection_collection;
+ case 'f': f_flag++; break;
+ case 'i': i_flag++; break;
+ case 'v': v_flag++; break;
+ case 'w': w_flag++; break;
+ case 'n': n_flag++; break;
+ case 0:
+ NEXTARG;
+ /*
+ * If no more arguments, all the "if(argc)..." tests will fail
+ * and the big loop will exit.
+ */
+ } /* END OF SWITCH */
+ }while(force);
+ } /* END OF IF */
+selection_collection:
+ num_found = 0; /* we should find at least one thing */
+ switch(force){ /* fall through each data type */
+ default: skillsnice_usage();
+ case 0: /* not forced */
+ if (argptr && argptr[0] == '-') /* its the next argument not a parameter */
+ continue;
+ case 't':
+ if(argc){
+ struct stat sbuf;
+ char path[32];
+ if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */
+ snprintf(path,32,"/dev/%s",argptr);
+ if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){
+ num_found++;
+ ENLIST(tty,sbuf.st_rdev);
+ if(!NEXTARG) break;
+ }else if(!(argptr[1])){ /* if only 1 character */
+ switch(*argptr){
+ default:
+ if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */
+ case '-':
+ case '?':
+ num_found++;
+ ENLIST(tty,0);
+ if(!NEXTARG) break;
+ }
+ }
+ }
+ if(force) continue;
+ case 'u':
+ if(argc){
+ struct passwd *passwd_data;
+ passwd_data = getpwnam(argptr);
+ if(passwd_data){
+ num_found++;
+ ENLIST(uid,passwd_data->pw_uid);
+ if(!NEXTARG) break;
+ }
+ }
+ if(force) continue;
+ case 'p':
+ if(argc && *argptr>='0' && *argptr<='9'){
+ char *endp;
+ int num;
+ num = strtol(argptr, &endp, 0);
+ if(*endp == '\0'){
+ num_found++;
+ ENLIST(pid,num);
+ if(!NEXTARG) break;
+ }
+ }
+ if(force) continue;
+ if(num_found) continue; /* could still be an option */
+ case 'c':
+ if(argc){
+ num_found++;
+ ENLIST(cmd,argptr);
+ if(!NEXTARG) break;
+ }
+ } /* END OF SWITCH */
+ } /* END OF WHILE */
+ /* No more arguments to process. Must sanity check. */
+ if(!tty_count && !uid_count && !cmd_count && !pid_count){
+ fprintf(stderr,"ERROR: no process selection criteria.\n");
+ skillsnice_usage();
+ }
+ if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){
+ fprintf(stderr,"ERROR: general flags may not be repeated.\n");
+ skillsnice_usage();
+ }
+ if(i_flag && (v_flag|f_flag|n_flag)){
+ fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n");
+ skillsnice_usage();
+ }
+ if(v_flag && (i_flag|f_flag)){
+ fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n");
+ skillsnice_usage();
+ }
+ /* OK, set up defaults */
+ if(prino==NO_PRI_VAL) prino=4;
+ if(signo<0) signo=SIGTERM;
+ if(n_flag){
+ program=PROG_SKILL;
+ signo=0; /* harmless */
+ }
+ if(program==PROG_SKILL) sig_or_pri = signo;
+ else sig_or_pri = prino;
+}
+
+/***** main body */
+int main(int argc, const char *argv[]){
+ const char *tmpstr;
+ my_pid = getpid();
+ saved_argc = argc;
+ if(!argc){
+ fprintf(stderr,"ERROR: could not determine own name.\n");
+ exit(1);
+ }
+ tmpstr=strrchr(*argv,'/');
+ if(tmpstr) tmpstr++;
+ if(!tmpstr) tmpstr=*argv;
+ program = PROG_GARBAGE;
+ if(*tmpstr=='s'){
+ setpriority(PRIO_PROCESS,my_pid,-20);
+ if(!strcmp(tmpstr,"snice")) program = PROG_SNICE;
+ if(!strcmp(tmpstr,"skill")) program = PROG_SKILL;
+ }else{
+ if(!strcmp(tmpstr,"kill")) program = PROG_KILL;
+ }
+ switch(program){
+ case PROG_SNICE:
+ case PROG_SKILL:
+ skillsnice_parse(argc, argv);
+/* show_lists(); */
+ iterate(); /* this is it, go get them */
+ break;
+ case PROG_KILL:
+ kill_main(argc, argv);
+ break;
+ default:
+ fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr);
+ }
+ return 0;
+}
+
+
diff --git a/smartt-top/slabtop.1 b/smartt-top/slabtop.1
new file mode 100644
index 0000000..7e27980
--- /dev/null
+++ b/smartt-top/slabtop.1
@@ -0,0 +1,129 @@
+.\" slabtop.1 - manpage for the slabtop(1) utility, part of procps
+.\"
+.\" Copyright (C) 2003 Chris Rivera
+.\" Licensed under the terms of the GNU Library General Public License, v2
+.TH SLABTOP 1 "13 Sep 2003" "Linux" "Linux User's Manual"
+.SH NAME
+slabtop \- display kernel slab cache information in real time
+
+.SH SYNOPSIS
+.B slabtop
+.RI [ options ]
+
+.SH DESCRIPTION
+.B slabtop
+displays detailed kernel slab cache information in real time. It displays a
+listing of the top caches sorted by one of the listed sort criteria. It also
+displays a statistics header filled with slab layer information.
+
+.SH OPTIONS
+Normal invocation of
+.B slabtop
+does not require any options. The behavior, however, can be fine-tuned by
+specifying one or more of the following flags:
+.TP
+.B \-\-delay=\fIn\fR, \fB\-d \fIn
+Refresh the display every
+.I n
+in seconds. By default,
+.B slabtop
+refreshes the display every three seconds. To exit the program, hit
+.BR q.
+.TP
+.B \-\-sort=\fIS\fR, \fB\-s\fR \fIS
+Sort by \fIS\fR, where \fIS\fR is one of the sort criteria.
+.TP
+.B \-\-once\fR, \fB\-o
+Display the output once and then exit.
+.TP
+.B \-\-version\fR, \fB\-V
+Display version information and exit.
+.TP
+.B \-\-help
+Display usage information and exit.
+
+.SH SORT CRITERIA
+The following are valid sort criteria used to sort the individual slab caches
+and thereby determine what are the "top" slab caches to display. The default
+sort criteria is to sort by the number of objects ("o").
+
+The sort criteria can also be changed while slabtop is running by pressing
+the associated character.
+.TP
+.BR a:
+sort by number of active objects
+.TP
+.BR b:
+sort by objects per slab
+.TP
+.BR c:
+sort by cache size
+.TP
+.BR l:
+sort by number of slabs
+.TP
+.BR v
+sort by number of active slabs
+.TP
+.BR n:
+sort by name
+.TP
+.BR o:
+sort by number of objects
+.TP
+.BR p:
+sort by pages per slab
+.TP
+.BR s:
+sort by object size
+.TP
+.BR u:
+sort by cache utilization
+
+.SH COMMANDS
+.B slabtop
+accepts keyboard commands from the user during use. The following are
+supported. In the case of letters, both cases are accepted.
+
+Each of the valid sort characters are also accepted, to change the sort
+routine. See the section
+.IR "SORT CRITERIA" .
+
+.TP
+.BR <SPACEBAR>
+Refresh the screen.
+.TP
+.BR Q
+Quit the program.
+
+.SH FILES
+.TP
+.I /proc/slabinfo
+slab information
+
+.SH "SEE ALSO"
+.BR free (1),
+.BR ps (1),
+.BR top (1),
+.BR vmstat (8)
+
+.SH NOTES
+Currently,
+.B slabtop
+requires a 2.4 or later kernel (specifically, a version 1.1 or later
+.IR /proc/slabinfo ).
+Kernel 2.2 should be supported in the future.
+
+The slabtop statistic header is tracking how many bytes of slabs are being used
+and it not a measure of physical memory. The 'Slab' field in the /proc/meminfo
+file is tracking information about used slab physical memory.
+
+.SH AUTHORS
+Written by Chris Rivera and Robert Love.
+
+.B slabtop
+was inspired by Martin Bligh's perl script,
+.BR vmtop .
+The procps package is maintained by Albert Cahalan <albert@users.sf.net>.
+
+Please send bug reports to <procps-feedback@lists.sf.net>.
diff --git a/smartt-top/slabtop.c b/smartt-top/slabtop.c
new file mode 100644
index 0000000..5c9d31e
--- /dev/null
+++ b/smartt-top/slabtop.c
@@ -0,0 +1,404 @@
+/*
+ * slabtop.c - utility to display kernel slab information.
+ *
+ * Chris Rivera <cmrivera@ufl.edu>
+ * Robert Love <rml@tech9.net>
+ *
+ * This program is licensed under the GNU Library General Public License, v2
+ *
+ * Copyright (C) 2003 Chris Rivera
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <ncurses.h>
+#include <termios.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "proc/slab.h"
+#include "proc/version.h"
+
+#define DEF_SORT_FUNC sort_nr_objs
+#define SLAB_STAT_ZERO { nr_objs: 0 }
+
+static unsigned short cols, rows;
+static struct termios saved_tty;
+static long delay = 3;
+static int (*sort_func)(const struct slab_info *, const struct slab_info *);
+
+static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b)
+{
+ struct slab_info sorted_list;
+ struct slab_info *curr = &sorted_list;
+
+ while ((a != NULL) && (b != NULL)) {
+ if (sort_func(a, b)) {
+ curr->next = a;
+ curr = a;
+ a = a->next;
+ } else {
+ curr->next = b;
+ curr = b;
+ b = b->next;
+ }
+ }
+
+ curr->next = (a == NULL) ? b : a;
+ return sorted_list.next;
+}
+
+/*
+ * slabsort - merge sort the slab_info linked list based on sort_func
+ */
+static struct slab_info *slabsort(struct slab_info *list)
+{
+ struct slab_info *a, *b;
+
+ if ((list == NULL) || (list->next == NULL))
+ return list;
+
+ a = list;
+ b = list->next;
+
+ while ((b != NULL) && (b->next != NULL)) {
+ list = list->next;
+ b = b->next->next;
+ }
+
+ b = list->next;
+ list->next = NULL;
+
+ return merge_objs(slabsort(a), slabsort(b));
+}
+
+/*
+ * Sort Routines. Each of these should be associated with a command-line
+ * search option. The functions should fit the prototype:
+ *
+ * int sort_foo(const struct slab_info *a, const struct slab_info *b)
+ *
+ * They return one if the first parameter is larger than the second
+ * Otherwise, they return zero.
+ */
+
+static int sort_name(const struct slab_info *a, const struct slab_info *b)
+{
+ return (strcmp(a->name, b->name) < 0) ? 1 : 0;
+}
+
+static int sort_nr_objs(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->nr_objs > b->nr_objs);
+}
+
+static int sort_nr_active_objs(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->nr_active_objs > b->nr_active_objs);
+}
+
+static int sort_obj_size(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->obj_size > b->obj_size);
+}
+
+static int sort_objs_per_slab(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->objs_per_slab > b->objs_per_slab);
+}
+
+static int sort_pages_per_slab(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->pages_per_slab > b->pages_per_slab);
+}
+
+static int sort_nr_slabs(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->nr_slabs > b->nr_slabs);
+}
+
+static int sort_nr_active_slabs(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->nr_active_slabs > b->nr_active_slabs);
+}
+
+
+static int sort_use(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->use > b->use);
+}
+
+static int sort_cache_size(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->cache_size > b->cache_size);
+}
+
+/*
+ * term_size - set the globals 'cols' and 'rows' to the current terminal size
+ */
+static void term_size(int unused)
+{
+ struct winsize ws;
+ (void) unused;
+
+ if ((ioctl(1, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) {
+ cols = ws.ws_col;
+ rows = ws.ws_row;
+ } else {
+ cols = 80;
+ rows = 24;
+ }
+}
+
+static void sigint_handler(int unused)
+{
+ (void) unused;
+
+ delay = 0;
+}
+
+static void usage(const char *cmd)
+{
+ fprintf(stderr, "usage: %s [options]\n\n", cmd);
+ fprintf(stderr, "options:\n");
+ fprintf(stderr, " --delay=n, -d n "
+ "delay n seconds between updates\n");
+ fprintf(stderr, " --once, -o "
+ "only display once, then exit\n");
+ fprintf(stderr, " --sort=S, -s S "
+ "specify sort criteria S (see below)\n");
+ fprintf(stderr, " --version, -V "
+ "display version information and exit\n");
+ fprintf(stderr, " --help display this help and exit\n\n");
+ fprintf(stderr, "The following are valid sort criteria:\n");
+ fprintf(stderr, " a: sort by number of active objects\n");
+ fprintf(stderr, " b: sort by objects per slab\n");
+ fprintf(stderr, " c: sort by cache size\n");
+ fprintf(stderr, " l: sort by number of slabs\n");
+ fprintf(stderr, " v: sort by number of active slabs\n");
+ fprintf(stderr, " n: sort by name\n");
+ fprintf(stderr, " o: sort by number of objects\n");
+ fprintf(stderr, " p: sort by pages per slab\n");
+ fprintf(stderr, " s: sort by object size\n");
+ fprintf(stderr, " u: sort by cache utilization\n");
+}
+
+/*
+ * set_sort_func - return the slab_sort_func that matches the given key.
+ * On unrecognizable key, DEF_SORT_FUNC is returned.
+ */
+static void * set_sort_func(char key)
+{
+ switch (key) {
+ case 'n':
+ return sort_name;
+ case 'o':
+ return sort_nr_objs;
+ case 'a':
+ return sort_nr_active_objs;
+ case 's':
+ return sort_obj_size;
+ case 'b':
+ return sort_objs_per_slab;
+ case 'p':
+ return sort_pages_per_slab;
+ case 'l':
+ return sort_nr_slabs;
+ case 'v':
+ return sort_nr_active_slabs;
+ case 'c':
+ return sort_cache_size;
+ case 'u':
+ return sort_use;
+ default:
+ return DEF_SORT_FUNC;
+ }
+}
+
+static void parse_input(char c)
+{
+ c = toupper(c);
+ switch(c) {
+ case 'A':
+ sort_func = sort_nr_active_objs;
+ break;
+ case 'B':
+ sort_func = sort_objs_per_slab;
+ break;
+ case 'C':
+ sort_func = sort_cache_size;
+ break;
+ case 'L':
+ sort_func = sort_nr_slabs;
+ break;
+ case 'V':
+ sort_func = sort_nr_active_slabs;
+ break;
+ case 'N':
+ sort_func = sort_name;
+ break;
+ case 'O':
+ sort_func = sort_nr_objs;
+ break;
+ case 'P':
+ sort_func = sort_pages_per_slab;
+ break;
+ case 'S':
+ sort_func = sort_obj_size;
+ break;
+ case 'U':
+ sort_func = sort_use;
+ break;
+ case 'Q':
+ delay = 0;
+ break;
+ }
+}
+
+#define print_line(fmt, args...) if (run_once) printf(fmt, ## args); else printw(fmt, ## args)
+int main(int argc, char *argv[])
+{
+ int o;
+ unsigned short old_rows;
+ struct slab_info *slab_list = NULL;
+ int run_once=0;
+
+ struct option longopts[] = {
+ { "delay", 1, NULL, 'd' },
+ { "sort", 1, NULL, 's' },
+ { "once", 0, NULL, 'o' },
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ sort_func = DEF_SORT_FUNC;
+
+ while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) {
+ int ret = 1;
+
+ switch (o) {
+ case 'd':
+ errno = 0;
+ delay = strtol(optarg, NULL, 10);
+ if (errno) {
+ perror("strtoul");
+ return 1;
+ }
+ if (delay < 0) {
+ fprintf(stderr, "error: can't have a "\
+ "negative delay\n");
+ exit(1);
+ }
+ break;
+ case 's':
+ sort_func = set_sort_func(optarg[0]);
+ break;
+ case 'o':
+ run_once=1;
+ delay = 0;
+ break;
+ case 'V':
+ display_version();
+ return 0;
+ case 'h':
+ ret = 0;
+ default:
+ usage(argv[0]);
+ return ret;
+ }
+ }
+
+ if (tcgetattr(0, &saved_tty) == -1)
+ perror("tcgetattr");
+
+ old_rows = rows;
+ term_size(0);
+ if (!run_once) {
+ initscr();
+ resizeterm(rows, cols);
+ signal(SIGWINCH, term_size);
+ }
+ signal(SIGINT, sigint_handler);
+
+ do {
+ struct slab_info *curr;
+ struct slab_stat stats = SLAB_STAT_ZERO;
+ struct timeval tv;
+ fd_set readfds;
+ char c;
+ int i;
+
+ if (get_slabinfo(&slab_list, &stats))
+ break;
+
+ if (!run_once && old_rows != rows) {
+ resizeterm(rows, cols);
+ old_rows = rows;
+ }
+
+ move(0,0);
+ print_line( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n"
+ " Minimum / Average / Maximum Object : %.2fK / %.2fK / %.2fK\n\n",
+ stats.nr_active_objs, stats.nr_objs, 100.0 * stats.nr_active_objs / stats.nr_objs,
+ stats.nr_active_slabs, stats.nr_slabs, 100.0 * stats.nr_active_slabs / stats.nr_slabs,
+ stats.nr_active_caches, stats.nr_caches, 100.0 * stats.nr_active_caches / stats.nr_caches,
+ stats.active_size / 1024.0, stats.total_size / 1024.0, 100.0 * stats.active_size / stats.total_size,
+ stats.min_obj_size / 1024.0, stats.avg_obj_size / 1024.0, stats.max_obj_size / 1024.0
+ );
+
+ slab_list = slabsort(slab_list);
+
+ attron(A_REVERSE);
+ print_line( "%6s %6s %4s %8s %6s %8s %10s %-23s\n",
+ "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS",
+ "OBJ/SLAB", "CACHE SIZE", "NAME");
+ attroff(A_REVERSE);
+
+ curr = slab_list;
+ for (i = 0; i < rows - 8 && curr->next; i++) {
+ print_line("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
+ curr->nr_objs, curr->nr_active_objs, curr->use,
+ curr->obj_size / 1024.0, curr->nr_slabs,
+ curr->objs_per_slab, (unsigned)(curr->cache_size / 1024),
+ curr->name);
+ curr = curr->next;
+ }
+
+ put_slabinfo(slab_list);
+
+ if (!run_once) {
+ refresh();
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ tv.tv_sec = delay;
+ tv.tv_usec = 0;
+ if (select(1, &readfds, NULL, NULL, &tv) > 0) {
+ if (read(0, &c, 1) != 1)
+ break;
+ parse_input(c);
+ }
+ }
+ } while (delay);
+
+ tcsetattr(0, TCSAFLUSH, &saved_tty);
+ free_slabinfo(slab_list);
+ if (!run_once) endwin();
+ return 0;
+}
diff --git a/smartt-top/snice.1 b/smartt-top/snice.1
new file mode 100644
index 0000000..1595a80
--- /dev/null
+++ b/smartt-top/snice.1
@@ -0,0 +1 @@
+.so man1/skill.1
diff --git a/smartt-top/sysctl.8 b/smartt-top/sysctl.8
new file mode 100644
index 0000000..3bb46d7
--- /dev/null
+++ b/smartt-top/sysctl.8
@@ -0,0 +1,117 @@
+.\" Copyright 1999, George Staikos (staikos@0wned.org)
+.\" This file may be used subject to the terms and conditions of the
+.\" GNU General Public License Version 2, or any later version
+.\" at your option, as published by the Free Software Foundation.
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details."
+.TH SYSCTL 8 "21 Sep 1999" "" ""
+.SH NAME
+sysctl \- configure kernel parameters at runtime
+.SH SYNOPSIS
+.B sysctl
+.RB [ \-n ]
+.RB [ \-e ]
+.I variable
+\&...
+.br
+.B sysctl
+.RB [ \-n ]
+.RB [ \-e ]
+.RB [ \-q ]
+.B \-w
+.IR variable = value
+\&...
+.br
+.B sysctl
+.RB [ \-n ]
+.RB [ \-e ]
+.RB [ \-q ]
+.B \-p
+.RI [ filename ]
+.br
+.B sysctl
+.RB [ \-n ]
+.RB [ \-e ]
+.B \-a
+.br
+.B sysctl
+.RB [ \-n ]
+.RB [ \-e ]
+.B \-A
+.SH DESCRIPTION
+.B sysctl
+is used to modify kernel parameters at runtime. The parameters available
+are those listed under /proc/sys/. Procfs is required for
+.B sysctl
+support in Linux. You can use
+.B sysctl
+to both read and write sysctl data.
+.SH PARAMETERS
+.TP
+.I variable
+The name of a key to read from. An example is kernel.ostype. The '/'
+separator is also accepted in place of a '.'.
+.TP
+.IR variable = value
+To set a key, use the form
+.IR variable = value
+where
+.I variable
+is the key and
+.I value
+is the value to set it to. If the value contains quotes or characters
+which are parsed by the shell, you may need to enclose the value in double
+quotes. This requires the
+.B \-w
+parameter to use.
+.TP
+.B \-n
+Use this option to disable printing of the key name when printing values.
+.TP
+.B \-e
+Use this option to ignore errors about unknown keys.
+.TP
+.B \-N
+Use this option to only print the names. It may be useful with shells that
+have programmable completion.
+.TP
+.B \-q
+Use this option to not display the values set to stdout.
+.TP
+.B \-w
+Use this option when you want to change a sysctl setting.
+.TP
+.B \-p
+Load in sysctl settings from the file specified or /etc/sysctl.conf if none given.
+Specifying \- as filename means reading data from standard input.
+.TP
+.B \-a
+Display all values currently available.
+.TP
+.B \-A
+Display all values currently available in table form.
+.SH EXAMPLES
+/sbin/sysctl \-a
+.br
+/sbin/sysctl \-n kernel.hostname
+.br
+/sbin/sysctl \-w kernel.domainname="example.com"
+.br
+/sbin/sysctl \-p /etc/sysctl.conf
+.SH FILES
+.I /proc/sys
+.br
+.I /etc/sysctl.conf
+.SH SEE ALSO
+.BR sysctl.conf (5)
+.SH BUGS
+The
+.B \-A
+parameter behaves just as
+.B \-a
+does.
+.SH AUTHOR
+George Staikos, <staikos@0wned.org>
+
diff --git a/smartt-top/sysctl.c b/smartt-top/sysctl.c
new file mode 100644
index 0000000..4024f71
--- /dev/null
+++ b/smartt-top/sysctl.c
@@ -0,0 +1,524 @@
+
+/*
+ * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
+ *
+ *
+ * "Copyright 1999 George Staikos
+ * This file may be used subject to the terms and conditions of the
+ * GNU General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details."
+ *
+ * Changelog:
+ * v1.01:
+ * - added -p <preload> to preload values from a file
+ * Horms:
+ * - added -q to be quiet when modifying values
+ *
+ * Changes by Albert Cahalan, 2002.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include "proc/procps.h"
+#include "proc/version.h"
+
+
+// Proof that C++ causes brain damage:
+typedef int bool;
+static bool true = 1;
+static bool false = 0;
+
+/*
+ * Globals...
+ */
+
+static const char PROC_PATH[] = "/proc/sys/";
+static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
+static bool NameOnly;
+static bool PrintName;
+static bool PrintNewline;
+static bool IgnoreError;
+static bool Quiet;
+
+/* error messages */
+static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter \"%s\"\n";
+static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting \"%s\"\n";
+static const char ERR_NO_EQUALS[] = "error: \"%s\" must be of the form name=value\n";
+static const char ERR_INVALID_KEY[] = "error: \"%s\" is an unknown key\n";
+static const char ERR_UNKNOWN_WRITING[] = "error: \"%s\" setting key \"%s\"\n";
+static const char ERR_UNKNOWN_READING[] = "error: \"%s\" reading key \"%s\"\n";
+static const char ERR_PERMISSION_DENIED[] = "error: permission denied on key '%s'\n";
+static const char ERR_OPENING_DIR[] = "error: unable to open directory \"%s\"\n";
+static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n";
+static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n";
+
+
+static void slashdot(char *restrict p, char old, char new){
+ p = strpbrk(p,"/.");
+ if(!p) return; /* nothing -- can't be, but oh well */
+ if(*p==new) return; /* already in desired format */
+ while(p){
+ char c = *p;
+ if(c==old) *p=new;
+ if(c==new) *p=old;
+ p = strpbrk(p+1,"/.");
+ }
+}
+
+
+
+/*
+ * Display the usage format
+ *
+ */
+static int Usage(const char *restrict const name) {
+ printf("usage: %s [-n] [-e] variable ... \n"
+ " %s [-n] [-e] [-q] -w variable=value ... \n"
+ " %s [-n] [-e] -a \n"
+ " %s [-n] [-e] [-q] -p <file> (default /etc/sysctl.conf) \n"
+ " %s [-n] [-e] -A\n", name, name, name, name, name);
+ return -1;
+}
+
+
+/*
+ * Strip the leading and trailing spaces from a string
+ *
+ */
+static char *StripLeadingAndTrailingSpaces(char *oneline) {
+ char *t;
+
+ if (!oneline || !*oneline)
+ return oneline;
+
+ t = oneline;
+ t += strlen(oneline)-1;
+
+ while ((*t==' ' || *t=='\t' || *t=='\n' || *t=='\r') && t!=oneline)
+ *t-- = 0;
+
+ t = oneline;
+
+ while ((*t==' ' || *t=='\t') && *t!=0)
+ t++;
+
+ return t;
+}
+
+static int DisplayAll(const char *restrict const path);
+
+/*
+ * Read a sysctl setting
+ *
+ */
+static int ReadSetting(const char *restrict const name) {
+ int rc = 0;
+ char *restrict tmpname;
+ char *restrict outname;
+ char inbuf[1025];
+ FILE *restrict fp;
+
+ if (!name || !*name) {
+ fprintf(stderr, ERR_INVALID_KEY, name);
+ return -1;
+ }
+
+ /* used to open the file */
+ tmpname = malloc(strlen(name)+strlen(PROC_PATH)+2);
+ strcpy(tmpname, PROC_PATH);
+ strcat(tmpname, name);
+ slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
+
+ /* used to display the output */
+ outname = strdup(name);
+ slashdot(outname,'/','.'); /* change / to . */
+
+ fp = fopen(tmpname, "r");
+
+ if (!fp) {
+ switch(errno) {
+ case ENOENT:
+ if (!IgnoreError) {
+ fprintf(stderr, ERR_INVALID_KEY, outname);
+ rc = -1;
+ }
+ break;
+ case EACCES:
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+ rc = -1;
+ break;
+ default:
+ fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
+ rc = -1;
+ break;
+ }
+ } else {
+ if(fgets(inbuf, sizeof inbuf - 1, fp)) {
+ // this loop is required, see
+ // /sbin/sysctl -a | egrep -6 dev.cdrom.info
+ do {
+ if (NameOnly) {
+ fprintf(stdout, "%s\n", outname);
+ } else {
+ /* already has the \n in it */
+ if (PrintName) {
+ fprintf(stdout, "%s = %s", outname, inbuf);
+ } else {
+ if (!PrintNewline) {
+ char *nlptr = strchr(inbuf,'\n');
+ if(nlptr) *nlptr='\0';
+ }
+ fprintf(stdout, "%s", inbuf);
+ }
+ }
+ } while(fgets(inbuf, sizeof inbuf - 1, fp));
+ } else {
+ switch(errno) {
+ case EACCES:
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+ rc = -1;
+ break;
+ case EISDIR:{
+ size_t len;
+ len = strlen(tmpname);
+ tmpname[len] = '/';
+ tmpname[len+1] = '\0';
+ rc = DisplayAll(tmpname);
+ break;
+ }
+ default:
+ fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
+ rc = -1;
+ break;
+ }
+ }
+ fclose(fp);
+ }
+
+ free(tmpname);
+ free(outname);
+ return rc;
+}
+
+
+
+/*
+ * Display all the sysctl settings
+ *
+ */
+static int DisplayAll(const char *restrict const path) {
+ int rc = 0;
+ int rc2;
+ DIR *restrict dp;
+ struct dirent *restrict de;
+ struct stat ts;
+
+ dp = opendir(path);
+
+ if (!dp) {
+ fprintf(stderr, ERR_OPENING_DIR, path);
+ rc = -1;
+ } else {
+ readdir(dp); // skip .
+ readdir(dp); // skip ..
+ while (( de = readdir(dp) )) {
+ char *restrict tmpdir;
+ tmpdir = (char *restrict)malloc(strlen(path)+strlen(de->d_name)+2);
+ sprintf(tmpdir, "%s%s", path, de->d_name);
+ rc2 = stat(tmpdir, &ts);
+ if (rc2 != 0) {
+ perror(tmpdir);
+ } else {
+ if (S_ISDIR(ts.st_mode)) {
+ strcat(tmpdir, "/");
+ DisplayAll(tmpdir);
+ } else {
+ rc |= ReadSetting(tmpdir+strlen(PROC_PATH));
+ }
+ }
+ free(tmpdir);
+ }
+ closedir(dp);
+ }
+ return rc;
+}
+
+
+/*
+ * Write a sysctl setting
+ *
+ */
+static int WriteSetting(const char *setting) {
+ int rc = 0;
+ const char *name = setting;
+ const char *value;
+ const char *equals;
+ char *tmpname;
+ FILE *fp;
+ char *outname;
+
+ if (!name) { /* probably don't want to display this err */
+ return 0;
+ } /* end if */
+
+ equals = index(setting, '=');
+
+ if (!equals) {
+ fprintf(stderr, ERR_NO_EQUALS, setting);
+ return -1;
+ }
+
+ value = equals + 1; /* point to the value in name=value */
+
+ if (!*name || !*value || name == equals) {
+ fprintf(stderr, ERR_MALFORMED_SETTING, setting);
+ return -2;
+ }
+
+ /* used to open the file */
+ tmpname = malloc(equals-name+1+strlen(PROC_PATH));
+ strcpy(tmpname, PROC_PATH);
+ strncat(tmpname, name, (int)(equals-name));
+ tmpname[equals-name+strlen(PROC_PATH)] = 0;
+ slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
+
+ /* used to display the output */
+ outname = malloc(equals-name+1);
+ strncpy(outname, name, (int)(equals-name));
+ outname[equals-name] = 0;
+ slashdot(outname,'/','.'); /* change / to . */
+
+ fp = fopen(tmpname, "w");
+
+ if (!fp) {
+ switch(errno) {
+ case ENOENT:
+ if (!IgnoreError) {
+ fprintf(stderr, ERR_INVALID_KEY, outname);
+ rc = -1;
+ }
+ break;
+ case EACCES:
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+ rc = -1;
+ break;
+ default:
+ fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
+ rc = -1;
+ break;
+ }
+ } else {
+ rc = fprintf(fp, "%s\n", value);
+ if (rc < 0) {
+ fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
+ fclose(fp);
+ } else {
+ rc=fclose(fp);
+ if (rc != 0)
+ fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
+ }
+ if (rc==0 && !Quiet) {
+ if (NameOnly) {
+ fprintf(stdout, "%s\n", outname);
+ } else {
+ if (PrintName) {
+ fprintf(stdout, "%s = %s\n", outname, value);
+ } else {
+ if (PrintNewline)
+ fprintf(stdout, "%s\n", value);
+ else
+ fprintf(stdout, "%s", value);
+ }
+ }
+ }
+ }
+
+ free(tmpname);
+ free(outname);
+ return rc;
+}
+
+
+
+/*
+ * Preload the sysctl's from the conf file
+ * - we parse the file and then reform it (strip out whitespace)
+ *
+ */
+static int Preload(const char *restrict const filename) {
+ char oneline[256];
+ char buffer[256];
+ FILE *fp;
+ char *t;
+ int n = 0;
+ int rc = 0;
+ char *name, *value;
+
+ fp = (filename[0]=='-' && !filename[1])
+ ? stdin
+ : fopen(filename, "r")
+ ;
+
+ if (!fp) {
+ fprintf(stderr, ERR_PRELOAD_FILE, filename);
+ return -1;
+ }
+
+ while (fgets(oneline, sizeof oneline, fp)) {
+ n++;
+ t = StripLeadingAndTrailingSpaces(oneline);
+
+ if (strlen(t) < 2)
+ continue;
+
+ if (*t == '#' || *t == ';')
+ continue;
+
+ name = strtok(t, "=");
+ if (!name || !*name) {
+ fprintf(stderr, WARN_BAD_LINE, filename, n);
+ continue;
+ }
+
+ StripLeadingAndTrailingSpaces(name);
+
+ value = strtok(NULL, "\n\r");
+ if (!value || !*value) {
+ fprintf(stderr, WARN_BAD_LINE, filename, n);
+ continue;
+ }
+
+ while ((*value == ' ' || *value == '\t') && *value != 0)
+ value++;
+
+ // should NameOnly affect this?
+ sprintf(buffer, "%s=%s", name, value);
+ rc |= WriteSetting(buffer);
+ }
+
+ fclose(fp);
+ return rc;
+}
+
+
+
+/*
+ * Main...
+ *
+ */
+int main(int argc, char *argv[]) {
+ const char *me = (const char *)basename(argv[0]);
+ bool SwitchesAllowed = true;
+ bool WriteMode = false;
+ bool DisplayAllOpt = false;
+ int ReturnCode = 0;
+ const char *preloadfile = DEFAULT_PRELOAD;
+
+ PrintName = true;
+ PrintNewline = true;
+ IgnoreError = false;
+ Quiet = false;
+
+ if (argc < 2) {
+ return Usage(me);
+ }
+
+ argv++;
+
+ for (; argv && *argv && **argv; argv++) {
+ if (SwitchesAllowed && **argv == '-') { /* we have a switch */
+ if ((*argv)[1] && (*argv)[2]){ // don't yet handle "sysctl -ew"
+ if (!strcmp("--help",*argv)) {
+ Usage(me);
+ exit(0);
+ }
+ if (!strcmp("--version",*argv)) {
+ fprintf(stdout, "sysctl (%s)\n",procps_version);
+ exit(0);
+ }
+ fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
+ return Usage(me);
+ }
+ switch((*argv)[1]) {
+ case 'b':
+ /* This is "binary" format, which means more for BSD. */
+ PrintNewline = false;
+ /* FALL THROUGH */
+ case 'n':
+ PrintName = false;
+ break;
+ case 'e':
+ // For FreeBSD, -e means a "%s=%s\n" format. ("%s: %s\n" default)
+ // We (and NetBSD) use "%s = %s\n" always, and -e to ignore errors.
+ IgnoreError = true;
+ break;
+ case 'N':
+ NameOnly = true;
+ break;
+ case 'w':
+ SwitchesAllowed = false;
+ WriteMode = true;
+ break;
+ case 'f': // the NetBSD way
+ case 'p':
+ argv++;
+ if (argv && *argv && **argv) {
+ preloadfile = *argv;
+ }
+ return Preload(preloadfile);
+ case 'q':
+ Quiet = true;
+ break;
+ case 'o': // BSD: binary values too, 1st 16 bytes in hex
+ case 'x': // BSD: binary values too, whole thing in hex
+ /* does nothing */ ;
+ break;
+ case 'a': // string and integer values (for Linux, all of them)
+ case 'A': // same as -a -o
+ case 'X': // same as -a -x
+ DisplayAllOpt = true;
+ break;
+ case 'V':
+ fprintf(stdout, "sysctl (%s)\n",procps_version);
+ exit(0);
+ case 'd': // BSD: print description ("vm.kvm_size: Size of KVM")
+ case 'h': // BSD: human-readable (did FreeBSD 5 make -e default?)
+ case '?':
+ return Usage(me);
+ default:
+ fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
+ return Usage(me);
+ }
+ } else {
+ if (NameOnly && Quiet) // nonsense
+ return Usage(me);
+ if (DisplayAllOpt) // We cannot have values with -a
+ return Usage(me);
+ SwitchesAllowed = false;
+ if (WriteMode || index(*argv, '='))
+ ReturnCode = WriteSetting(*argv);
+ else
+ ReturnCode = ReadSetting(*argv);
+ }
+ }
+ if (DisplayAllOpt) {
+ if (Quiet)
+ return Usage(me);
+ return DisplayAll(PROC_PATH);
+ }
+
+ return ReturnCode;
+}
+
+
diff --git a/smartt-top/sysctl.conf b/smartt-top/sysctl.conf
new file mode 100644
index 0000000..720294f
--- /dev/null
+++ b/smartt-top/sysctl.conf
@@ -0,0 +1,60 @@
+# /etc/sysctl.conf - Configuration file for setting system variables
+# See sysctl.conf (5) for information.
+
+# you can have the CD-ROM close when you use it, and open
+# when you are done.
+#dev.cdrom.autoeject = 1
+#dev.cdrom.autoclose = 1
+
+# protection from the SYN flood attack
+net/ipv4/tcp_syncookies=1
+
+# see the evil packets in your log files
+net/ipv4/conf/all/log_martians=1
+
+# makes you vulnerable or not :-)
+net/ipv4/conf/all/accept_redirects=0
+net/ipv4/conf/all/accept_source_route=0
+net/ipv4/icmp_echo_ignore_broadcasts =1
+
+# needed for routing, including masquerading or NAT
+#net/ipv4/ip_forward=1
+
+# sets the port range used for outgoing connections
+#net.ipv4.ip_local_port_range = 32768 61000
+
+# Broken routers and obsolete firewalls will corrupt the window scaling
+# and ECN. Set these values to 0 to disable window scaling and ECN.
+# This may, rarely, cause some performance loss when running high-speed
+# TCP/IP over huge distances or running TCP/IP over connections with high
+# packet loss and modern routers. This sure beats dropped connections.
+#net.ipv4.tcp_default_win_scale = 0
+#net.ipv4.tcp_ecn = 0
+
+# Swapping too much or not enough? Disks spinning up when you'd
+# rather they didn't? Tweak these.
+#vm.vfs_cache_pressure = 100
+#vm.laptop_mode = 0
+#vm.swappiness = 60
+
+#kernel.printk_ratelimit_burst = 10
+#kernel.printk_ratelimit = 5
+#kernel.panic_on_oops = 0
+
+# Reboot 600 seconds after a panic
+#kernel.panic = 600
+
+# enable SysRq key (note: console security issues)
+#kernel.sysrq = 1
+
+# Change name of core file to start with the command name
+# so you get things like: emacs.core mozilla-bin.core X.core
+#kernel.core_pattern = %e.core
+
+# NIS/YP domain (not always equal to DNS domain)
+#kernel.domainname = example.com
+#kernel.hostname = darkstar
+
+# This limits PID values to 4 digits, which allows tools like ps
+# to save screen space.
+kernel/pid_max=10000
diff --git a/smartt-top/sysctl.conf.5 b/smartt-top/sysctl.conf.5
new file mode 100644
index 0000000..0d8b073
--- /dev/null
+++ b/smartt-top/sysctl.conf.5
@@ -0,0 +1,51 @@
+.\" Copyright 1999, George Staikos (staikos@0wned.org)
+.\" This file may be used subject to the terms and conditions of the
+.\" GNU General Public License Version 2, or any later version
+.\" at your option, as published by the Free Software Foundation.
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details."
+.TH SYSCTL.CONF 5 "21 Sep 1999" "" ""
+.SH NAME
+sysctl.conf \- sysctl(8) preload/configuration file
+.SH DESCRIPTION
+.I sysctl.conf
+is a simple file containing sysctl values to be read in and set by sysctl(8).
+The syntax is simply as follows:
+.RS
+.sp
+.nf
+.ne 7
+# comment
+; comment
+
+ token = value
+.fi
+.sp
+.RE
+.PP
+Note that blank lines are ignored, and whitespace before and after a token or
+value is ignored, although a value can contain whitespace within. Lines which
+begin with a # or ; are considered comments and ignored.
+.SH EXAMPLE
+.RS
+.sp
+.nf
+.ne 7
+# sysctl.conf sample
+#
+
+ kernel.domainname = example.com
+; this one has a space which will be written to the sysctl!
+ kernel.modprobe = /sbin/mod probe
+
+.fi
+.sp
+.RE
+.PP
+.SH SEE ALSO
+.BR sysctl(8)
+.SH AUTHOR
+George Staikos, <staikos@0wned.org>
+
diff --git a/smartt-top/t b/smartt-top/t
new file mode 100755
index 0000000..3842ee0
--- /dev/null
+++ b/smartt-top/t
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# Wow, using $* causes great pain with embedded spaces in arguments.
+# The "$@" won't break that into 2 arguments.
+#
+LD_LIBRARY_PATH=proc exec ./top "$@"
diff --git a/smartt-top/tload.1 b/smartt-top/tload.1
new file mode 100644
index 0000000..8ec8c43
--- /dev/null
+++ b/smartt-top/tload.1
@@ -0,0 +1,50 @@
+.\" -*-Nroff-*-
+.\" This page Copyright (C) 1993 Matt Welsh, mdw@tc.cornell.edu.
+.\" Freely distributable under the terms of the GPL
+.TH TLOAD 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+tload \- graphic representation of system load average
+.SH SYNOPSIS
+.B tload
+.RB [ "\-V" "] [" "\-s"
+.IR scale "] ["
+.BI "\-d" " delay"
+.RI "] [" tty ]
+.SH DESCRIPTION
+\fBtload\fP prints a graph of the current system load average to the
+specified \fItty\fP (or the tty of the tload process if none is specified).
+.SS Options
+The
+.BI "\-s" " scale"
+option allows a vertical scale to be specified for the
+display (in characters between graph ticks); thus, a smaller value
+represents a larger scale, and vice versa.
+
+The
+.BI "\-d" " delay"
+sets the delay between graph updates in seconds.
+.PP
+.SH FILES
+.I /proc/loadavg
+load average information
+
+.SH "SEE ALSO"
+.BR ps (1),
+.BR top (1),
+.BR uptime (1),
+.BR w (1)
+
+.SH BUGS
+The
+.BI "\-d" " delay"
+option sets the time argument for an
+.BR alarm (2);
+if \-d 0 is specified, the alarm is set to 0, which will never send the
+.B SIGALRM
+and update the display.
+
+.SH AUTHORS
+Branko Lankester, David Engel <david@ods.com>, and
+Michael K. Johnson <johnsonm@redhat.com>.
+
+Please send bug reports to <albert@users.sf.net>
diff --git a/smartt-top/tload.c b/smartt-top/tload.c
new file mode 100644
index 0000000..cece48c
--- /dev/null
+++ b/smartt-top/tload.c
@@ -0,0 +1,153 @@
+/*
+ * tload.c - terminal version of xload
+ *
+ * Options:
+ * -s initial scaling exponent (default = 6)
+ * -d delay
+ *
+ * Copyright (c) 1992 Branko Lankester
+ * /proc changes by David Engel (david@ods.com)
+ * Made a little more efficient by Michael K. Johnson (johnsonm@sunsite.unc.edu)
+ */
+#include "proc/version.h"
+#include "proc/sysinfo.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+static char *screen;
+
+static int nrows = 25;
+static int ncols = 80;
+static int scr_size;
+static int fd=1;
+static int dly=5;
+static jmp_buf jb;
+
+static void alrm(int signo)
+{
+ (void)signo;
+ signal(SIGALRM, alrm);
+ alarm(dly);
+}
+
+static void setsize(int i)
+{
+ struct winsize win;
+
+ signal(SIGWINCH, setsize);
+ if (ioctl(fd, TIOCGWINSZ, &win) != -1) {
+ if (win.ws_col > 0)
+ ncols = win.ws_col;
+ if (win.ws_row > 0)
+ nrows = win.ws_row;
+ }
+ scr_size = nrows * ncols;
+ if (screen == NULL)
+ screen = (char *) malloc(scr_size);
+ else
+ screen = (char *) realloc(screen, scr_size);
+
+ if (screen == NULL) {
+ perror("");
+ exit(1);
+ }
+ memset(screen, ' ', scr_size-1);
+ *(screen + scr_size - 2) = '\0';
+ if (i)
+ longjmp(jb, 0);
+}
+
+int main(int argc, char **argv)
+{
+ int lines, row, col=0;
+ int i, opt;
+ double av[3];
+ static double max_scale, scale_fact;
+ char *scale_arg = NULL;
+
+ while ((opt = getopt(argc, argv, "s:d:V")) != -1)
+ switch (opt) {
+ case 's': scale_arg = optarg; break;
+ case 'd': dly = atoi(optarg); break;
+ case 'V': display_version(); exit(0); break;
+ default:
+ printf("usage: tload [-V] [-d delay] [-s scale] [tty]\n");
+ exit(1);
+ }
+
+ if (argc > optind) {
+ if ((fd = open(argv[optind], 1)) == -1) {
+ perror(argv[optind]);
+ exit(1);
+ }
+ }
+
+ setsize(0);
+
+ if (scale_arg)
+ max_scale = atof(scale_arg);
+ else
+ max_scale = nrows;
+
+ scale_fact = max_scale;
+
+ setjmp(jb);
+ col = 0;
+ alrm(0);
+
+ while (1) {
+
+ if (scale_fact < max_scale)
+ scale_fact *= 2.0; /* help it drift back up. */
+
+ loadavg(&av[0], &av[1], &av[2]);
+
+ repeat:
+ lines = av[0] * scale_fact;
+ row = nrows-1;
+
+ while (--lines >= 0) {
+ *(screen + row * ncols + col) = '*';
+ if (--row < 0) {
+ scale_fact /= 2.0;
+ goto repeat;
+ }
+ }
+ while (row >= 0)
+ *(screen + row-- * ncols + col) = ' ';
+
+ for (i = 1; ; ++i) {
+ char *p;
+ row = nrows - (i * scale_fact);
+ if (row < 0)
+ break;
+ if (*(p = screen + row * ncols + col) == ' ')
+ *p = '-';
+ else
+ *p = '=';
+ }
+
+ if (++col == ncols) {
+ --col;
+ memmove(screen, screen + 1, scr_size-1);
+
+ for(row = nrows-2; row >= 0; --row)
+ *(screen + row * ncols + col) = ' ';
+ }
+ i = sprintf(screen, " %.2f, %.2f, %.2f",
+ av[0], av[1], av[2]);
+ if (i>0)
+ screen[i] = ' ';
+
+ write(fd, "\033[H", 3);
+ write(fd, screen, scr_size - 1);
+ pause();
+ }
+}
diff --git a/smartt-top/top.1 b/smartt-top/top.1
new file mode 100644
index 0000000..5b44828
--- /dev/null
+++ b/smartt-top/top.1
@@ -0,0 +1,1651 @@
+.ig
+. manual page for NEW top
+. Copyright (c) 2002, by: JC Warner & Associates, Ltd.
+.
+. Permission is granted to copy, distribute and/or modify this document
+. under the terms of the GNU Free Documentation License, Version 1.1 or
+. any later version published by the Free Software Foundation;
+. with no Front-Cover Texts, no Back-Cover Texts, and with the following
+. Invariant Sections (and any sub-sections therein):
+. all .ig sections, including this one
+. STUPID TRICKS Sampler
+. AUTHOR
+.
+. A copy of the Free Documentation License is included in the section
+. entitled "GNU Free Documentation License".
+.
+. [ that section is found near the end of this document & ]
+. [ can be made printable by disabling the .ig directive! ]
+.
+..
+.\" Setup ////////////////////////////////////////////////////////////////
+\# ** Comment out '.nr' or set to 0 to eliminate WIDTH fiddlin' !
+.nr half_xtra 4
+.
+.ll +(\n[half_xtra] + \n[half_xtra])
+.
+\# Our darn Bullet style ----------------------------
+.de Jbu
+.IP "-" 3
+..
+\# - bullet continuation paragraph
+.de Jp
+.IP "" 3
+..
+\# New features/differences style -------------------
+.de New
+.IP "-*-" 5
+..
+.
+\# Commonly used strings (for consistency) ----------
+\# - a real em-dash, darn-it
+.ds EM \ \fB\-\-\ \fR
+\# - these two are for chuckles, makes great grammar
+.ds Me top
+.ds NE \fBtop\fR
+\# - other misc strings for consistent usage/emphasis
+.ds F \fIOff\fR
+.ds O \fIOn\fR
+.
+.ds AM alternate\-display mode
+.ds AS asterisk ('*')
+.ds CF configuration file
+.ds CI interactive command
+.ds CO command\-line option
+.ds CW \'current' window
+.ds FM full\-screen mode
+.ds MP \fBphysical\fR memory
+.ds MS \fBshared\fR memory
+.ds MV \fBvirtual\fR memory
+.ds NT \fBNote\fR:
+.ds PU CPU
+.ds Pu cpu
+.ds SA summary area
+.ds TA task area
+.ds TD task display
+.ds TW task window
+\# - xref's that depend on commands or topic names
+.ds XC See the
+.ds Xc see the
+.ds XT See topic
+.ds Xt see topic
+.
+.\" //////////////////////////////////////////////////////////////////////
+.\" ----------------------------------------------------------------------
+.TH TOP 1 "September 2002" "Linux" "Linux User's Manual"
+.\" ----------------------------------------------------------------------
+
+
+.\" ----------------------------------------------------------------------
+.SH NAME
+.\" ----------------------------------------------------------------------
+top \- display Linux tasks
+
+
+.\" ----------------------------------------------------------------------
+.SH SYNOPSIS
+.\" ----------------------------------------------------------------------
+\*(NE \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI
+iterations\fR \-\fBp\fI pid\fR [,\fI pid\fR ...]
+
+The traditional switches '-' and whitespace are optional.
+
+
+.\" ----------------------------------------------------------------------
+.SH DESCRIPTION
+.\" ----------------------------------------------------------------------
+The \*(NE program provides a dynamic real-time view of a running system.
+It can display\fB system\fR summary information as well as a list of\fB
+tasks\fR currently being managed by the Linux kernel.
+The types of system summary information shown and the types, order and
+size of information displayed for tasks are all user configurable and
+that configuration can be made persistent across restarts.
+
+The program provides a limited interactive interface for process
+manipulation as well as a much more extensive interface for personal
+configuration \*(EM encompassing every aspect of its operation.
+And while \*(NE is referred to throughout this document, you are free
+to name the program anything you wish.
+That new name, possibly an alias, will then be reflected on \*(Me's display
+and used when reading and writing a \*(CF.
+
+
+.\" ----------------------------------------------------------------------
+.SH OVERVIEW
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS Documentation
+.\" ----------------------------------------------------------------------
+The remaining Table of Contents
+ 1. COMMAND\-LINE Options
+ 2. FIELDS / Columns
+ a. DESCRIPTIONS of Fields
+ b. SELECTING and ORDERING Columns
+ 3. INTERACTIVE Commands
+ a. GLOBAL Commands
+ b. SUMMARY Area Commands
+ c. TASK Area Commands
+ d. COLOR Mapping
+ 4. ALTERNATE\-DISPLAY Mode
+ a. WINDOWS Overview
+ b. COMMANDS for Windows
+ 5. FILES
+ a. SYSTEM Configuration File
+ b. PERSONAL Configuration File
+ 6. STUPID TRICKS Sampler
+ a. Kernel Magic
+ b. Bouncing Windows
+ c. The Big Bird Window
+ 7. BUGS, 8. HISTORY Former top, 9. AUTHOR, 10. SEE ALSO
+
+.\" ......................................................................
+.SS Operation
+.\" ----------------------------------------------------------------------
+When operating \*(Me, the two most important keys are help ('h' or '?') and
+quit ('q') key.
+Alternatively, you could simply use the traditional interrupt key ('^C')
+when you're done.
+
+When you start \*(Me for the first time, you'll be presented with the
+traditional screen elements: 1) Summary Area; 2) Message/Prompt Line;
+3) Columns Header; 4) Task Area.
+There will, however, be some differences when compared to the former top.
+
+.TP 3
+.B Highlighting
+.I Summary_Area\fR:
+There is no highlighting for load/uptime and only values are highlighted for
+other elements.
+
+.I Task_Area\fR:
+Tasks running (or ready to run) will be highlighted, and bold is only one way
+of emphasizing such processes.
+
+.TP 3
+.B Content/Labels
+.I Summary_Area\fR:
+The program name is shown, perhaps a symlink or alias.
+The Cpu(s) state label hints at other possibilities.
+The memory stats use a lower case 'k'.
+
+.I Columns_Header\fR:
+Will show a new field and some changed labels.
+More new fields will be found as you customize your \*(Me.
+
+.PP
+\*(NT the width of \*(Me's display will be limited to 512 positions.
+Displaying all fields requires a minimum of 160 characters.
+The remaining width could be used for the 'Command' column.
+
+.\" ......................................................................
+.SS Startup Defaults
+.\" ----------------------------------------------------------------------
+The following startup defaults assume no \*(CF, thus no user customizations.
+Even so, items shown with an \*(AS could be overridden through the
+command-line.
+
+ \fIGlobal_defaults\fR
+ 'A' - Alt display Off (full-screen)
+ * 'd' - Delay time 3.0 seconds
+ 'I' - Irix mode On\ \ (no, 'solaris' smp)
+ * 'p' - PID monitoring Off
+ * 's' - Secure mode Off (unsecured)
+ 'B' - Bold enable Off
+ \fISummary_Area_defaults\fR
+ 'l' - Load Avg/Uptime On\ \ (thus program name)
+ 't' - Task/Cpu states On\ \ (1+1 lines, see '1')
+ 'm' - Mem/Swap usage On\ \ (2 lines worth)
+ '1' - Single Cpu On\ \ (thus 1 line if smp)
+ \fITask_Area_defaults\fR
+ 'b' - Bold hilite On\ \ (not 'reverse')
+ * 'c' - Command line Off (name, not cmdline)
+ * 'H' - Threads Off\ (show all threads)
+ * 'i' - Idle tasks On\ \ (show all tasks)
+ 'R' - Reverse sort On\ \ (pids high-to-low)
+ * 'S' - Cumulative time Off (no, dead children)
+ 'x' - Column hilite Off\ (no, sort field)
+ 'y' - Row hilite On\ \ (yes, running tasks)
+ 'z' - color/mono Off\ (no, colors)
+
+
+.\" ----------------------------------------------------------------------
+.SH 1. COMMAND-LINE Options
+.\" ----------------------------------------------------------------------
+The command-line syntax for \*(Me consists of:
+
+ \-\fBhv\fR\ |\ -\fBbcHisS\fR\ \-\fBd\fI\ delay\fR\ \-\fBn\fI\ iterations\
+\fR\ \-\fBp\fI\ pid\fR\ [,\fIpid\fR...]
+
+The typically mandatory switches ('-') and even whitespace are completely
+optional.
+
+.TP 5
+\-\fBb\fR :\fB Batch mode\fR operation
+Starts \*(Me in 'Batch mode', which could be useful for sending output
+from \*(Me to other programs or to a file.
+In this mode, \*(Me will not accept input and runs until the iterations
+limit you've set with the '-n' \*(CO or until killed.
+
+.TP 5
+\-\fBc\fR :\fB Command line/Program name\fR toggle
+Starts \*(Me with the last remembered 'c' state reversed.
+Thus, if \*(Me was displaying command lines, now that field will show program
+names, and visa versa.
+\*(XC 'c' \*(CI for additional information.
+
+.TP 5
+\-\fBd\fR :\fB Delay time\fR interval as:\ \ \fB-d ss.tt\fR (\fIseconds\fR.\fItenths\fR)
+Specifies the delay between screen updates, and overrides the corresponding
+value in one's personal \*(CF or the startup default.
+Later this can be changed with the 'd' or 's' \*(CIs.
+
+Fractional seconds are honored, but a negative number is not allowed.
+In all cases, however, such changes are prohibited if \*(Me is running
+in 'Secure mode', except for root (unless the 's' \*(CO was used).
+For additional information on 'Secure mode' \*(Xt 5a. SYSTEM Configuration File.
+
+
+.TP 5
+\-\fBh\fR :\fB Help\fR
+Show library version and the usage prompt, then quit.
+
+.TP 5
+\-\fBH\fR :\fB Threads\fR toggle
+Starts \*(Me with the last remembered 'H' state reversed.
+When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process.
+
+.TP 5
+\-\fBi\fR :\fB Idle Processes\fR toggle
+Starts \*(Me with the last remembered 'i' state reversed.
+When this toggle is \*F, tasks that are idled or zombied will not be displayed.
+
+.TP 5
+\-\fBn\fR :\fB Number of iterations\fR limit as:\fB\ \ -n number\fR
+Specifies the maximum number of iterations, or frames, \*(Me should
+produce before ending.
+
+.TP 5
+\-\fBu\fR :\fB Monitor by user\fR as:\fB\ \ -u somebody
+Monitor only processes with an effective UID or user name
+matching that given.
+
+.TP 5
+\-\fBU\fR :\fB Monitor by user\fR as:\fB\ \ -U somebody
+Monitor only processes with a UID or user name matching that given.
+This matches real, effective, saved, and filesystem UIDs.
+
+.TP 5
+\-\fBp\fR :\fB Monitor PIDs\fR as:\fB\ \ -pN1 -pN2 ...\fR\ \ or\fB\ \ -pN1, N2 [,...]
+Monitor only processes with specified process IDs.
+This option can be given up to 20 times, or you can provide a comma delimited
+list with up to 20 pids.
+Co-mingling both approaches is permitted.
+
+This is a \*(CO only.
+And should you wish to return to normal operation, it is not necessary
+to quit and and restart \*(Me \*(EM just issue the '=' \*(CI.
+
+.TP 5
+\-\fBs\fR :\fB Secure mode\fR operation
+Starts \*(Me with secure mode forced, even for root.
+This mode is far better controlled through the system \*(CF
+(\*(Xt 5. FILES).
+
+.TP 5
+\-\fBS\fR :\fB Cumulative time mode\fR toggle
+Starts \*(Me with the last remembered 'S' state reversed.
+When 'Cumulative mode' is \*O, each process is listed with the \*(Pu
+time that it and its dead children have used.
+\*(XC 'S' \*(CI for additional information regarding this mode.
+
+.TP 5
+\-\fBv\fR :\fB Version\fR
+Show library version and the usage prompt, then quit.
+
+
+.\" ----------------------------------------------------------------------
+.SH 2. FIELDS / Columns
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS 2a. DESCRIPTIONS of Fields
+.\" ----------------------------------------------------------------------
+Listed below are \*(Me's available fields.
+They are always associated with the letter shown, regardless of the position
+you may have established for them with the 'o' (Order fields) \*(CI.
+
+Any field is selectable as the sort field, and you control whether they
+are sorted high-to-low or low-to-high.
+For additional information on sort provisions \*(Xt 3c. TASK Area Commands.
+
+.TP 3
+a:\fB PID\fR \*(EM Process Id\fR
+The task's unique process ID, which periodically wraps,
+though never restarting at zero.
+
+.TP 3
+b:\fB PPID\fR \*(EM Parent Process Pid\fR
+The process ID of a task's parent.
+
+.TP 3
+c:\fB RUSER\fR \*(EM Real User Name\fR
+The real user name of the task's owner.
+
+.TP 3
+d:\fB UID\fR \*(EM User Id\fR
+The effective user ID of the task's owner.
+
+.TP 3
+e:\fB USER\fR \*(EM User Name\fR
+The effective user name of the task's owner.
+
+.TP 3
+f:\fB GROUP\fR \*(EM Group Name\fR
+The effective group name of the task's owner.
+
+.TP 3
+g:\fB TTY\fR \*(EM Controlling Tty
+The name of the controlling terminal.
+This is usually the device (serial port, pty, etc.) from which the
+process was started, and which it uses for input or output.
+However, a task need not be associated with a terminal, in which case
+you'll see '?' displayed.
+
+.TP 3
+h:\fB PR\fR \*(EM Priority
+The priority of the task.
+
+.TP 3
+i:\fB NI\fR \*(EM Nice value
+The nice value of the task.
+A negative nice value means higher priority, whereas a positive nice value
+means lower priority.
+Zero in this field simply means priority will not be adjusted in determining a
+task's dispatchability.
+
+.TP 3
+j:\fB P\fR \*(EM Last used \*(PU (SMP)
+A number representing the last used processor.
+In a true SMP environment this will likely change frequently since the kernel
+intentionally uses weak affinity.
+Also, the very act of running \*(Me may break this weak affinity and cause more
+processes to change \*(PUs more often (because of the extra demand for
+\*(Pu time).
+
+.TP 3
+k:\fB %CPU\fR \*(EM \*(PU usage
+The task's share of the elapsed \*(PU time since the last screen update,
+expressed as a percentage of total \*(PU time.
+In a true SMP environment, if 'Irix mode' is \*F, \*(Me will operate
+in 'Solaris mode' where a task's \*(Pu usage will be divided by the total
+number of \*(PUs.
+You toggle 'Irix/Solaris' modes with the 'I' \*(CI.
+
+.TP 3
+l:\fB TIME\fR \*(EM \*(PU Time
+Total \*(PU time the task has used since it started.
+When 'Cumulative mode' is \*O, each process is listed with the \*(Pu
+time that it and its dead children has used.
+You toggle 'Cumulative mode' with 'S', which is a \*(CO and an \*(CI.
+\*(XC 'S' \*(CI for additional information regarding this mode.
+
+.TP 3
+m:\fB TIME+\fR \*(EM \*(PU Time, hundredths
+The same as 'TIME', but reflecting more granularity through hundredths of
+a second.
+
+.TP 3
+n:\fB %MEM\fR \*(EM Memory usage (RES)
+A task's currently used share of available \*(MP.
+
+.TP 3
+o:\fB VIRT\fR \*(EM Virtual Image (kb)
+The total amount of \*(MV used by the task.
+It includes all code, data and shared libraries plus pages that have been
+swapped out and pages that have been mapped but not used.
+
+.TP 3
+p:\fB SWAP\fR \*(EM Swapped size (kb)
+Memory that is not resident but is present in a task. This is memory that
+has been swapped out but could include additional non-resident memory.
+This column is calculated by subtracting \*(MP from \*(MV.
+
+.TP 3
+q:\fB RES\fR \*(EM Resident size (kb)
+The non-swapped \*(MP a task has used.
+
+.TP 3
+r:\fB CODE\fR \*(EM Code size (kb)
+The amount of \*(MV devoted to executable code, also known as
+the 'text resident set' size or TRS.
+
+.TP 3
+s:\fB DATA\fR \*(EM Data+Stack size (kb)
+The amount of \*(MV devoted to other than executable code, also known as
+the 'data resident set' size or DRS.
+
+.TP 3
+t:\fB SHR\fR \*(EM Shared Mem size (kb)
+The amount of \*(MS used by a task.
+It simply reflects memory that could be potentially shared with
+other processes.
+
+.TP 3
+u:\fB nFLT\fR \*(EM Page Fault count
+The number of\fB major\fR page faults that have occurred for a task.
+A page fault occurs when a process attempts to read from or write to a virtual
+page that is not currently present in its address space.
+A major page fault is when backing storage access (such as a disk) is involved
+in making that page available.
+
+.TP 3
+v:\fB nDRT\fR \*(EM Dirty Pages count
+The number of pages that have been modified since they were last
+written to disk.
+Dirty pages must be written to disk before the corresponding physical memory
+location can be used for some other virtual page.
+
+.TP 3
+w:\fB S\fR \*(EM Process Status
+The status of the task which can be one of:
+ '\fBD\fR' = uninterruptible sleep
+ '\fBR\fR' = running
+ '\fBS\fR' = sleeping
+ '\fBT\fR' = traced or stopped
+ '\fBZ\fR' = zombie
+
+Tasks shown as running should be more properly thought of as 'ready to run'
+\*(EM their task_struct is simply represented on the Linux run-queue.
+Even without a true SMP machine, you may see numerous tasks in this state
+depending on \*(Me's delay interval and nice value.
+
+.TP 3
+x:\fB Command\fR \*(EM Command\fB line\fR or Program\fB name\fR
+Display the command line used to start a task or the name of the associated
+program.
+You toggle between command\fI line\fR and\fI name\fR with 'c', which is both
+a \*(CO and an \*(CI.
+
+When you've chosen to display command lines, processes without a command
+line (like kernel threads) will be shown with only the program name in
+parentheses, as in this example:
+ \fR( mdrecoveryd )
+
+Either form of display is subject to potential truncation if it's too long to
+fit in this field's current width.
+That width depends upon other fields selected, their order and the current
+screen width.
+
+\*(NT The 'Command' field/column is unique, in that it is not fixed-width.
+When displayed, this column will be allocated all remaining screen width (up
+to the maximum 512 characters) to provide for the potential growth of program
+names into command lines.
+
+.TP 3
+y:\fB WCHAN\fR \*(EM Sleeping in Function
+Depending on the availability of the kernel link map ('System.map'), this field
+will show the name or the address of the kernel function in which the task is
+currently sleeping.
+Running tasks will display a dash ('-') in this column.
+
+\*(NT By displaying this field, \*(Me's own working set will be increased by
+over 700Kb.
+Your only means of reducing that overhead will be to stop and restart \*(Me.
+
+.TP 3
+z:\fB Flags\fR \*(EM Task Flags
+This column represents the task's current scheduling flags which are
+expressed in hexadecimal notation and with zeros suppressed.
+These flags are officially documented in <linux/sched.h>.
+Less formal documentation can also be found on the 'Fields select'
+and 'Order fields' screens.
+
+.\" ......................................................................
+.SS 2b. SELECTING and ORDERING Columns
+.\" ----------------------------------------------------------------------
+After pressing the \*(CIs 'f' (Fields select) or 'o' (Order fields) you will
+be shown a screen containing the current \fBfields string\fR followed by names
+and descriptions for all fields.
+
+Here is a sample\fB fields string\fR from one of \*(Me's four windows/field
+groups and an explanation of the conventions used:
+.Jbu
+Sample fields string:
+ \fIANOPQRSTUVXbcdefgjlmyzWHIK\fR
+.Jbu
+The order of displayed fields corresponds to the order of the letters
+in that string.
+.Jbu
+If the letter is\fI upper case\fR the corresponding field itself will
+then be shown as part of the \*(TD (screen width permitting).
+This will also be indicated by a leading \*(AS, as in this excerpt:
+ \fR...
+ \fB* K: %CPU = CPU usage
+ \fR l: TIME = CPU Time
+ \fR m: TIME+ = CPU Time, hundredths
+ \fB* N: %MEM = Memory usage (RES)
+ \fB* O: VIRT = Virtual Image (kb)
+ \fR...
+
+.TP
+.B Fields select\fR screen \*(EM the 'f' \*(CI
+You\fI toggle\fR the\fB display\fR of a field by simply pressing the
+corresponding letter.
+
+.TP
+.B Order fields\fR screen \*(EM the 'o' \*(CI
+You\fI move\fR a field to the\fB left\fR by pressing the corresponding\fB
+upper case\fR letter and to the\fB right\fR with the\fB lower case\fR
+letter.
+
+.\" ......................................................................
+.SS 2c. CPU States
+.\" ----------------------------------------------------------------------
+The CPU states are shown in the Summary Area. They are always shown as a
+percentage and are for the time between now and the last refresh.
+
+.TP 3
+\fB us\fR \*(EM User CPU time
+The time the CPU has spent running users' processes that are not
+niced.
+
+.TP 3
+\fB sy\fR \*(EM System CPU time
+The time the CPU has spent running the kernel and its processes.
+
+.TP 3
+\fB ni\fR \*(EM Nice CPU time
+The time the CPU has spent running users' proccess that have been
+niced.
+
+.TP 3
+\fB wa\fR \*(EM iowait
+Amount of time the CPU has been waiting for I/O to complete.
+
+.TP 3
+\fB hi\fR \*(EM Hardware IRQ
+The amount of time the CPU has been servicing hardware interrupts.
+
+.TP 3
+\fB si\fR \*(EM Software Interrupts
+The amount of time the CPU has been servicing software interrupts.
+
+.TP 3
+\fB st\fR \*(EM Steal Time
+The amount of CPU 'stolen' from this virtual machine by the hypervisor
+for other tasks (such as running another virtual machine).
+
+.\" ----------------------------------------------------------------------
+.SH 3. INTERACTIVE Commands
+.\" ----------------------------------------------------------------------
+Listed below is a brief index of commands within categories.
+Some commands appear more than once \*(EM their meaning or scope may vary
+depending on the context in which they are issued.
+
+ 3a.\fI GLOBAL_Commands\fR
+ <Ret/Sp> ?, =, A, B, d, G, h, I, k, q, r, s, W, Z
+ 3b.\fI SUMMARY_Area_Commands\fR
+ l, m, t, 1
+ 3c.\fI TASK_Area_Commands\fR
+ Appearance: b, x, y, z
+ Content: c, f, H, o, S, u
+ Size: #, i, n
+ Sorting: <, >, F, O, R
+ 3d.\fI COLOR_Mapping\fR
+ <Ret>, a, B, b, H, M, q, S, T, w, z, 0 - 7
+ 4b.\fI COMMANDS_for_Windows\fR
+ -, _, =, +, A, a, G, g, w
+
+.\" ......................................................................
+.SS 3a. GLOBAL Commands
+The global \*(CIs are\fB always\fR available\fR in both \*(FM and \*(AM.
+However, some of these \*(CIs are\fB not available\fR when running
+in 'Secure mode'.
+
+If you wish to know in advance whether or not your \*(Me has been secured,
+simply ask for help and view the system summary on the second line.
+
+.TP 7
+\ \ \<\fBEnter\fR> or <\fBSpace\fR> :\fIRefresh_Display\fR
+These commands do nothing, they are simply ignored.
+However, they will awaken \*(Me and following receipt of any input
+the entire display will be repainted.
+
+Use either of these keys if you have a large delay interval and wish to
+see current status,
+
+.TP 7
+\ \ \<\fB?\fR\> or \<\fBh\fR\> :\fIHelp\fR
+There are two help levels available.
+The first will provide a reminder of all the basic \*(CIs.
+If \*(Me is\fI secured\fR, that screen will be abbreviated.
+
+Typing 'h' or '?' on that help screen will take you to help for those \*(CIs
+applicable to \*(AM.
+
+.TP 7
+\ \ \<\fB=\fR\> :\fIExit_Task_Limits\fR
+Removes restrictions on which tasks are shown.
+This command will reverse any 'i' (idle tasks) and 'n' (max tasks) commands
+that might be active.
+It also provides for an 'exit' from PID monitoring.
+See the '-p' \*(CO for a discussion of PID monitoring.
+
+When operating in \*(AM this command has a slightly broader meaning.
+
+.TP 7
+\ \ \<\fBA\fR\> :\fIAlternate_Display_Mode_toggle\fR
+This command will switch between \*(FM and \*(AM.
+\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight
+into \*(CWs and field groups.
+
+.TP 7
+\ \ \<\fBB\fR\> :\fIBold_Disable/Enable_toggle\fR
+This command will influence use of the 'bold' terminfo capability and
+alters\fB both\fR the \*(SA and \*(TA for the \*(CW.
+While it is intended primarily for use with dumb terminals, it can be
+applied anytime.
+
+\*(NT When this toggle is \*O and \*(Me is operating in monochrome mode,
+the\fB entire display\fR will appear as normal text.
+Thus, unless the 'x' and/or 'y' toggles are using reverse for emphasis,
+there will be no visual confirmation that they are even on.
+
+.TP 7
+*\ \<\fBd\fR\> or \<\fBs\fR\> :\fIChange_Delay_Time_interval\fR
+You will be prompted to enter the delay time, in seconds, between
+display updates.
+
+Fractional seconds are honored, but a negative number is not allowed.
+Entering 0 causes (nearly) continuous updates, with an unsatisfactory
+display as the system and tty driver try to keep up with \*(Me's demands.
+The delay value is inversely proportional to system loading,
+so set it with care.
+
+If at any time you wish to know the current delay time, simply ask for help
+and view the system summary on the second line.
+
+.TP 7
+\ \ \<\fBG\fR\> :\fIChoose_Another_Window/Field_Group\fR
+You will be prompted to enter a number between 1 and 4 designating the
+window/field group which should be made the \*(CW.
+You will soon grow comfortable with these 4 windows, especially after
+experimenting with \*(AM.
+
+.TP 7
+\ \ \<\fBI\fR\> :\fIIrix/Solaris_Mode_toggle\fR
+When operating in 'Solaris mode' ('I' toggled \*F), a task's \*(Pu usage
+will be divided by the total number of \*(PUs.
+After issuing this command, you'll be informed of the new state of this toggle.
+
+.TP 7
+\ \ \<\fBu\fR\> :\fIselect a user\fR
+You will be prompted for a UID or username. Only processes
+belonging to the selected user will be displayed. This option
+matches on the effective UID.
+
+.TP 7
+\ \ \<\fBU\fR\> :\fIselect a user\fR
+You will be prompted for a UID or username. Only processes
+belonging to the selected user will be displayed. This option
+matches on the real, effective, saved, and filesystem UID.
+
+.TP 7
+*\ \<\fBk\fR\> :\fIKill_a_task\fR
+You will be prompted for a PID and then the signal to send.
+The default signal, as reflected in the prompt, is SIGTERM.
+However, you can send any signal, via number or name.
+
+If you wish to abort the kill process, do one of the following
+depending on your progress:
+ 1) at the pid prompt, just press <Enter>
+ 2) at the signal prompt, type 0
+
+.TP 7
+\ \ \<\fBq\fR\> :\fIQuit\fR
+
+.TP 7
+*\ \<\fBr\fR\> :\fIRenice_a_Task\fR
+You will be prompted for a PID and then the value to nice it to.
+Entering a positive value will cause a process to lose priority.
+Conversely, a negative value will cause a process to be viewed more
+favorably by the kernel.
+
+.TP 7
+\ \ \<\fBW\fR\> :\fIWrite_the_Configuration_File\fR
+This will save all of your options and toggles plus the current
+display mode and delay time.
+By issuing this command just before quitting \*(Me, you will be able restart
+later in exactly that same state.
+
+.TP 7
+\ \ \<\fBZ\fR\> :\fIChange_Color_Mapping
+This key will take you to a separate screen where you can change the
+colors for the \*(CW, or for all windows.
+For details regarding this \*(CI \*(Xt 3d. COLOR Mapping.
+
+.IP "*" 3
+The commands shown with an \*(AS are not available in 'Secure mode',
+nor will they be shown on the level-1 help screen.
+
+.\" ......................................................................
+.SS 3b. SUMMARY Area Commands
+The \*(SA \*(CIs are\fB always available\fR in both \*(FM and \*(AM.
+They affect the beginning lines of your display and will determine the position
+of messages and prompts.
+
+These commands always impact just the \*(CW/field group.
+\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight
+into \*(CWs and field groups.
+
+.TP 7
+\ \ \<\fBl\fR\> :\fIToggle_Load_Average/Uptime\fR \*(EM On/Off
+This is also the line containing the program name (possibly an alias) when
+operating in \*(FM or the \*(CW name when operating in \*(AM.
+
+.TP 7
+\ \ \<\fBm\fR\> :\fIToggle_Memory/Swap_Usage\fR \*(EM On/Off
+This command affects two \*(SA lines.
+
+.TP 7
+\ \ \<\fBt\fR\> :\fIToggle_Task/Cpu_States\fR \*(EM On/Off
+This command affects from 2 to many \*(SA lines, depending on the state
+of the '1' toggle and whether or not \*(Me is running under true SMP.
+
+.TP 7
+\ \ \<\fB1\fR\> :\fIToggle_Single/Separate_Cpu_States\fR \*(EM On/Off
+This command affects how the 't' command's Cpu States portion is shown.
+Although this toggle exists primarily to serve massively-parallel SMP machines,
+it is not restricted to solely SMP environments.
+
+When you see 'Cpu(s):' in the \*(SA, the '1' toggle is \*O and all
+\*(Pu information is gathered in a single line.
+Otherwise, each \*(Pu is displayed separately as: 'Cpu0, Cpu1, ...'
+
+.PP
+\*(NT If the entire \*(SA has been toggled \*F for any window, you would be left
+with just the\fB message line\fR.
+In that way, you will have maximized available task rows but (temporarily)
+sacrificed the program name in \*(FM or the \*(CW name when in \*(AM.
+
+.\" ......................................................................
+.SS 3c. TASK Area Commands
+The \*(TA \*(CIs are\fB always\fR available in \*(FM.
+
+The \*(TA \*(CIs are\fB never available\fR in \*(AM\fI if\fR the \*(CW's
+\*(TD has been toggled \*F (\*(Xt 4. ALTERNATE\-DISPLAY Mode).
+
+.PP
+.\" .........................
+.B APPEARANCE\fR of \*(TW
+.br
+.in +2
+The following commands will also be influenced by the state of the
+global 'B' (bold disable) toggle.
+.in
+
+.TP 7
+\ \ \<\fBb\fR\> :\fIBold/Reverse_toggle\fR
+This command will impact how the 'x' and 'y' toggles are displayed.
+Further, it will only be available when at least one of those toggles is \*O.
+
+.TP 7
+\ \ \<\fBx\fR\> :\fIColumn_Highlight_toggle\fR
+Changes highlighting for the current sort field.
+You probably don't need a constant visual reminder of the sort field and
+\*(Me hopes that you always run with 'column highlight' \*F, due to the cost
+in path-length.
+
+If you forget which field is being sorted this command can serve as a quick
+visual reminder.
+
+.TP 7
+\ \ \<\fBy\fR\> :\fIRow_Highlight_toggle\fR
+Changes highlighting for "running" tasks.
+For additional insight into this task state, \*(Xt 2a. DESCRIPTIONS of Fields,
+Process Status.
+
+Use of this provision provides important insight into your system's health.
+The only costs will be a few additional tty escape sequences.
+
+.TP 7
+\ \ \<\fBz\fR\> :\fIColor/Monochrome_toggle\fR
+Switches the \*(CW between your last used color scheme and the older form
+of black-on-white or white-on-black.
+This command will alter\fB both\fR the \*(SA and \*(TA but does not affect the
+state of the 'x', 'y' or 'b' toggles.
+
+.PP
+.\" .........................
+.B CONTENT\fR of \*(TW
+.PD 0
+.TP 7
+\ \ \<\fBc\fR\> :\fICommand_Line/Program_Name_toggle\fR
+This command will be honored whether or not the 'Command' column
+is currently visible.
+Later, should that field come into view, the change you applied will be seen.
+
+.TP 7
+\ \ \<\fBf\fR\> and \<\fBo\fR\> :\fIFields_select\fR or \fIOrder_fields\fR
+These keys display separate screens where you can change which
+fields are displayed and their order.
+For additional information on these \*(CIs
+\*(Xt 2b. SELECTING and ORDERING Columns.
+
+.TP 7
+\ \ \<\fBS\fR\> :\fICumulative_Time_Mode_toggle\fR
+When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process.
+
+.TP 7
+\ \ \'\fBS\fR\' :\fICumulative_Time_Mode_toggle\fR
+When 'Cumulative mode' is \*O, each process is listed with the \*(Pu
+time that it and its dead children have used.
+
+When \*F, programs that fork into many separate tasks will appear
+less demanding.
+For programs like 'init' or a shell this is appropriate but for others,
+like compilers, perhaps not.
+Experiment with two \*(TWs sharing the same sort field but with different 'S'
+states and see which representation you prefer.
+
+After issuing this command, you'll be informed of the new state of this toggle.
+If you wish to know in advance whether or not 'Cumulative mode' is in
+effect, simply ask for help and view the window summary on the second line.
+
+.TP 7
+\ \ \<\fBu\fR\> :\fIShow_Specific_User_Only\fR
+You will be prompted to enter the name of the user to display.
+Thereafter, in that \*(TW only matching User ID's will be shown, or possibly
+no tasks will be shown.
+
+Later, if you wish to monitor all tasks again, re-issue this command but
+just press <Enter> at the prompt, without providing a name.
+
+.PP
+.\" .........................
+.B SIZE\fR of \*(TW
+.PD 0
+.TP 7
+\ \ \<\fBi\fR\> :\fIIdle_Processes_toggle\fR
+Displays all tasks or just active tasks.
+When this toggle is \*F, idled or zombied processes will not be displayed.
+
+If this command is applied to the last \*(TD when in \*(AM, then it will not
+affect the window's size, as all prior \*(TDs will have already been painted.
+
+.TP 7
+\ \ \<\fBn\fR\> or \<\fB#\fR\> :\fISet_Maximum_Tasks\fR
+You will be prompted to enter the number of tasks to display.
+The lessor of your number and available screen rows will be used.
+
+When used in \*(AM, this is the command that gives you precise control over
+the size of each currently visible \*(TD, except for the very last.
+It will not affect the last window's size, as all prior \*(TDs will have
+already been painted.
+
+\*(NT If you wish to increase the size of the last visible \*(TD when in \*(AM,
+simply decrease the size of the \*(TD(s) above it.
+
+.PP
+.\" .........................
+.B SORTING\fR of \*(TW
+.br
+.in +2
+For compatibility, this \*(Me supports most of the former \*(Me sort keys.
+Since this is primarily a service to former \*(Me users, these commands do
+not appear on any help screen.
+ command sorted field supported
+ A start time (non-display) No
+ M %MEM Yes
+ N PID Yes
+ P %CPU Yes
+ T TIME+ Yes
+
+Before using any of the following sort provisions, \*(Me suggests that you
+temporarily turn on column highlighting using the 'x' \*(CI.
+That will help ensure that the actual sort environment matches your intent.
+
+The following \*(CIs will\fB only\fR be honored when the
+current sort field is\fB visible\fR.
+The sort field might\fI not\fR be visible because:
+ 1) there is insufficient\fI Screen Width\fR
+ 2) the 'f' \*(CI turned it \*F
+.in
+
+.TP 7
+\ \ \<\fB<\fR\> :\fIMove_Sort_Field_Left\fR
+Moves the sort column to the left unless the current sort field is
+the first field being displayed.
+
+.TP 7
+\ \ \<\fB>\fR\> :\fIMove_Sort_Field_Right\fR
+Moves the sort column to the right unless the current sort field is
+the last field being displayed.
+
+.PP
+.in +2
+The following \*(CIs will\fB always\fR be honored whether or not
+the current sort field is visible.
+.in
+
+.TP 7
+\ \ \<\fBF\fR\> or \<\fBO\fR\> :\fISelect_Sort_Field\fR
+These keys display a separate screen where you can change which field
+is used as the sort column.
+
+If a field is selected which was not previously being displayed, it will
+be forced \*O when you return to the \*(Me display.
+However, depending upon your screen width and the order of your fields,
+this sort field may not be displayable.
+
+This \*(CI can be a convenient way to simply verify the current sort field,
+when running \*(Me with column highlighting turned \*F.
+
+.TP 7
+\ \ \<\fBR\fR\> :\fIReverse/Normal_Sort_Field_toggle\fR
+Using this \*(CI you can alternate between high-to-low and low-to-high sorts.
+
+.PP
+.in +2
+\*(NT Field sorting uses internal values, not those in column display.
+Thus, the TTY and WCHAN fields will violate strict ASCII collating sequence.
+.in
+
+.\" ......................................................................
+.SS 3d. COLOR Mapping
+When you issue the 'Z' \*(CI, you will be presented with a separate screen.
+That screen can be used to change the colors in just the \*(CW or
+in all four windows before returning to the \*(Me display.
+
+.P
+.B Available \*(CIs
+ \fB4\fR upper case letters to select a\fB target\fR
+ \fB8\fR numbers to select a\fB color\fR
+ normal toggles available\fR
+ 'B' :bold disable/enable
+ 'b' :running tasks "bold"/reverse
+ 'z' :color/mono
+ other commands available\fR
+ 'a'/'w' :apply, then go to next/prior
+ <Enter> :apply and exit
+ 'q' :abandon current changes and exit
+
+If your use 'a' or 'w' to cycle the targeted window, you will
+have applied the color scheme that was displayed when you left that window.
+You can, of course, easily return to any window and reapply different
+colors or turn colors \*F completely with the 'z' toggle.
+
+The Color Mapping screen can also be used to change the \*(CW/field group
+in either \*(FM or \*(AM.
+Whatever was targeted when 'q' or <Enter> was pressed will be made current
+as you return to the \*(Me display.
+
+
+.\" ----------------------------------------------------------------------
+.SH 4. ALTERNATE\-DISPLAY Mode
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS 4a. WINDOWS Overview
+.TP
+.B Field Groups/Windows\fR:
+.br
+In \*(FM there is a single window represented by the entire screen.
+That single window can still be changed to display 1 of 4 different\fB field
+groups\fR (\*(Xc 'G' \*(CI, repeated below).
+Each of the 4 field groups has a unique separately configurable\fB \*(SA\fR
+and its own configurable\fB \*(TA\fR.
+
+In \*(AM, those 4 underlying field groups can now be made visible
+simultaneously, or can be turned \*F individually at your command.
+
+The \*(SA will always exist, even if it's only the message line.
+At any given time only\fI one\fR \*(SA can be displayed.
+However, depending on your commands, there could be from\fI zero\fR
+to\fI four\fR separate \*(TDs currently showing on the screen.
+
+.TP
+.B Current Window\fR:
+.br
+The \*(CW is the window associated with the \*(SA and the window to which
+task related commands are always directed.
+Since in \*(AM you can toggle the \*(TD \*F, some commands might be
+restricted for the \*(CW.
+
+A further complication arises when you have toggled the first \*(SA
+line \*F.
+With the loss of the window name (the 'l' toggled line), you'll not easily
+know what window is the \*(CW.
+
+.\" ......................................................................
+.SS 4b. COMMANDS for Windows
+.TP 7
+\ \ \<\fB-\fR\> and \<\fB_\fR\> :\fIShow/Hide_Window(s)_toggles\fR
+The '-' key turns the \*(CW's \*(TD \*O and \*F.
+When \*O, that \*(TA will show a minimum of the columns header you've
+established with the 'f' and 'o' commands.
+It will also reflect any other \*(TA options/toggles you've applied yielding
+zero or more tasks.
+
+The '_' key does the same for all \*(TDs.
+In other words, it switches between the currently visible \*(TD(s) and any
+\*(TD(s) you had toggled \*F.
+If all 4 \*(TDs are currently visible, this \*(CI will leave the \*(SA
+as the only display element.
+
+.TP 7
+*\ \<\fB=\fR\> and \<\fB+\fR\> :\fIEqualize_(re-balance)_Window(s)\fR
+The '=' key forces the \*(CW's \*(TD to be visible.
+It also reverses any 'i' (idle tasks) and 'n' (max tasks) commands that might
+be active.
+
+The '+' key does the same for all windows.
+The four \*(TDs will reappear, evenly balanced.
+They will also have retained any customizations you had previously applied,
+except for the 'i' (idle tasks) and 'n' (max tasks) commands.
+
+.TP 7
+*\ \<\fBA\fR\> :\fIAlternate_Display_Mode_toggle\fR
+This command will switch between \*(FM and \*(AM.
+
+The first time you issue this command, all four \*(TDs will be shown.
+Thereafter when you switch modes, you will see only the \*(TD(s) you've
+chosen to make visible.
+
+.TP 7
+*\ \<\fBa\fR\> and \<\fBw\fR\> :\fINext_Window_Forward/Backward\fR
+This will change the \*(CW, which in turn changes the window to which
+commands are directed.
+These keys act in a circular fashion so you can reach any desired \*(CW
+using either key.
+
+Assuming the window name is visible (you have not toggled 'l' \*F),
+whenever the \*(CW name loses its emphasis/color, that's a reminder
+the \*(TD is \*F and many commands will be restricted.
+
+.TP 7
+*\ \<\fBG\fR\> :\fIChoose_Another_Window/Field_Group\fR
+You will be prompted to enter a number between 1 and 4 designating the
+window/field group which should be made the \*(CW.
+
+In \*(FM, this command is necessary to alter the \*(CW.
+In \*(AM, it is simply a less convenient alternative to the 'a' and 'w'
+commands.
+
+.TP 7
+\ \ \<\fBg\fR\> :\fIChange_Window/Field_Group_Name\fR
+You will be prompted for a new name to be applied to the \*(CW.
+It does not require that the window name be visible
+(the 'l' toggle to be \*O).
+
+.IP "*" 3
+The \*(CIs shown with an \*(AS have use beyond \*(AM.
+ '=', 'A', 'G' are always available
+ 'a', 'w' act the same when color mapping
+
+
+.\" ----------------------------------------------------------------------
+.SH 5. FILES
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS 5a. SYSTEM Configuration File
+The presence of this file will influence which version of the 'help' screen
+is shown to an ordinary user.
+More importantly, it will limit what ordinary users are allowed
+to do when \*(Me is running.
+They will not be able to issue the following commands.
+ k Kill a task
+ r Renice a task
+ d or s Change delay/sleep interval
+
+The system \*(CF is\fB not\fR created by \*(Me.
+Rather, you create this file manually and place it in the \fI/etc\fR
+directory.
+Its name must be 'toprc' and must have no leading '.' (period).
+It must have only two lines.
+
+Here is an example of the contents of\fI /etc/toprc\fR:
+ s # line 1: 'secure' mode switch
+ 5.0 # line 2: 'delay'\ \ interval in seconds
+
+.\" ......................................................................
+.SS 5b. PERSONAL Configuration File
+This file is written as '$HOME/.your-name-4-top' + 'rc'.
+Use the 'W' \*(CI to create it or update it.
+
+Here is the general layout:
+ global # line 1: the program name/alias notation
+ " # line 2: id,altscr,irixps,delay,curwin
+ per ea # line a: winname,fieldscur
+ window # line b: winflags,sortindx,maxtasks
+ " # line c: summclr,msgsclr,headclr,taskclr
+
+If the $HOME variable is not present, \*(Me will try to write the
+personal \*(CF to the current directory, subject to permissions.
+
+
+.\" ----------------------------------------------------------------------
+.SH 6. STUPID TRICKS Sampler
+.\" ----------------------------------------------------------------------
+Many of these 'tricks' work best when you give \*(Me a scheduling boost.
+So plan on starting him with a nice value of -10, assuming you've got
+the authority.
+
+.\" ......................................................................
+.SS 6a. Kernel Magic
+.\" sorry, just can't help it -- don't ya love the sound of this?
+For these stupid tricks, \*(Me needs \*(FM.
+.\" ( apparently AM static was a potential concern )
+
+.New
+The user interface, through prompts and help, intentionally implies
+that the delay interval is limited to tenths of a second.
+However, you're free to set any desired delay.
+If you want to see Linux at his scheduling best, try a delay of .09
+seconds or less.
+
+For this experiment, under x-windows open an xterm and maximize it.
+Then do the following:
+ . provide a scheduling boost and tiny delay via:
+ nice -n -10 top -d.09
+ . keep sorted column highlighting \*F to minimize
+ path length
+ . turn \*O reverse row highlighting for emphasis
+ . try various sort columns (TIME/MEM work well),
+ and normal or reverse sorts to bring the most
+ active processes into view
+
+What you'll see is a very busy Linux doing what he's always done for you,
+but there was no program available to illustrate this.
+
+.New
+Under an xterm using 'white-on-black' colors, try setting \*(Me's task color
+to black and be sure that task highlighting is set to bold, not reverse.
+Then set the delay interval to around .3 seconds.
+
+After bringing the most active processes into view, what you'll see are
+the ghostly images of just the currently running tasks.
+
+.New
+Delete the existing rcfile, or create a new symlink.
+Start this new version then type 'T' (a secret key, \*(Xt 3c. TASK Area
+Commands, Sorting) followed by 'W' and 'q'.
+Finally, restart the program with -d0 (zero delay).
+
+Your display will be refreshed at three times the rate of the former \*(Me,
+a 300% speed advantage.
+As \*(Me climbs the TIME ladder, be as patient as you can while speculating
+on whether or not \*(Me will ever reach the \*(Me.
+
+.\" ......................................................................
+.SS 6b. Bouncing Windows
+For these stupid tricks, \*(Me needs \*(AM.
+
+.New
+With 3 or 4 \*(TDs visible, pick any window other than the last
+and turn idle processes \*F.
+Depending on where you applied 'i', sometimes several \*(TDs are bouncing and
+sometimes it's like an accordion, as \*(Me tries his best to allocate space.
+
+.New
+Set each window's summary lines differently: one with no memory; another with
+no states; maybe one with nothing at all, just the message line.
+Then hold down 'a' or 'w' and watch a variation on bouncing windows \*(EM
+hopping windows.
+
+.New
+Display all 4 windows and for each, in turn, set idle processes to \*F.
+You've just entered the "extreme bounce" zone.
+
+.\" ......................................................................
+.SS 6c. The Big Bird Window
+This stupid trick also requires \*(AM.
+
+.New
+Display all 4 windows and make sure that 1:Def is the \*(CW.
+Then, keep increasing window size until the all the other \*(TDs
+are "pushed out of the nest".
+
+When they've all been displaced, toggle between all visible/invisible windows.
+Then ponder this:
+.br
+ is \*(Me fibbing or telling honestly your imposed truth?
+
+
+.\" ----------------------------------------------------------------------
+.SH 7. BUGS
+.\" ----------------------------------------------------------------------
+Send bug reports to:
+ Albert D\. Cahalan, <albert@users.sf.net>
+
+
+.\" ----------------------------------------------------------------------
+.SH 8. HISTORY Former top
+.\" ----------------------------------------------------------------------
+The original top was written by Roger Binns,
+based on Branko Lankester's <lankeste@fwi.uva.nl> ps program.
+
+Robert Nation <nation@rocket.sanders.lockheed.com>
+adapted it for the proc file system.
+
+Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+added support for configurable fields.
+
+Plus many other individuals contributed over the years.
+
+
+.\" ----------------------------------------------------------------------
+.SH 9. AUTHOR
+.\" ----------------------------------------------------------------------
+This entirely new and enhanced replacement was written by:
+ Jim / James C. Warner, <warnerjc@worldnet.att.net>
+.ig
+ ( as a means to learn Linux, can you believe it? )
+ ( & he accidentally learned a little groff, too! )
+..
+
+With invaluable help from:
+ Albert D\. Cahalan, <albert@users.sf.net>
+ Craig Small, <csmall@small.dropbear.id.au>
+
+.ig
+.rj 2
+.B -*-\fR few though they are, some yet believe\fB -*-\fR
+.B -*-\~\~\~\~\~\~\~\fRin-the-\fBart\fR-of-programming\~\~\~\~\~\~\~\fB-*-\fR
+..
+
+.\" ----------------------------------------------------------------------
+.SH 10. SEE ALSO
+.\" ----------------------------------------------------------------------
+.BR free (1),
+.BR ps (1),
+.BR uptime (1),
+.BR atop (1),
+.BR slabtop (1),
+.BR vmstat (8),
+.BR w (1).
+
+
+.\" ----------------------------------------------------------------------
+.ig
+.rj 1
+\-*-
+.PD
+.in -3
+Copyright (c) 2002 \*(EM JC Warner & Associates, Ltd.
+
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.1 or
+any later version published by the Free Software Foundation;
+with no Front-Cover Texts, no Back-Cover Texts, and with the following
+Invariant Sections and any sub-sections therein:
+.na
+.hy 0
+.in +3
+STUPID\ TRICKS\ Sampler;
+.br
+AUTHOR
+.in
+A copy of the license is included in the section entitled
+\(dqGNU Free Documentation License\(dq.
+..
+.
+.\" end: active doc ||||||||||||||||||||||||||||||||||||||||||||||||||
+.\" ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+.ig
+.\" ----------------------------------------------------------------------
+.SH GNU Free Documentation License
+Version 1.1, March 2000
+
+Copyright (C) 2000 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+.SS 0. PREAMBLE
+The purpose of this License is to make a manual, textbook, or other
+written document "free" in the sense of freedom: to assure everyone
+the effective freedom to copy and redistribute it, with or without
+modifying it, either commercially or noncommercially. Secondarily,
+this License preserves for the author and publisher a way to get
+credit for their work, while not being considered responsible for
+modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+.SS 1. APPLICABILITY AND DEFINITIONS
+This License applies to any manual or other work that contains a
+notice placed by the copyright holder saying it can be distributed
+under the terms of this License. The "Document", below, refers to any
+such manual or work. Any member of the public is a licensee, and is
+addressed as "you".
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (For example, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, whose contents can be viewed and edited directly and
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup has been designed to thwart or discourage
+subsequent modification by readers is not Transparent. A copy that is
+not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML designed for human modification. Opaque formats include
+PostScript, PDF, proprietary formats that can be read and edited only
+by proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML produced by some word processors for output
+purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+.SS 2. VERBATIM COPYING
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+.SS 3. COPYING IN QUANTITY
+If you publish printed copies of the Document numbering more than 100,
+and the Document's license notice requires Cover Texts, you must enclose
+the copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a publicly-accessible computer-network location containing a complete
+Transparent copy of the Document, free of added material, which the
+general network-using public has access to download anonymously at no
+charge using public-standard network protocols. If you use the latter
+option, you must take reasonably prudent steps, when you begin
+distribution of Opaque copies in quantity, to ensure that this
+Transparent copy will remain thus accessible at the stated location
+until at least one year after the last time you distribute an Opaque
+copy (directly or through your agents or retailers) of that edition to
+the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+.SS 4. MODIFICATIONS
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+.HP 3
+.B A\fR.\ Use in the Title Page (and on the covers, if any) a title distinct
+from that of the Document, and from those of previous versions (which should,
+if there were any, be listed in the History section of the Document).
+You may use the same title as a previous version if the original publisher of
+that version gives permission.
+.HP 3
+.B B\fR.\ List on the Title Page, as authors, one or more persons or entities
+responsible for authorship of the modifications in the Modified Version,
+together with at least five of the principal authors of the Document
+(all of its principal authors, if it has less than five).
+.HP 3
+.B C\fR.\ State on the Title page the name of the publisher of the Modified
+Version, as the publisher.
+.HP 3
+.B D\fR.\ Preserve all the copyright notices of the Document.
+.HP 3
+.B E\fR.\ Add an appropriate copyright notice for your modifications adjacent
+to the other copyright notices.
+.HP 3
+.B F\fR.\ Include, immediately after the copyright notices, a license notice
+giving the public permission to use the Modified Version under the terms of
+this License, in the form shown in the Addendum below.
+.HP 3
+.B G\fR.\ Preserve in that license notice the full lists of Invariant Sections
+and required Cover Texts given in the Document's license notice.
+.HP 3
+.B H\fR.\ Include an unaltered copy of this License.
+.HP 3
+.B I\fR.\ Preserve the section entitled "History", and its title, and add to it
+an item stating at least the title, year, new authors, and publisher of the
+Modified Version as given on the Title Page.
+If there is no section entitled "History" in the Document, create one stating
+the title, year, authors, and publisher of the Document as given on its Title
+Page, then add an item describing the Modified Version as stated in the
+previous sentence.
+.HP 3
+.B J\fR.\ Preserve the network location, if any, given in the Document for
+public access to a Transparent copy of the Document, and likewise the network
+locations given in the Document for previous versions it was based on.
+These may be placed in the "History" section.
+You may omit a network location for a work that was published at least four
+years before the Document itself, or if the original publisher of the version
+it refers to gives permission.
+.HP 3
+.B K\fR.\ In any section entitled "Acknowledgements" or "Dedications", preserve
+the section's title, and preserve in the section all the substance and tone of
+each of the contributor acknowledgements and/or dedications given therein.
+.HP 3
+.B L\fR.\ Preserve all the Invariant Sections of the Document, unaltered in their
+text and in their titles.
+Section numbers or the equivalent are not considered part of the section titles.
+.HP 3
+.B M\fR.\ Delete any section entitled "Endorsements".
+Such a section may not be included in the Modified Version.
+.HP 3
+.B N\fR.\ Do not retitle any existing section as "Endorsements" or to conflict
+in title with any Invariant Section.
+
+.PP
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+.SS 5. COMBINING DOCUMENTS
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections entitled "History"
+in the various original documents, forming one section entitled
+"History"; likewise combine any sections entitled "Acknowledgements",
+and any sections entitled "Dedications". You must delete all sections
+entitled "Endorsements."
+
+.SS 6. COLLECTIONS OF DOCUMENTS
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+.SS 7. AGGREGATION WITH INDEPENDENT WORKS
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, does not as a whole count as a Modified Version
+of the Document, provided no compilation copyright is claimed for the
+compilation. Such a compilation is called an "aggregate", and this
+License does not apply to the other self-contained works thus compiled
+with the Document, on account of their being thus compiled, if they
+are not themselves derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one quarter
+of the entire aggregate, the Document's Cover Texts may be placed on
+covers that surround only the Document within the aggregate.
+Otherwise they must appear on covers around the whole aggregate.
+
+.SS 8. TRANSLATION
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License provided that you also include the
+original English version of this License. In case of a disagreement
+between the translation and the original English version of this
+License, the original English version will prevail.
+
+.SS 9. TERMINATION
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+.SS 10. FUTURE REVISIONS OF THIS LICENSE
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+.SS ADDENDUM: How to use this License for your documents
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+.IP "" 3
+Copyright (c) YEAR YOUR NAME.
+
+Permission is granted to copy, distribute and/or modify this document under the
+terms of the GNU Free Documentation License, Version 1.1 or any later version
+published by the Free Software Foundation;\ \ with the Invariant Sections being
+LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the
+Back-Cover Texts being LIST.
+A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+
+If you have no Invariant Sections, write "with no Invariant Sections"
+instead of saying which ones are invariant. If you have no
+Front-Cover Texts, write "no Front-Cover Texts" instead of
+"Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
+.\" ----------------------------------------------------------------------
+.SH \fRend of\fB GNU Free Documentation License
+.IP ""
+.PP
+..
+.\" end: gfdl license ||||||||||||||||||||||||||||||||||||||||||||||||
+.\" ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
diff --git a/smartt-top/top.c b/smartt-top/top.c
new file mode 100644
index 0000000..0fa4048
--- /dev/null
+++ b/smartt-top/top.c
@@ -0,0 +1,3509 @@
+/* top.c - Source file: show Linux processes */
+/*
+ * Copyright (c) 2002, by: James C. Warner
+ * All rights reserved. 8921 Hilloway Road
+ * Eden Prairie, Minnesota 55347 USA
+ * <warnerjc@worldnet.att.net>
+ *
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * For their contributions to this program, the author wishes to thank:
+ * Albert D. Cahalan, <albert@users.sf.net>
+ * Craig Small, <csmall@small.dropbear.id.au>
+ *
+ * Changes by Albert Cahalan, 2002-2004.
+ */
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+// Foul POS defines all sorts of stuff...
+#include <term.h>
+#undef tab
+
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <values.h>
+
+#include "proc/devname.h"
+#include "proc/wchan.h"
+#include "proc/procps.h"
+#include "proc/readproc.h"
+#include "proc/escape.h"
+#include "proc/sig.h"
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+#include "proc/whattime.h"
+
+#include "top.h"
+
+/*###### Miscellaneous global stuff ####################################*/
+
+ /* The original and new terminal attributes */
+static struct termios Savedtty,
+ Rawtty;
+static int Ttychanged = 0;
+
+ /* Program name used in error messages and local 'rc' file name */
+static char *Myname;
+
+ /* Name of user config file (dynamically constructed) and our
+ 'Current' rcfile contents, initialized with defaults but may be
+ overridden with the local rcfile (old or new-style) values */
+static char Rc_name [OURPATHSZ];
+static RCF_t Rc = DEF_RCFILE;
+
+ /* The run-time acquired page size */
+static unsigned Page_size;
+static unsigned page_to_kb_shift;
+
+ /* SMP Irix/Solaris mode */
+static int Cpu_tot;
+static double pcpu_max_value; // usually 99.9, for %CPU display
+ /* assume no IO-wait stats, overridden if linux 2.5.41 */
+static const char *States_fmts = STATES_line2x4;
+
+ /* Specific process id monitoring support */
+static pid_t Monpids [MONPIDMAX] = { 0 };
+static int Monpidsidx = 0;
+
+ /* A postponed error message */
+static char Msg_delayed [SMLBUFSIZ];
+static int Msg_awaiting = 0;
+
+// This is the select() timeout. Clear it in sig handlers to avoid a race.
+// (signal happens just as we are about to select() and thus does not
+// break us out of the select(), causing us to delay until timeout)
+static volatile struct timeval tv;
+static int sock_desc;
+static struct sockaddr_in smartt_server;
+static struct s_mem_info mem_info;
+#define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0);
+
+ /* Configurable Display support ##################################*/
+
+ /* Current screen dimensions.
+ note: the number of processes displayed is tracked on a per window
+ basis (see the WIN_t). Max_lines is the total number of
+ screen rows after deducting summary information overhead. */
+ /* Current terminal screen size. */
+static int Screen_cols, Screen_rows, Max_lines;
+
+// set to 1 if writing to the last column would be troublesome
+// (we don't distinguish the lowermost row from the other rows)
+static int avoid_last_column;
+
+ /* This is really the number of lines needed to display the summary
+ information (0 - nn), but is used as the relative row where we
+ stick the cursor between frames. */
+static int Msg_row;
+
+ /* Global/Non-windows mode stuff that is NOT persistent */
+static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise
+ PSDBopen = 0, // set to '1' if psdb opened (now postponed)
+ Batch = 0, // batch mode, collect no input, dumb output
+ Loops = -1, // number of iterations, -1 loops forever
+ Secure_mode = 0; // set if some functionality restricted
+
+ /* Some cap's stuff to reduce runtime calls --
+ to accomodate 'Batch' mode, they begin life as empty strings */
+static char Cap_clr_eol [CAPBUFSIZ],
+ Cap_clr_eos [CAPBUFSIZ],
+ Cap_clr_scr [CAPBUFSIZ],
+ Cap_rmam [CAPBUFSIZ],
+ Cap_smam [CAPBUFSIZ],
+ Cap_curs_norm [CAPBUFSIZ],
+ Cap_curs_huge [CAPBUFSIZ],
+ Cap_home [CAPBUFSIZ],
+ Cap_norm [CAPBUFSIZ],
+ Cap_reverse [CAPBUFSIZ],
+ Caps_off [CAPBUFSIZ];
+static int Cap_can_goto = 0;
+
+ /* Some optimization stuff, to reduce output demands...
+ The Pseudo_ guys are managed by wins_resize and frame_make. They
+ are exploited in a macro and represent 90% of our optimization.
+ The Stdout_buf is transparent to our code and regardless of whose
+ buffer is used, stdout is flushed at frame end or if interactive. */
+static PSEUDO_SCREEN_t Pseudo_scrn;
+static int Pseudo_row, Pseudo_cols, Pseudo_size;
+#ifndef STDOUT_IOLBF
+ // less than stdout's normal buffer but with luck mostly '\n' anyway
+static char Stdout_buf[2048];
+#endif
+
+
+ /* ////////////////////////////////////////////////////////////// */
+ /* Special Section: multiple windows/field groups ---------------*/
+
+ /* The pointers to our four WIN_t's, and which of those is considered
+ the 'current' window (ie. which window is associated with any summ
+ info displayed and to which window commands are directed) */
+static WIN_t Winstk [GROUPSMAX],
+ *Curwin;
+
+ /* Frame oriented stuff that can't remain local to any 1 function
+ and/or that would be too cumbersome managed as parms,
+ and/or that are simply more efficiently handled as globals
+ (first 2 persist beyond a single frame, changed infrequently) */
+static int Frames_libflags; // PROC_FILLxxx flags (0 = need new)
+//atic int Frames_maxcmdln; // the largest from the 4 windows
+static unsigned Frame_maxtask; // last known number of active tasks
+ // ie. current 'size' of proc table
+static unsigned Frame_running, // state categories for this frame
+ Frame_sleepin,
+ Frame_stopped,
+ Frame_zombied;
+static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu'
+static int Frame_srtflg, // the subject window's sort direction
+ Frame_ctimes, // the subject window's ctimes flag
+ Frame_cmdlin; // the subject window's cmdlin flag
+ /* ////////////////////////////////////////////////////////////// */
+
+
+/*###### Sort callbacks ################################################*/
+
+ /*
+ * These happen to be coded in the same order as the enum 'pflag'
+ * values. Note that 2 of these routines serve double duty --
+ * 2 columns each.
+ */
+
+SCB_NUMx(P_PID, XXXID)
+SCB_NUMx(P_PPD, ppid)
+SCB_STRx(P_URR, ruser)
+SCB_NUMx(P_UID, euid)
+SCB_STRx(P_URE, euser)
+SCB_STRx(P_GRP, egroup)
+SCB_NUMx(P_TTY, tty)
+SCB_NUMx(P_PRI, priority)
+SCB_NUMx(P_NCE, nice)
+SCB_NUMx(P_CPN, processor)
+SCB_NUM1(P_CPU, pcpu)
+ // also serves P_TM2 !
+static int sort_P_TME (const proc_t **P, const proc_t **Q)
+{
+ if (Frame_ctimes) {
+ if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
+ < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+ return SORT_lt;
+ if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
+ > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+ return SORT_gt;
+ } else {
+ if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
+ return SORT_lt;
+ if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
+ return SORT_gt;
+ }
+ return SORT_eq;
+}
+
+SCB_NUM1(P_VRT, size)
+SCB_NUM2(P_SWP, size, resident)
+SCB_NUM1(P_RES, resident) // also serves P_MEM !
+SCB_NUM1(P_COD, trs)
+SCB_NUM1(P_DAT, drs)
+SCB_NUM1(P_SHR, share)
+SCB_NUM1(P_FLT, maj_flt)
+SCB_NUM1(P_DRT, dt)
+SCB_NUMx(P_STA, state)
+
+static int sort_P_CMD (const proc_t **P, const proc_t **Q)
+{
+ /* if a process doesn't have a cmdline, we'll consider it a kernel thread
+ -- since displayed tasks are given special treatment, we must too */
+ if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) {
+ if (!(*Q)->cmdline) return Frame_srtflg * -1;
+ if (!(*P)->cmdline) return Frame_srtflg;
+ return Frame_srtflg *
+ strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln);
+ }
+ // this part also handles the compare if both are kernel threads
+ return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd);
+}
+
+SCB_NUM1(P_WCH, wchan)
+SCB_NUM1(P_FLG, flags)
+
+ /* ///////////////////////////////// special sort for prochlp() ! */
+static int sort_HST_t (const HST_t *P, const HST_t *Q)
+{
+ return P->pid - Q->pid;
+}
+
+
+/*###### Tiny useful routine(s) ########################################*/
+
+ /*
+ * This routine isolates ALL user INPUT and ensures that we
+ * wont be mixing I/O from stdio and low-level read() requests */
+static int chin (int ech, char *buf, unsigned cnt)
+{
+ int rc;
+
+ fflush(stdout);
+ if (!ech)
+ rc = read(STDIN_FILENO, buf, cnt);
+ else {
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ rc = read(STDIN_FILENO, buf, cnt);
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
+ }
+ // may be the beginning of a lengthy escape sequence
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ return rc; // note: we do NOT produce a vaid 'string'
+}
+
+
+// This routine simply formats whatever the caller wants and
+// returns a pointer to the resulting 'const char' string...
+static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2)));
+static const char *fmtmk (const char *fmts, ...)
+{
+ static char buf[BIGBUFSIZ]; // with help stuff, our buffer
+ va_list va; // requirements exceed 1k
+
+ va_start(va, fmts);
+ vsnprintf(buf, sizeof(buf), fmts, va);
+ va_end(va);
+ return (const char *)buf;
+}
+
+
+// This guy is just our way of avoiding the overhead of the standard
+// strcat function (should the caller choose to participate)
+static inline char *scat (char *restrict dst, const char *restrict src)
+{
+ while (*dst) dst++;
+ while ((*(dst++) = *(src++)));
+ return --dst;
+}
+
+
+// Trim the rc file lines and any 'open_psdb_message' result which arrives
+// with an inappropriate newline (thanks to 'sysmap_mmap')
+static char *strim_0 (char *str)
+{
+ static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape
+ char *p;
+
+ if ((p = strpbrk(str, ws))) *p = 0;
+ return str;
+}
+
+
+// This guy just facilitates Batch and protects against dumb ttys
+// -- we'd 'inline' him but he's only called twice per frame,
+// yet used in many other locations.
+static const char *tg2 (int x, int y)
+{
+ return Cap_can_goto ? tgoto(cursor_address, x, y) : "";
+}
+
+
+/*###### Exit/Interrput routines #######################################*/
+
+// The usual program end -- called only by functions in this section.
+static void bye_bye (FILE *fp, int eno, const char *str) NORETURN;
+static void bye_bye (FILE *fp, int eno, const char *str)
+{
+ if (!Batch)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ putp(tg2(0, Screen_rows));
+ putp(Cap_curs_norm);
+ putp(Cap_smam);
+ putp("\n");
+ fflush(stdout);
+
+//#define ATEOJ_REPORT
+#ifdef ATEOJ_REPORT
+
+ fprintf(fp,
+ "\n\tTerminal: %s"
+ "\n\t device = %s, ncurses = v%s"
+ "\n\t max_colors = %d, max_pairs = %d"
+ "\n\t Cap_can_goto = %s"
+ "\n\t Screen_cols = %d, Screen_rows = %d"
+ "\n\t Max_lines = %d, most recent Pseudo_size = %d"
+ "\n"
+#ifdef PRETENDNOCAP
+ , "dumb"
+#else
+ , termname()
+#endif
+ , ttyname(STDOUT_FILENO), NCURSES_VERSION
+ , max_colors, max_pairs
+ , Cap_can_goto ? "yes" : "No!"
+ , Screen_cols, Screen_rows
+ , Max_lines, Pseudo_size
+ );
+
+ fprintf(fp,
+#ifndef STDOUT_IOLBF
+ "\n\t Stdout_buf = %d, BUFSIZ = %u"
+#endif
+ "\n\tWindows and Curwin->"
+ "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d"
+ "\n\t rc.winname = %s, grpname = %s"
+ "\n\t rc.winflags = %08x, maxpflgs = %d"
+ "\n\t rc.fieldscur = %s"
+ "\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d"
+ "\n\t rc.sortindx = %d"
+ "\n"
+#ifndef STDOUT_IOLBF
+ , sizeof(Stdout_buf), (unsigned)BUFSIZ
+#endif
+ , sizeof(WIN_t), GROUPSMAX
+ , Curwin->rc.winname, Curwin->grpname
+ , Curwin->rc.winflags, Curwin->maxpflgs
+ , Curwin->rc.fieldscur
+ , Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln
+ , Curwin->rc.sortindx
+ );
+
+ fprintf(fp,
+ "\n\tProgram"
+ "\n\t Linux version = %u.%u.%u, %s"
+ "\n\t Hertz = %u (%u bytes, %u-bit time)"
+ "\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u"
+ "\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)"
+ "\n"
+ , LINUX_VERSION_MAJOR(linux_version_code)
+ , LINUX_VERSION_MINOR(linux_version_code)
+ , LINUX_VERSION_PATCH(linux_version_code)
+ , procps_version
+ , (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8
+ , Page_size, Cpu_tot, sizeof(proc_t)
+ , sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t)
+ );
+
+
+#endif
+
+ if (str) fputs(str, fp);
+ exit(eno);
+}
+
+
+ /*
+ * Normal end of execution.
+ * catches:
+ * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
+// FIXME: can't do this shit in a signal handler
+static void end_pgm (int sig) NORETURN;
+static void end_pgm (int sig)
+{
+ if(sig)
+ sig |= 0x80; // for a proper process exit code
+ bye_bye(stdout, sig, NULL);
+}
+
+
+ /*
+ * Standard error handler to normalize the look of all err o/p */
+static void std_err (const char *str) NORETURN;
+static void std_err (const char *str)
+{
+ static char buf[SMLBUFSIZ];
+
+ fflush(stdout);
+ /* we'll use our own buffer so callers can still use fmtmk() and, yes the
+ leading tab is not the standard convention, but the standard is wrong
+ -- OUR msg won't get lost in screen clutter, like so many others! */
+ snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
+ if (!Ttychanged) {
+ fprintf(stderr, "%s\n", buf);
+ exit(1);
+ }
+ /* not to worry, he'll change our exit code to 1 due to 'buf' */
+ bye_bye(stderr, 1, buf);
+}
+
+
+ /*
+ * Standard out handler */
+static void std_out (const char *str) NORETURN;
+static void std_out (const char *str)
+{
+ static char buf[SMLBUFSIZ];
+
+ fflush(stdout);
+ /* we'll use our own buffer so callers can still use fmtmk() and, yes the
+ leading tab is not the standard convention, but the standard is wrong
+ -- OUR msg won't get lost in screen clutter, like so many others! */
+ snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
+ if (!Ttychanged) {
+ fprintf(stdout, "%s\n", buf);
+ exit(0);
+ }
+ bye_bye(stdout, 0, buf);
+}
+
+
+ /*
+ * Suspend ourself.
+ * catches:
+ * SIGTSTP, SIGTTIN and SIGTTOU */
+// FIXME: can't do this shit in a signal handler!
+static void suspend (int dont_care_sig)
+{
+ (void)dont_care_sig;
+ /* reset terminal */
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ putp(tg2(0, Screen_rows));
+ putp(Cap_curs_norm);
+ putp(Cap_smam);
+ putp("\n");
+ fflush(stdout);
+ raise(SIGSTOP);
+ /* later, after SIGCONT... */
+ ZAP_TIMEOUT
+ if (!Batch)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
+ putp(Cap_clr_scr);
+ putp(Cap_rmam);
+}
+
+
+/*###### Misc Color/Display support ####################################*/
+
+ /* macro to test if a basic (non-color) capability is valid
+ thanks: Floyd Davidson <floyd@ptialaska.net> */
+#define tIF(s) s ? s : ""
+#define CAPCOPY(dst,src) src && strcpy(dst,src)
+
+ /*
+ * Make the appropriate caps/color strings and set some
+ * lengths which are used to distinguish twix the displayed
+ * columns and an actual printed row!
+ * note: we avoid the use of background color so as to maximize
+ * compatibility with the user's xterm settings */
+static void capsmk (WIN_t *q)
+{
+ static int capsdone = 0;
+
+ // we must NOT disturb our 'empty' terminfo strings!
+ if (Batch) return;
+
+ // these are the unchangeable puppies, so we only do 'em once
+ if (!capsdone) {
+ CAPCOPY(Cap_clr_eol, clr_eol);
+ CAPCOPY(Cap_clr_eos, clr_eos);
+ CAPCOPY(Cap_clr_scr, clear_screen);
+
+ if (!eat_newline_glitch) { // we like the eat_newline_glitch
+ CAPCOPY(Cap_rmam, exit_am_mode);
+ CAPCOPY(Cap_smam, enter_am_mode);
+ if (!*Cap_rmam || !*Cap_smam) { // need both
+ *Cap_rmam = '\0';
+ *Cap_smam = '\0';
+ if (auto_right_margin) {
+ avoid_last_column = 1;
+ }
+ }
+ }
+
+ CAPCOPY(Cap_curs_huge, cursor_visible);
+ CAPCOPY(Cap_curs_norm, cursor_normal);
+ CAPCOPY(Cap_home, cursor_home);
+ CAPCOPY(Cap_norm, exit_attribute_mode);
+ CAPCOPY(Cap_reverse, enter_reverse_mode);
+
+ snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair));
+ if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1;
+ capsdone = 1;
+ }
+ /* the key to NO run-time costs for configurable colors -- we spend a
+ little time with the user now setting up our terminfo strings, and
+ the job's done until he/she/it has a change-of-heart */
+ strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode));
+ if (CHKw(q, Show_COLORS) && max_colors > 0) {
+ strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr));
+ snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s"
+ , tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse);
+ snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s"
+ , tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold);
+ snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s"
+ , tparm(set_a_foreground, q->rc.headclr), Cap_reverse);
+ snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s"
+ , Caps_off, tparm(set_a_foreground, q->rc.taskclr));
+ } else {
+ q->capclr_sum[0] = '\0';
+ strcpy(q->capclr_msg, Cap_reverse);
+ strcpy(q->capclr_pmt, q->cap_bold);
+ strcpy(q->capclr_hdr, Cap_reverse);
+ strcpy(q->capclr_rownorm, Cap_norm);
+ }
+ // composite(s), so we do 'em outside and after the if
+ snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s"
+ , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse);
+ q->len_rownorm = strlen(q->capclr_rownorm);
+ q->len_rowhigh = strlen(q->capclr_rowhigh);
+
+#undef tIF
+}
+
+
+// Show an error, but not right now.
+// Due to the postponed opening of ksym, using open_psdb_message,
+// if P_WCH had been selected and the program is restarted, the
+// message would otherwise be displayed prematurely.
+static void msg_save (const char *fmts, ...) __attribute__((format(printf,1,2)));
+static void msg_save (const char *fmts, ...)
+{
+ char tmp[SMLBUFSIZ];
+ va_list va;
+
+ va_start(va, fmts);
+ vsnprintf(tmp, sizeof(tmp), fmts, va);
+ va_end(va);
+ /* we'll add some extra attention grabbers to whatever this is */
+ snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim_0(tmp));
+ Msg_awaiting = 1;
+}
+
+
+ /*
+ * Show an error message (caller may include a '\a' for sound) */
+static void show_msg (const char *str)
+{
+ PUTT("%s%s %s %s%s",
+ tg2(0, Msg_row),
+ Curwin->capclr_msg,
+ str,
+ Caps_off,
+ Cap_clr_eol
+ );
+ fflush(stdout);
+ sleep(MSG_SLEEP);
+ Msg_awaiting = 0;
+}
+
+
+ /*
+ * Show an input prompt + larger cursor */
+static void show_pmt (const char *str)
+{
+ PUTT("%s%s%s: %s%s",
+ tg2(0, Msg_row),
+ Curwin->capclr_pmt,
+ str,
+ Cap_curs_huge,
+ Caps_off
+ );
+ fflush(stdout);
+}
+
+
+ /*
+ * Show lines with specially formatted elements, but only output
+ * what will fit within the current screen width.
+ * Our special formatting consists of:
+ * "some text <_delimiter_> some more text <_delimiter_>...\n"
+ * Where <_delimiter_> is a single byte in the range of:
+ * \01 through \10 (in decimalizee, 1 - 8)
+ * and is used to select an 'attribute' from a capabilities table
+ * which is then applied to the *preceding* substring.
+ * Once recognized, the delimiter is replaced with a null character
+ * and viola, we've got a substring ready to output! Strings or
+ * substrings without delimiters will receive the Cap_norm attribute.
+ *
+ * Caution:
+ * This routine treats all non-delimiter bytes as displayable
+ * data subject to our screen width marching orders. If callers
+ * embed non-display data like tabs or terminfo strings in our
+ * glob, a line will truncate incorrectly at best. Worse case
+ * would be truncation of an embedded tty escape sequence.
+ *
+ * Tabs must always be avoided or our efforts are wasted and
+ * lines will wrap. To lessen but not eliminate the risk of
+ * terminfo string truncation, such non-display stuff should
+ * be placed at the beginning of a "short" line.
+ * (and as for tabs, gimme 1 more color then no worries, mate) */
+static void show_special (int interact, const char *glob)
+{ /* note: the following is for documentation only,
+ the real captab is now found in a group's WIN_t !
+ +------------------------------------------------------+
+ | char *captab[] = { : Cap's/Delim's |
+ | Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 |
+ | Sum_color, = \03 |
+ | Msg_color, Pmt_color, = \04, \05 |
+ | Hdr_color, = \06 |
+ | Row_color_high, = \07 |
+ | Row_color_norm }; = \10 [octal!] |
+ +------------------------------------------------------+ */
+ char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ];
+ char *rp, *cap, *lin_end, *sub_beg, *sub_end;
+ int room;
+
+ /* handle multiple lines passed in a bunch */
+ while ((lin_end = strchr(glob, '\n'))) {
+
+ /* create a local copy we can extend and otherwise abuse */
+ size_t amt = lin_end - glob;
+ if(amt > sizeof lin - 1)
+ amt = sizeof lin - 1; // shit happens
+ memcpy(lin, glob, amt);
+ /* zero terminate this part and prepare to parse substrings */
+ lin[amt] = '\0';
+ room = Screen_cols;
+ sub_beg = sub_end = lin;
+ *(rp = row) = '\0';
+
+ while (*sub_beg) {
+ switch (*sub_end) {
+ case 0: /* no end delim, captab makes normal */
+ *(sub_end + 1) = '\0'; /* extend str end, then fall through */
+ case 1 ... 8:
+ cap = Curwin->captab[(int)*sub_end];
+ *sub_end = '\0';
+ snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off);
+ amt = strlen(tmp);
+ if(rp - row + amt + 1 > sizeof row)
+ goto overflow; // shit happens
+ rp = scat(rp, tmp);
+ room -= (sub_end - sub_beg);
+ sub_beg = ++sub_end;
+ break;
+ default: /* nothin' special, just text */
+ ++sub_end;
+ }
+ if (unlikely(0 >= room)) break; /* skip substrings that won't fit */
+ }
+overflow:
+ if (interact) PUTT("%s%s\n", row, Cap_clr_eol);
+ else PUFF("%s%s\n", row, Cap_clr_eol);
+ glob = ++lin_end; /* point to next line (maybe) */
+ } /* end: while 'lines' */
+
+ /* If there's anything left in the glob (by virtue of no trailing '\n'),
+ it probably means caller wants to retain cursor position on this final
+ line. That, in turn, means we're interactive and so we'll just do our
+ 'fit-to-screen' thingy... */
+ if (*glob) PUTT("%.*s", Screen_cols, glob);
+}
+
+
+/*###### Small Utility routines ########################################*/
+
+// Get a string from the user
+static char *ask4str (const char *prompt)
+{
+ static char buf[GETBUFSIZ];
+
+ show_pmt(prompt);
+ memset(buf, '\0', sizeof(buf));
+ chin(1, buf, sizeof(buf) - 1);
+ putp(Cap_curs_norm);
+ return strim_0(buf);
+}
+
+
+// Get a float from the user
+static float get_float (const char *prompt)
+{
+ char *line;
+ float f;
+
+ if (!(*(line = ask4str(prompt)))) return -1;
+ // note: we're not allowing negative floats
+ if (strcspn(line, ",.1234567890")) {
+ show_msg("\aNot valid");
+ return -1;
+ }
+ sscanf(line, "%f", &f);
+ return f;
+}
+
+
+// Get an integer from the user
+static int get_int (const char *prompt)
+{
+ char *line;
+ int n;
+
+ if (!(*(line = ask4str(prompt)))) return -1;
+ // note: we've got to allow negative ints (renice)
+ if (strcspn(line, "-1234567890")) {
+ show_msg("\aNot valid");
+ return -1;
+ }
+ sscanf(line, "%d", &n);
+ return n;
+}
+
+
+ /*
+ * Do some scaling stuff.
+ * We'll interpret 'num' as one of the following types and
+ * try to format it to fit 'width'.
+ * SK_no (0) it's a byte count
+ * SK_Kb (1) it's kilobytes
+ * SK_Mb (2) it's megabytes
+ * SK_Gb (3) it's gigabytes
+ * SK_Tb (4) it's terabytes */
+static const char *scale_num (unsigned long num, const int width, const unsigned type)
+{
+ /* kilobytes, megabytes, gigabytes, terabytes, duh! */
+ static double scale[] = { 1024.0, 1024.0*1024, 1024.0*1024*1024, 1024.0*1024*1024*1024, 0 };
+ /* kilo, mega, giga, tera, none */
+#ifdef CASEUP_SCALE
+ static char nextup[] = { 'K', 'M', 'G', 'T', 0 };
+#else
+ static char nextup[] = { 'k', 'm', 'g', 't', 0 };
+#endif
+ static char buf[TNYBUFSIZ];
+ double *dp;
+ char *up;
+
+ /* try an unscaled version first... */
+ if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf;
+
+ /* now try successively higher types until it fits */
+ for (up = nextup + type, dp = scale; *dp; ++dp, ++up) {
+ /* the most accurate version */
+ if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up))
+ return buf;
+ /* the integer version */
+ if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up))
+ return buf;
+ }
+ /* well shoot, this outta' fit... */
+ return "?";
+}
+
+
+ /*
+ * Do some scaling stuff.
+ * format 'tics' to fit 'width'. */
+static const char *scale_tics (TIC_t tics, const int width)
+{
+#ifdef CASEUP_SCALE
+#define HH "%uH"
+#define DD "%uD"
+#define WW "%uW"
+#else
+#define HH "%uh"
+#define DD "%ud"
+#define WW "%uw"
+#endif
+ static char buf[TNYBUFSIZ];
+ unsigned long nt; // narrow time, for speed on 32-bit
+ unsigned cc; // centiseconds
+ unsigned nn; // multi-purpose whatever
+
+ nt = (tics * 100ull) / Hertz;
+ cc = nt % 100; // centiseconds past second
+ nt /= 100; // total seconds
+ nn = nt % 60; // seconds past the minute
+ nt /= 60; // total minutes
+ if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc))
+ return buf;
+ if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn))
+ return buf;
+ nn = nt % 60; // minutes past the hour
+ nt /= 60; // total hours
+ if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn))
+ return buf;
+ nn = nt; // now also hours
+ if (width >= snprintf(buf, sizeof buf, HH, nn))
+ return buf;
+ nn /= 24; // now days
+ if (width >= snprintf(buf, sizeof buf, DD, nn))
+ return buf;
+ nn /= 7; // now weeks
+ if (width >= snprintf(buf, sizeof buf, WW, nn))
+ return buf;
+ // well shoot, this outta' fit...
+ return "?";
+
+#undef HH
+#undef DD
+#undef WW
+}
+
+#include <pwd.h>
+
+static int selection_type;
+static uid_t selection_uid;
+
+// FIXME: this is "temporary" code we hope
+static int good_uid(const proc_t *restrict const pp){
+ switch(selection_type){
+ case 'p':
+ return 1;
+ case 0:
+ return 1;
+ case 'U':
+ if (pp->ruid == selection_uid) return 1;
+ if (pp->suid == selection_uid) return 1;
+ if (pp->fuid == selection_uid) return 1;
+ // FALLTHROUGH
+ case 'u':
+ if (pp->euid == selection_uid) return 1;
+ // FALLTHROUGH
+ default:
+ ; // don't know what it is; find bugs fast
+ }
+ return 0;
+}
+
+// swiped from ps, and ought to be in libproc
+static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){
+ struct passwd *passwd_data;
+ char *endp;
+ unsigned long num;
+ static const char uidrange[] = "User ID out of range.";
+ static const char uidexist[] = "User name does not exist.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0'){ /* hmmm, try as login name */
+ passwd_data = getpwnam(str);
+ if(!passwd_data) return uidexist;
+ num = passwd_data->pw_uid;
+ }
+ if(num > 0xfffffffeUL) return uidrange;
+ *ret = num;
+ return 0;
+}
+
+
+/*###### Library Alternatives ##########################################*/
+
+ /*
+ * Handle our own memory stuff without the risk of leaving the
+ * user's terminal in an ugly state should things go sour. */
+
+static void *alloc_c (unsigned numb) MALLOC;
+static void *alloc_c (unsigned numb)
+{
+ void * p;
+
+ if (!numb) ++numb;
+ if (!(p = calloc(1, numb)))
+ std_err("failed memory allocate");
+ return p;
+}
+
+static void *alloc_r (void *q, unsigned numb) MALLOC;
+static void *alloc_r (void *q, unsigned numb)
+{
+ void *p;
+
+ if (!numb) ++numb;
+ if (!(p = realloc(q, numb)))
+ std_err("failed memory allocate");
+ return p;
+}
+
+
+ /*
+ * This guy's modeled on libproc's 'five_cpu_numbers' function except
+ * we preserve all cpu data in our CPU_t array which is organized
+ * as follows:
+ * cpus[0] thru cpus[n] == tics for each separate cpu
+ * cpus[Cpu_tot] == tics from the 1st /proc/stat line */
+static CPU_t *cpus_refresh (CPU_t *cpus)
+{
+ static FILE *fp = NULL;
+ int i;
+ int num;
+ // enough for a /proc/stat CPU line (not the intr line)
+ char buf[SMLBUFSIZ];
+
+ /* by opening this file once, we'll avoid the hit on minor page faults
+ (sorry Linux, but you'll have to close it for us) */
+ if (!fp) {
+ if (!(fp = fopen("/proc/stat", "r")))
+ std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno)));
+ /* note: we allocate one more CPU_t than Cpu_tot so that the last slot
+ can hold tics representing the /proc/stat cpu summary (the first
+ line read) -- that slot supports our View_CPUSUM toggle */
+ cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t));
+ }
+ rewind(fp);
+ fflush(fp);
+
+ // first value the last slot with the cpu summary line
+ if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
+ cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number
+ cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number
+ cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number
+ num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
+ &cpus[Cpu_tot].u,
+ &cpus[Cpu_tot].n,
+ &cpus[Cpu_tot].s,
+ &cpus[Cpu_tot].i,
+ &cpus[Cpu_tot].w,
+ &cpus[Cpu_tot].x,
+ &cpus[Cpu_tot].y,
+ &cpus[Cpu_tot].z
+ );
+ if (num < 4)
+ std_err("failed /proc/stat read");
+
+ // and just in case we're 2.2.xx compiled without SMP support...
+ if (Cpu_tot == 1) {
+ cpus[1].id = 0;
+ memcpy(cpus, &cpus[1], sizeof(CPU_t));
+ }
+
+ // now value each separate cpu's tics
+ for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) {
+ if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
+ cpus[i].x = 0; // FIXME: can't tell by kernel version number
+ cpus[i].y = 0; // FIXME: can't tell by kernel version number
+ cpus[i].z = 0; // FIXME: can't tell by kernel version number
+ num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
+ &cpus[i].id,
+ &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z
+ );
+ if (num < 4)
+ std_err("failed /proc/stat read");
+ }
+ return cpus;
+}
+
+
+ /*
+ * Refresh procs *Helper* function to eliminate yet one more need
+ * to loop through our darn proc_t table. He's responsible for:
+ * 1) calculating the elapsed time since the previous frame
+ * 2) counting the number of tasks in each state (run, sleep, etc)
+ * 3) maintaining the HST_t's and priming the proc_t pcpu field
+ * 4) establishing the total number tasks for this frame */
+static void prochlp (proc_t *this)
+{
+ static HST_t *hist_sav = NULL;
+ static HST_t *hist_new = NULL;
+ static unsigned hist_siz = 0; // number of structs
+ static unsigned maxt_sav; // prior frame's max tasks
+ TIC_t tics;
+
+ if (unlikely(!this)) {
+ static struct timeval oldtimev;
+ struct timeval timev;
+ struct timezone timez;
+ HST_t *hist_tmp;
+ float et;
+
+ gettimeofday(&timev, &timez);
+ et = (timev.tv_sec - oldtimev.tv_sec)
+ + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
+ oldtimev.tv_sec = timev.tv_sec;
+ oldtimev.tv_usec = timev.tv_usec;
+
+ // if in Solaris mode, adjust our scaling for all cpus
+ Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot));
+ maxt_sav = Frame_maxtask;
+ Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;
+
+ // reuse memory each time around
+ hist_tmp = hist_sav;
+ hist_sav = hist_new;
+ hist_new = hist_tmp;
+ // prep for our binary search by sorting the last frame's HST_t's
+ qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
+ return;
+ }
+
+ switch (this->state) {
+ case 'R':
+ Frame_running++;
+ break;
+ case 'S':
+ case 'D':
+ Frame_sleepin++;
+ break;
+ case 'T':
+ Frame_stopped++;
+ break;
+ case 'Z':
+ Frame_zombied++;
+ break;
+ }
+
+ if (unlikely(Frame_maxtask+1 >= hist_siz)) {
+ hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25%
+ hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz);
+ hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz);
+ }
+ /* calculate time in this process; the sum of user time (utime) and
+ system time (stime) -- but PLEASE dont waste time and effort on
+ calcs and saves that go unused, like the old top! */
+ hist_new[Frame_maxtask].pid = this->tid;
+ hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
+
+#if 0
+{ int i;
+ int lo = 0;
+ int hi = maxt_sav - 1;
+
+ // find matching entry from previous frame and make ticks elapsed
+ while (lo <= hi) {
+ i = (lo + hi) / 2;
+ if (this->tid < hist_sav[i].pid)
+ hi = i - 1;
+ else if (likely(this->tid > hist_sav[i].pid))
+ lo = i + 1;
+ else {
+ tics -= hist_sav[i].tics;
+ break;
+ }
+ }
+}
+#else
+{
+ HST_t tmp;
+ const HST_t *ptr;
+ tmp.pid = this->tid;
+ ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t);
+ if(ptr) tics -= ptr->tics;
+}
+#endif
+
+ // we're just saving elapsed tics, to be converted into %cpu if
+ // this task wins it's displayable screen row lottery... */
+ this->pcpu = tics;
+// if (Frames_maxcmdln) { }
+ // shout this to the world with the final call (or us the next time in)
+ Frame_maxtask++;
+}
+
+
+ /*
+ * This guy's modeled on libproc's 'readproctab' function except
+ * we reuse and extend any prior proc_t's. He's been customized
+ * for our specific needs and to avoid the use of <stdarg.h> */
+static proc_t **procs_refresh (proc_t **table, int flags)
+{
+#define PTRsz sizeof(proc_t *)
+#define ENTsz sizeof(proc_t)
+ static unsigned savmax = 0; // first time, Bypass: (i)
+ proc_t *ptsk = (proc_t *)-1; // first time, Force: (ii)
+ unsigned curmax = 0; // every time (jeeze)
+ PROCTAB* PT;
+ static int show_threads_was_enabled = 0; // optimization
+
+ prochlp(NULL); // prep for a new frame
+ if (Monpidsidx)
+ PT = openproc(flags, Monpids);
+ else
+ PT = openproc(flags);
+
+ if (PT==NULL) {
+ std_err(strerror(errno));
+ exit(1);
+ }
+
+
+ // i) Allocated Chunks: *Existing* table; refresh + reuse
+ if (!(CHKw(Curwin, Show_THREADS))) {
+ while (curmax < savmax) {
+ if (table[curmax]->cmdline) {
+ unsigned idx;
+ // Skip if Show_THREADS was never enabled
+ if (show_threads_was_enabled) {
+ for (idx = curmax + 1; idx < savmax; idx++) {
+ if (table[idx]->cmdline == table[curmax]->cmdline)
+ table[idx]->cmdline = NULL;
+ }
+ }
+ free(*table[curmax]->cmdline);
+ table[curmax]->cmdline = NULL;
+ }
+ if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break;
+ prochlp(ptsk); // tally & complete this proc_t
+ ++curmax;
+ }
+ }
+ else { // show each thread in a process separately
+ while (curmax < savmax) {
+ proc_t *ttsk;
+ if (unlikely(!(ptsk = readproc(PT, NULL)))) break;
+ show_threads_was_enabled = 1;
+ while (curmax < savmax) {
+ unsigned idx;
+ if (table[curmax]->cmdline) {
+ // threads share the same cmdline storage. 'table' is
+ // qsort()ed, so must look through the rest of the table.
+ for (idx = curmax + 1; idx < savmax; idx++) {
+ if (table[idx]->cmdline == table[curmax]->cmdline)
+ table[idx]->cmdline = NULL;
+ }
+ free(*table[curmax]->cmdline); // only free once
+ table[curmax]->cmdline = NULL;
+ }
+ if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break;
+ prochlp(ttsk);
+ ++curmax;
+ }
+ free(ptsk); // readproc() proc_t not used
+ }
+ }
+
+ // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill
+ if (!(CHKw(Curwin, Show_THREADS))) {
+ while (ptsk) {
+ // realloc as we go, keeping 'table' ahead of 'currmax++'
+ table = alloc_r(table, (curmax + 1) * PTRsz);
+ // here, readproc will allocate the underlying proc_t stg
+ if (likely(ptsk = readproc(PT, NULL))) {
+ prochlp(ptsk); // tally & complete this proc_t
+ table[curmax++] = ptsk;
+ }
+ }
+ }
+ else { // show each thread in a process separately
+ while (ptsk) {
+ proc_t *ttsk;
+ if (likely(ptsk = readproc(PT, NULL))) {
+ show_threads_was_enabled = 1;
+ while (1) {
+ table = alloc_r(table, (curmax + 1) * PTRsz);
+ if (!(ttsk = readtask(PT, ptsk, NULL))) break;
+ prochlp(ttsk);
+ table[curmax++] = ttsk;
+ }
+ free(ptsk); // readproc() proc_t not used
+ }
+ }
+ }
+ closeproc(PT);
+
+ // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists
+ if (curmax >= savmax) {
+ table = alloc_r(table, (curmax + 1) * PTRsz);
+ // here, we must allocate the underlying proc_t stg ourselves
+ table[curmax] = alloc_c(ENTsz);
+ savmax = curmax + 1;
+ }
+ // this frame's end, but not necessarily end of allocated space
+ table[curmax]->tid = -1;
+ return table;
+
+#undef PTRsz
+#undef ENTsz
+}
+
+/*###### Field Table/RCfile compatability support ######################*/
+
+// from either 'stat' or 'status' (preferred), via bits not otherwise used
+#define L_EITHER PROC_SPARE_1
+// These are the Fieldstab.lflg values used here and in reframewins.
+// (own identifiers as documentation and protection against changes)
+#define L_stat PROC_FILLSTAT
+#define L_statm PROC_FILLMEM
+#define L_status PROC_FILLSTATUS
+#define L_CMDLINE L_EITHER | PROC_FILLARG
+#define L_EUSER PROC_FILLUSR
+#define L_RUSER L_status | PROC_FILLUSR
+#define L_GROUP L_status | PROC_FILLGRP
+#define L_NONE 0
+// for reframewins and summary_show 1st pass
+#define L_DEFAULT PROC_FILLSTAT
+
+// a temporary macro, soon to be undef'd...
+#define SF(f) (QFP_t)sort_P_ ## f
+
+ /* These are our gosh darn 'Fields' !
+ They MUST be kept in sync with pflags !!
+ note: for integer data, the length modifiers found in .fmts may
+ NOT reflect the true field type found in proc_t -- this plus
+ a cast when/if displayed provides minimal width protection. */
+static FLD_t Fieldstab[] = {
+/* .lflg anomolies:
+ P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid)
+ P_CPU, L_stat - never filled by libproc, but requires times (pcpu)
+ P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline)
+ L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit !
+ keys head fmts width scale sort desc lflg
+ ------ ----------- ------- ------ ----- ----- ---------------------- -------- */
+ { "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE },
+ { "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER },
+ { "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER },
+ { "DdCc", " UID", " %5u", -1, -1, SF(UID), "User Id", L_NONE },
+ { "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER },
+ { "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP },
+ { "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat },
+ { "HhHh", " PR", " %3d", -1, -1, SF(PRI), "Priority", L_stat },
+ { "IiIi", " NI", " %3d", -1, -1, SF(NCE), "Nice value", L_stat },
+ { "JjYy", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
+ { "KkEe", " %CPU", " %#4.1f", -1, -1, SF(CPU), "CPU usage", L_stat },
+ { "LlWw", " TIME", " %6.6s", 6, -1, SF(TME), "CPU Time", L_stat },
+ { "MmRr", " TIME+ ", " %9.9s", 9, -1, SF(TME), "CPU Time, hundredths", L_stat },
+ { "NnFf", " %MEM", " %#4.1f", -1, -1, SF(RES), "Memory usage (RES)", L_statm },
+ { "OoMm", " VIRT", " %5.5s", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", L_statm },
+ { "PpOo", " SWAP", " %4.4s", 4, SK_Kb, SF(SWP), "Swapped size (kb)", L_statm },
+ { "QqTt", " RES", " %4.4s", 4, SK_Kb, SF(RES), "Resident size (kb)", L_statm },
+ { "RrKk", " CODE", " %4.4s", 4, SK_Kb, SF(COD), "Code size (kb)", L_statm },
+ { "SsLl", " DATA", " %4.4s", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm },
+ { "TtPp", " SHR", " %4.4s", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm },
+ { "UuJj", " nFLT", " %4.4s", 4, SK_no, SF(FLT), "Page Fault count", L_stat },
+ { "VvSs", " nDRT", " %4.4s", 4, SK_no, SF(DRT), "Dirty Pages count", L_statm },
+ { "WwVv", " S", " %c", -1, -1, SF(STA), "Process Status", L_EITHER },
+ // next entry's special: '.head' will be formatted using table entry's own
+ // '.fmts' plus runtime supplied conversion args!
+ { "XxXx", " COMMAND", " %-*.*s", -1, -1, SF(CMD), "Command name/line", L_EITHER },
+ { "YyUu", " WCHAN ", " %-9.9s", -1, -1, SF(WCH), "Sleeping in Function", L_stat },
+ // next entry's special: the 0's will be replaced with '.'!
+ { "ZzZz", " Flags ", " %08lx", -1, -1, SF(FLG), "Task Flags <sched.h>", L_stat },
+#if 0
+ { "..Qq", " A", " %4.4s", 4, SK_no, SF(PID), "Accessed Page count", L_stat },
+ { "..Nn", " TRS", " %4.4s", 4, SK_Kb, SF(PID), "Code in memory (kb)", L_stat },
+ { "..Rr", " WP", " %4.4s", 4, SK_no, SF(PID), "Unwritable Pages", L_stat },
+ { "Jj[{", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
+ { "..\\|"," Bad", " %2u", -1, -1, SF(CPN), "-- must ignore | --", 0 },
+ { "..]}", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
+ { "..^~", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
+#endif
+};
+#undef SF
+
+
+ /* All right, those-that-follow -- Listen Up!
+ * For the above table keys and the following present/future rc file
+ * compatibility support, you have Mr. Albert D. Cahalan to thank.
+ * He must have been in a 'Christmas spirit'. Were it left to me,
+ * this top would never have gotten that close to the former top's
+ * crufty rcfile. Not only is it illogical, it's odoriferous !
+ */
+
+ // used as 'to' and/or 'from' args in the ft_xxx utilities...
+#define FT_NEW_fmt 0
+#define FT_OLD_fmt 2
+
+
+#if 0
+ // convert, or 0 for failure
+static int ft_cvt_char (const int fr, const int to, int c) {
+ int j = -1;
+
+ while (++j < MAXTBL(Fieldstab)) {
+ if (c == Fieldstab[j].keys[fr]) return Fieldstab[j].keys[to];
+ if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1];
+ }
+ return 0;
+}
+#endif
+
+
+ // convert
+static inline int ft_get_char (const int fr, int i) {
+ int c;
+ if (i < 0) return 0;
+ if (i >= MAXTBL(Fieldstab)) return 0;
+ c = Fieldstab[i].keys[fr];
+ if (c == '.') c = 0; // '.' marks a bad entry
+ return c;
+}
+
+
+#if 0
+ // convert, or -1 for failure
+static int ft_get_idx (const int fr, int c) {
+ int j = -1;
+
+ while (++j < MAXTBL(Fieldstab)) {
+ if (c == Fieldstab[j].keys[fr]) return j;
+ if (c == Fieldstab[j].keys[fr+1]) return j;
+ }
+ return -1;
+}
+#endif
+
+
+ // convert, or NULL for failure
+static const FLD_t *ft_get_ptr (const int fr, int c) {
+ int j = -1;
+
+ while (++j < MAXTBL(Fieldstab)) {
+ if (c == Fieldstab[j].keys[fr]) return Fieldstab+j;
+ if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j;
+ }
+ return NULL;
+}
+
+
+#if 0
+ // convert, or NULL for failure
+static const FLD_t *ft_idx_to_ptr (const int i) {
+ if (i < 0) return NULL;
+ if (i >= MAXTBL(Fieldstab)) return NULL;
+ return Fieldstab + i;
+}
+
+
+ // convert, or -1 for failure
+static int ft_ptr_to_idx (const FLD_t *p) {
+ int i;
+ if (p < Fieldstab) return -1;
+ i = p - Fieldstab;
+ if (i >= MAXTBL(Fieldstab)) return -1;
+ return i;
+}
+#endif
+
+
+#if 0
+static void rc_bugless (const RCF_t *const rc) {
+ const RCW_t *w;
+ int i = 0;
+
+ fprintf(stderr,"\n%d %d %f %d\n",
+ rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index
+ );
+ while(i < 4) {
+ w = &rc->win[i++];
+ fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n",
+ w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks,
+ w->summclr, w->msgsclr, w->headclr, w->taskclr
+ );
+ }
+}
+#endif
+
+
+// '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window.
+// line 1: an eyecatcher, with a shameless advertisement
+// line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin.
+// For each of the 4 windows:
+// line a: contains winname, fieldscur
+// line b: contains winflags, sortindx, maxtasks
+// line c: contains summclr, msgsclr, headclr, taskclr
+// line d: if present, would crash procps-3.1.1
+static int rc_read_new (const char *const buf, RCF_t *rc) {
+ int i;
+ int cnt;
+ const char *cp;
+
+ cp = strstr(buf, "\n\n" RCF_EYECATCHER);
+ if (!cp) return -1;
+ cp = strchr(cp + 2, '\n');
+ if (!cp++) return -2;
+
+ cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n",
+ &rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index
+ );
+ if (cnt != 4) return -3;
+ cp = strchr(cp, '\n');
+ if (!cp++) return -4;
+
+ for (i = 0; i < GROUPSMAX; i++) {
+ RCW_t *ptr = &rc->win[i];
+ cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur);
+ if (cnt != 2) return 5+100*i; // OK to have less than 4 windows
+ if (WINNAMSIZ <= strlen(ptr->winname)) return -6;
+ if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7;
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(8+100*i);
+
+ cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n",
+ &ptr->winflags, &ptr->sortindx, &ptr->maxtasks
+ );
+ if (cnt != 3) return -(9+100*i);
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(10+100*i);
+
+ cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n",
+ &ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr
+ );
+ if (cnt != 4) return -(11+100*i);
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(12+100*i);
+ while (*cp == '\t') { // skip unknown per-window settings
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(13+100*i);
+ }
+ }
+ return 13;
+}
+
+
+
+static int rc_read_old (const char *const buf, RCF_t *rc) {
+ const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......";
+ const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr";
+ unsigned u;
+ const char *cp;
+ unsigned c_show = 0;
+ int badchar = 0; // allow a limited number of duplicates and junk
+
+ char scoreboard[256];
+ memset(scoreboard, '\0', sizeof scoreboard);
+
+ cp = buf+2; // skip the "\n\n" we stuck at the beginning
+ u = 0;
+ for (;;) {
+ const char *tmp;
+ int c = *cp++;
+ if (u+1 >= sizeof rc->win[0].fieldscur) return -1;
+ if (c == '\0') return -2;
+ if (c == '\n') break;
+ if (c & ~0x7f) return -3;
+ if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden
+ if (scoreboard[c|0xe0u]) badchar++; // duplicates not allowed
+ scoreboard[c|0xe0u]++;
+ tmp = strchr(old,c);
+ if (!tmp) continue;
+ c = *((tmp-old)+std);
+ if (c == '.') continue;
+ if (scoreboard[c&0x1fu]) badchar++; // duplicates not allowed
+ scoreboard[c&0x1fu]++;
+ rc->win[0].fieldscur[u++] = c;
+ }
+ rc->win[0].fieldscur[u++] = '\0';
+ if (u < 21) return -6; // catch junk, not good files (had 23 chars in one)
+ if (u > 33) return -7; // catch junk, not good files (had 29 chars in one)
+// fprintf(stderr, "badchar: %d\n", badchar); sleep(2);
+ if (badchar > 8) return -8; // too much junk
+ if (!c_show) return -9; // nothing was shown
+
+ // rest of file is optional, but better look right if it exists
+ if (!*cp) return 12;
+ if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid?
+ rc->delay_time = *cp - '0';
+
+ memset(scoreboard, '\0', sizeof(scoreboard));
+ for (;;) {
+ int c = *++cp & 0xffu; // protect scoreboard[] from negative char
+ if (!c) return -14; // not OK to hit EOL w/o '\n'
+ if (c == '\n') break;
+ switch (c) {
+ case ' ':
+ case '.':
+ case '0' ... '9':
+ return -15; // not supposed to have digits here
+
+// case 's': // mostly for global rcfile
+// rc->mode_secure = 1;
+// break;
+ case 'S':
+ rc->win[0].winflags |= Show_CTIMES;
+ break;
+ case 'c':
+ rc->win[0].winflags |= Show_CMDLIN;
+ break;
+ case 'i':
+ rc->win[0].winflags &= ~Show_IDLEPS;
+ break;
+ case 'H':
+ rc->win[0].winflags |= Show_THREADS;
+ break;
+ case 'm':
+ rc->win[0].winflags &= ~View_MEMORY;
+ break;
+ case 'l':
+ rc->win[0].winflags &= ~View_LOADAV;
+ break;
+ case 't':
+ rc->win[0].winflags &= ~View_STATES;
+ break;
+ case 'I':
+ rc->mode_irixps = 0;
+ break;
+
+ case 'M':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_MEM;
+ break;
+ case 'P':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_CPU;
+ break;
+ case 'A': // supposed to be start_time
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_PID;
+ break;
+ case 'T':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_TM2;
+ break;
+ case 'N':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_PID;
+ break;
+
+ default:
+ // just ignore it, except for the scoreboard of course
+ break;
+ }
+ if (scoreboard[c]) return -16; // duplicates not allowed
+ scoreboard[c] = 1;
+ }
+ return 17;
+}
+
+
+static void rc_write_new (FILE *fp) {
+ int i;
+
+ fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n",
+ Myname
+ );
+ fprintf(fp, RCF_DEPRECATED
+ "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n",
+ Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk)
+ );
+ for (i = 0; i < GROUPSMAX; i++) {
+ char buf[40];
+ char *cp = Winstk[i].rc.fieldscur;
+ int j = 0;
+
+ while (j < 36) {
+ int c = *cp++ & 0xff;
+ switch (c) {
+ case '.':
+ case 1 ... ' ':
+ case 0x7f ... 0xff:
+ continue; // throw away junk (some of it)
+ default:
+ buf[j++] = c; // gets the '\0' too
+ }
+ if (!c) break;
+ }
+ fprintf(fp, "%s\tfieldscur=%s\n",
+ Winstk[i].rc.winname, buf
+ );
+ fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n",
+ Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks
+ );
+ fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n",
+ Winstk[i].rc.summclr, Winstk[i].rc.msgsclr,
+ Winstk[i].rc.headclr, Winstk[i].rc.taskclr
+ );
+ }
+}
+
+
+static const char *rc_write_whatever (void) {
+ FILE *fp = fopen(Rc_name, "w");
+
+ if (!fp) return strerror(errno);
+ rc_write_new(fp);
+ fclose(fp);
+ return NULL;
+}
+
+
+/*###### Startup routines ##############################################*/
+
+// No mater what *they* say, we handle the really really BIG and
+// IMPORTANT stuff upon which all those lessor functions depend!
+static void before (char *me)
+{
+ int i;
+
+ /* setup our program name -- big! */
+ Myname = strrchr(me, '/');
+ if (Myname) ++Myname; else Myname = me;
+
+ /* establish cpu particulars -- even bigger! */
+ Cpu_tot = smp_num_cpus;
+ if (linux_version_code > LINUX_VERSION(2, 5, 41))
+ States_fmts = STATES_line2x5;
+ if (linux_version_code >= LINUX_VERSION(2, 6, 0)) // grrr... only some 2.6.0-testX :-(
+ States_fmts = STATES_line2x6;
+ if (linux_version_code >= LINUX_VERSION(2, 6, 11))
+ States_fmts = STATES_line2x7;
+
+ /* get virtual page size -- nearing huge! */
+ Page_size = getpagesize();
+ i = Page_size;
+ while(i>1024){
+ i >>= 1;
+ page_to_kb_shift++;
+ }
+
+ pcpu_max_value = 99.9;
+
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %1u";
+ if(smp_num_cpus>9){
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %2u";
+ }
+ if(smp_num_cpus>99){
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %3u";
+ }
+ if(smp_num_cpus>999){
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %4u";
+ }
+
+ {
+ static char pid_fmt[6];
+ unsigned pid_digits = get_pid_digits();
+ if(pid_digits<4) pid_digits=4;
+ snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits);
+ Fieldstab[P_PID].fmts = pid_fmt;
+ Fieldstab[P_PID].head = " PID" + 10 - pid_digits;
+ Fieldstab[P_PPD].fmts = pid_fmt;
+ Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits;
+ }
+}
+
+
+// Config file read *helper* function.
+// Anything missing won't show as a choice in the field editor,
+// so make sure there is exactly one of each letter.
+//
+// Due to Rik blindly accepting damem's broken patches, procps-2.0.1x
+// has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too.
+static void confighlp (char *fields) {
+ unsigned upper[PFLAGSSIZ];
+ unsigned lower[PFLAGSSIZ];
+ char c;
+ char *cp;
+
+ memset(upper, '\0', sizeof upper);
+ memset(lower, '\0', sizeof lower);
+
+ cp = fields;
+ for (;;) {
+ c = *cp++;
+ if (!c) break;
+ if(isupper(c)) upper[c&0x1f]++;
+ else lower[c&0x1f]++;
+ }
+
+ c = 'a';
+ while (c <= 'z') {
+ if (upper[c&0x1f] && lower[c&0x1f]) {
+ lower[c&0x1f] = 0; // got both, so wipe out unseen column
+ for (;;) {
+ cp = strchr(fields, c);
+ if (cp) memmove(cp, cp+1, strlen(cp));
+ else break;
+ }
+ }
+ while (lower[c&0x1f] > 1) { // got too many a..z
+ lower[c&0x1f]--;
+ cp = strchr(fields, c);
+ memmove(cp, cp+1, strlen(cp));
+ }
+ while (upper[c&0x1f] > 1) { // got too many A..Z
+ upper[c&0x1f]--;
+ cp = strchr(fields, toupper(c));
+ memmove(cp, cp+1, strlen(cp));
+ }
+ if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing
+ lower[c&0x1f]++;
+ memmove(fields+1, fields, strlen(fields)+1);
+ fields[0] = c;
+ }
+ c++;
+ }
+}
+
+
+// First attempt to read the /etc/rcfile which contains two lines
+// consisting of the secure mode switch and an update interval.
+// It's presence limits what ordinary users are allowed to do.
+// (it's actually an old-style config file)
+//
+// Then build the local rcfile name and try to read a crufty old-top
+// rcfile (whew, odoriferous), which may contain an embedded new-style
+// rcfile. Whether embedded or standalone, new-style rcfile values
+// will always override that crufty stuff!
+// note: If running in secure mode via the /etc/rcfile,
+// Delay_time will be ignored except for root.
+static void configs_read (void)
+{
+ const RCF_t def_rcf = DEF_RCFILE;
+ char fbuf[MEDBUFSIZ];
+ int i, fd;
+ RCF_t rcf;
+ float delay = Rc.delay_time;
+
+ // read part of an old-style config in /etc/toprc
+ fd = open(SYS_RCFILESPEC, O_RDONLY);
+ if (fd > 0) {
+ ssize_t num;
+ num = read(fd, fbuf, sizeof(fbuf) - 1);
+ if (num > 0) {
+ const char *sec = strchr(fbuf, 's');
+ const char *eol = strchr(fbuf, '\n');
+ if (eol) {
+ const char *two = eol + 1; // line two
+ if (sec < eol) Secure_mode = !!sec;
+ eol = strchr(two, '\n');
+ if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two);
+ }
+ }
+ close(fd);
+ }
+
+ if (getenv("TOPRC")) { // should switch on Myname before documenting this?
+ // not the most optimal here...
+ snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC"));
+ } else {
+ snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew...
+ if (getenv("HOME"))
+ snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname);
+ }
+
+ rcf = def_rcf;
+ fd = open(Rc_name, O_RDONLY);
+ if (fd > 0) {
+ ssize_t num;
+ num = read(fd, fbuf+2, sizeof(fbuf) -3);
+ if (num > 0) {
+ fbuf[0] = '\n';
+ fbuf[1] = '\n';
+ fbuf[num+2] = '\0';
+//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf));
+//sleep(2);
+ if (rc_read_new(fbuf, &rcf) < 0) {
+ rcf = def_rcf; // on failure, maybe mangled
+ if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf;
+ }
+ delay = rcf.delay_time;
+ }
+ close(fd);
+ }
+
+ // update Rc defaults, establish a Curwin and fix up the window stack
+ Rc.mode_altscr = rcf.mode_altscr;
+ Rc.mode_irixps = rcf.mode_irixps;
+ if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0;
+ Curwin = &Winstk[rcf.win_index];
+ for (i = 0; i < GROUPSMAX; i++) {
+ memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]);
+ confighlp(Winstk[i].rc.fieldscur);
+ }
+
+ if(Rc.mode_irixps && smp_num_cpus>1 &&
+ !(CHKw(Curwin, Show_THREADS))) {
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+ Fieldstab[P_CPU].fmts = " %4.0f";
+ }
+
+ // lastly, establish the true runtime secure mode and delay time
+ if (!getuid()) Secure_mode = 0;
+ if (!Secure_mode) Rc.delay_time = delay;
+}
+
+
+// Parse command line arguments.
+// Note: it's assumed that the rc file(s) have already been read
+// and our job is to see if any of those options are to be
+// overridden -- we'll force some on and negate others in our
+// best effort to honor the loser's (oops, user's) wishes...
+static void parse_args (char **args)
+{
+ /* differences between us and the former top:
+ -C (separate CPU states for SMP) is left to an rcfile
+ -p (pid monitoring) allows a comma delimited list
+ -q (zero delay) eliminated as redundant, incomplete and inappropriate
+ use: "nice -n-10 top -d0" to achieve what was only claimed
+ -c,i,S act as toggles (not 'on' switches) for enhanced user flexibility
+ . no deprecated/illegal use of 'breakargv:' with goto
+ . bunched args are actually handled properly and none are ignored
+ . we tolerate NO whitespace and NO switches -- maybe too tolerant? */
+ static const char usage[] =
+ " -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]";
+ float tmp_delay = MAXFLOAT;
+ char *p;
+
+ while (*args) {
+ const char *cp = *(args++);
+ if (*cp!='-') {
+ std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s" , *cp, Myname, usage));
+ }
+
+ for(++cp; *cp; cp++) {
+ char *end;
+ switch (*cp) {
+ case '\0':
+ case '-':
+ break;
+ case 'b':
+ Batch = 1;
+ break;
+ case 'c':
+ TOGw(Curwin, Show_CMDLIN);
+ break;
+ case 'd':
+ if (cp[1]) ++cp;
+ else if (*args) cp = *args++;
+ else std_err("-d requires argument");
+ /* a negative delay will be dealt with shortly... */
+ errno=0;
+ if (( (fabs(tmp_delay=strtod(cp, &end))==HUGE_VAL &&
+ errno==ERANGE) ||
+ (tmp_delay==0 && errno!=0) ||
+ end==cp ||
+ end!=cp+strlen(cp))) {
+ std_err(fmtmk("bad delay '%s'", cp));
+ }
+ cp=-1+end;
+ break;
+ case 'H':
+ TOGw(Curwin, Show_THREADS);
+ break;
+ case 'h':
+ case 'v': case 'V':
+ std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage));
+ case 'i':
+ TOGw(Curwin, Show_IDLEPS);
+ Curwin->rc.maxtasks = 0;
+ break;
+ case 'n':
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-n requires argument");
+ errno=0;
+ if ((((Loops=strtol(cp, &end, 0))==LONG_MIN ||
+ Loops==LONG_MAX) &&
+ errno==ERANGE) ||
+ end==cp ||
+ end!=cp+strlen(cp) || Loops<1) {
+ std_err(fmtmk("bad iterations arg '%s'", cp));
+ }
+ cp=-1+end;
+ break;
+ case 'p':
+ do {
+ if (selection_type && selection_type != 'p') std_err("conflicting process selection");
+ selection_type = 'p';
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-p requires argument");
+ if (Monpidsidx >= MONPIDMAX)
+ std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
+ errno=0;
+ if ((((Monpids[Monpidsidx]=strtol(cp, &end, 0))==LONG_MIN ||
+ Monpids[Monpidsidx]==LONG_MAX) &&
+ errno==ERANGE) ||
+ end==cp ||
+ Monpids[Monpidsidx]<1) {
+ std_err(fmtmk("bad pid '%s'", cp));
+ }
+ cp=-1+end;
+
+ if (!Monpids[Monpidsidx])
+ Monpids[Monpidsidx] = getpid();
+ Monpidsidx++;
+ if (!(p = strchr(cp, ',')))
+ break;
+ cp = p;
+ } while (*cp);
+ break;
+ case 's':
+ Secure_mode = 1;
+ break;
+ case 'S':
+ TOGw(Curwin, Show_CTIMES);
+ break;
+ case 'u':
+ do {
+ const char *errmsg;
+ if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection");
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-u missing name");
+ errmsg = parse_uid(cp, &selection_uid);
+ if (errmsg) std_err(errmsg);
+ selection_type = 'u';
+ cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp)-1; // FIXME: junk
+ } while(0);
+ break;
+ case 'U':
+ do {
+ const char *errmsg;
+ if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection");
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-u missing name");
+ errmsg = parse_uid(cp, &selection_uid);
+ if (errmsg) std_err(errmsg);
+ selection_type = 'U';
+ cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
+ } while(0);
+ break;
+ default :
+ std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
+ , *cp, Myname, usage));
+
+ } /* end: switch (*cp) */
+ } /* end: for (; *cp) */
+ } /* end: while (*args) */
+
+ /* fixup delay time, maybe... */
+ if (MAXFLOAT != tmp_delay) {
+ if (Secure_mode || tmp_delay < 0)
+ msg_save("Delay time Not changed");
+ else
+ Rc.delay_time = tmp_delay;
+ }
+}
+
+
+ /*
+ * Set up the terminal attributes */
+static void whack_terminal (void)
+{
+ struct termios newtty;
+
+ if (Batch) {
+ setupterm("dumb", STDOUT_FILENO, NULL);
+ return;
+ }
+ setupterm(NULL, STDOUT_FILENO, NULL);
+ if (tcgetattr(STDIN_FILENO, &Savedtty) == -1)
+ std_err("failed tty get");
+ newtty = Savedtty;
+ newtty.c_lflag &= ~(ICANON | ECHO);
+ newtty.c_oflag &= ~(TAB3);
+ newtty.c_cc[VMIN] = 1;
+ newtty.c_cc[VTIME] = 0;
+
+ Ttychanged = 1;
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) {
+ putp(Cap_clr_scr);
+ std_err(fmtmk("failed tty set: %s", strerror(errno)));
+ }
+ tcgetattr(STDIN_FILENO, &Rawtty);
+#ifndef STDOUT_IOLBF
+ // thanks anyway stdio, but we'll manage buffering at the frame level...
+ setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf));
+#endif
+ putp(Cap_clr_scr);
+ fflush(stdout);
+}
+
+
+/*###### Field Selection/Ordering routines #############################*/
+
+
+// Display each field represented in the Fields Table along with its
+// description and mark (with a leading asterisk) fields associated
+// with upper case letter(s) in the passed 'fields string'.
+//
+// After all fields have been displayed, some extra explanatory
+// text may also be output
+static void display_fields (const char *fields, const char *xtra)
+{
+#define yRSVD 3
+ const char *p;
+ int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD;
+
+ /* we're relying on callers to first clear the screen and thus avoid screen
+ flicker if they're too lazy to handle their own asterisk (*) logic */
+ putp(Curwin->cap_bold);
+ for (i = 0; fields[i]; ++i) {
+ const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]);
+ int b = isupper(fields[i]);
+
+ if (!f) continue; // hey, should be std_err!
+ for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces
+ ;
+ PUTT("%s%s%c %c: %-10s = %s",
+ tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
+ b ? Curwin->cap_bold : Cap_norm,
+ b ? '*' : ' ',
+ fields[i],
+ p,
+ f->desc
+ );
+ }
+ if (xtra) {
+ putp(Curwin->capclr_rownorm);
+ while ((p = strchr(xtra, '\n'))) {
+ ++i;
+ PUTT("%s%.*s",
+ tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
+ (int)(p - xtra),
+ xtra
+ );
+ xtra = ++p;
+ }
+ }
+ putp(Caps_off);
+
+#undef yRSVD
+}
+
+
+// Change order of displayed fields.
+static void fields_reorder (void)
+{
+ static const char prompt[] =
+ "Upper case letter moves field left, lower case right";
+ char c, *p;
+ int i;
+
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ for (;;) {
+ display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
+ show_special(1, fmtmk(FIELDS_current
+ , Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
+ chin(0, &c, 1);
+ if (!ft_get_ptr(FT_NEW_fmt, c)) break;
+ i = toupper(c) - 'A';
+ if (((p = strchr(Curwin->rc.fieldscur, i + 'A')))
+ || ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) {
+ if (isupper(c)) p--;
+ if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) {
+ c = p[0];
+ p[0] = p[1];
+ p[1] = c;
+ }
+ }
+ }
+ putp(Cap_curs_norm);
+}
+
+// Select sort field.
+static void fields_sort (void)
+{
+ static const char prompt[] =
+ "Select sort field via field letter, type any other key to return";
+ char phoney[PFLAGSSIZ];
+ char c, *p;
+ int i, x;
+
+ strcpy(phoney, NUL_FIELDS);
+ x = i = Curwin->rc.sortindx;
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ for (;;) {
+ p = phoney + i;
+ *p = toupper(*p);
+ display_fields(phoney, SORT_xtra);
+ show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt));
+ chin(0, &c, 1);
+ if (!ft_get_ptr(FT_NEW_fmt, c)) break;
+ i = toupper(c) - 'A';
+ *p = tolower(*p);
+ x = i;
+ }
+ if ((p = strchr(Curwin->rc.fieldscur, x + 'a')))
+ *p = x + 'A';
+ Curwin->rc.sortindx = x;
+ putp(Cap_curs_norm);
+}
+
+
+// Toggle displayed fields.
+static void fields_toggle (void)
+{
+ static const char prompt[] =
+ "Toggle fields via field letter, type any other key to return";
+ char c, *p;
+ int i;
+
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ for (;;) {
+ display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
+ show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
+ chin(0, &c, 1);
+ if (!ft_get_ptr(FT_NEW_fmt, c)) break;
+ i = toupper(c) - 'A';
+ if ((p = strchr(Curwin->rc.fieldscur, i + 'A')))
+ *p = i + 'a';
+ else if ((p = strchr(Curwin->rc.fieldscur, i + 'a')))
+ *p = i + 'A';
+ }
+ putp(Cap_curs_norm);
+}
+
+/*###### Windows/Field Groups support #################################*/
+
+// For each of the four windows:
+// 1) Set the number of fields/columns to display
+// 2) Create the field columns heading
+// 3) Set maximum cmdline length, if command lines are in use
+// In the process, the required PROC_FILLxxx flags will be rebuilt!
+static void reframewins (void)
+{
+ WIN_t *w;
+ char *s;
+ const char *h;
+ int i, needpsdb = 0;
+
+// Frames_libflags = 0; // should be called only when it's zero
+// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible
+ w = Curwin;
+ do {
+ if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) {
+ // build window's procflags array and establish a tentative maxpflgs
+ for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) {
+ if (isupper(w->rc.fieldscur[i]))
+ w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A';
+ }
+
+ /* build a preliminary columns header not to exceed screen width
+ while accounting for a possible leading window number */
+ *(s = w->columnhdr) = '\0';
+ if (Rc.mode_altscr) s = scat(s, " ");
+ for (i = 0; i < w->maxpflgs; i++) {
+ h = Fieldstab[w->procflags[i]].head;
+ // oops, won't fit -- we're outta here...
+ if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break;
+ s = scat(s, h);
+ }
+
+ // establish the final maxpflgs and prepare to grow the command column
+ // heading via maxcmdln - it may be a fib if P_CMD wasn't encountered,
+ // but that's ok because it won't be displayed anyway
+ w->maxpflgs = i;
+ w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head));
+
+ // finally, we can build the true run-time columns header, format the
+ // command column heading, if P_CMD is really being displayed, and
+ // rebuild the all-important PROC_FILLxxx flags that will be used
+ // until/if we're we're called again
+ *(s = w->columnhdr) = '\0';
+// if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum));
+ for (i = 0; i < w->maxpflgs; i++) {
+ int advance = (i==0) && !Rc.mode_altscr;
+ h = Fieldstab[w->procflags[i]].head;
+ if (P_WCH == w->procflags[i]) needpsdb = 1;
+ if (P_CMD == w->procflags[i]) {
+ s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/ ));
+ if (CHKw(w, Show_CMDLIN)) {
+ Frames_libflags |= L_CMDLINE;
+// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln;
+ }
+ } else
+ s = scat(s, h+advance);
+ Frames_libflags |= Fieldstab[w->procflags[i]].lflg;
+ }
+ if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0';
+ }
+ if (Rc.mode_altscr) w = w->next;
+ } while (w != Curwin);
+
+ // do we need the kernel symbol table (and is it already open?)
+ if (needpsdb) {
+ if (No_ksyms == -1) {
+ No_ksyms = 0;
+ if (open_psdb_message(NULL, msg_save))
+ No_ksyms = 1;
+ else
+ PSDBopen = 1;
+ }
+ }
+
+ if (selection_type=='U') Frames_libflags |= L_status;
+
+ if (Frames_libflags & L_EITHER) {
+ Frames_libflags &= ~L_EITHER;
+ if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status;
+ }
+ if (!Frames_libflags) Frames_libflags = L_DEFAULT;
+ if (selection_type=='p') Frames_libflags |= PROC_PID;
+}
+
+
+// Value a window's name and make the associated group name.
+static void win_names (WIN_t *q, const char *name)
+{
+ sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name);
+ sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name);
+}
+
+
+// Display a window/field group (ie. make it "current").
+static void win_select (char ch)
+{
+ static const char prompt[] = "Choose field group (1 - 4)";
+
+ /* if there's no ch, it means we're supporting the external interface,
+ so we must try to get our own darn ch by begging the user... */
+ if (!ch) {
+ show_pmt(prompt);
+ chin(0, (char *)&ch, 1);
+ }
+ switch (ch) {
+ case 'a': /* we don't carry 'a' / 'w' in our */
+ Curwin = Curwin->next; /* pmt - they're here for a good */
+ break; /* friend of ours -- wins_colors. */
+ case 'w': /* (however those letters work via */
+ Curwin = Curwin->prev; /* the pmt too but gee, end-loser */
+ break; /* should just press the darn key) */
+ case '1': case '2':
+ case '3': case '4':
+ Curwin = &Winstk[ch - '1'];
+ break;
+ }
+}
+
+
+// Just warn the user when a command can't be honored.
+static int win_warn (void)
+{
+ show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname));
+ // we gotta' return false 'cause we're somewhat well known within
+ // macro society, by way of that sassy little tertiary operator...
+ return 0;
+}
+
+
+// Change colors *Helper* function to save/restore settings;
+// ensure colors will show; and rebuild the terminfo strings.
+static void winsclrhlp (WIN_t *q, int save)
+{
+ static int flgssav, summsav, msgssav, headsav, tasksav;
+
+ if (save) {
+ flgssav = q->rc.winflags; summsav = q->rc.summclr;
+ msgssav = q->rc.msgsclr; headsav = q->rc.headclr; tasksav = q->rc.taskclr;
+ SETw(q, Show_COLORS);
+ } else {
+ q->rc.winflags = flgssav; q->rc.summclr = summsav;
+ q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav;
+ }
+ capsmk(q);
+}
+
+
+// Change colors used in display
+static void wins_colors (void)
+{
+#define kbdABORT 'q'
+#define kbdAPPLY '\n'
+ int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr;
+ char ch, tgt = 'T';
+
+ if (0 >= max_colors) {
+ show_msg("\aNo colors to map!");
+ return;
+ }
+ winsclrhlp(Curwin, 1);
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+
+ do {
+ putp(Cap_home);
+ /* this string is well above ISO C89's minimum requirements! */
+ show_special(
+ 1,
+ fmtmk(
+ COLOR_help,
+ procps_version,
+ Curwin->grpname,
+ CHKw(Curwin, View_NOBOLD) ? "On" : "Off",
+ CHKw(Curwin, Show_COLORS) ? "On" : "Off",
+ CHKw(Curwin, Show_HIBOLD) ? "On" : "Off",
+ tgt,
+ clr,
+ Curwin->grpname
+ )
+ );
+ chin(0, &ch, 1);
+ switch (ch) {
+ case 'S':
+ pclr = &Curwin->rc.summclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'M':
+ pclr = &Curwin->rc.msgsclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'H':
+ pclr = &Curwin->rc.headclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'T':
+ pclr = &Curwin->rc.taskclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case '0' ... '7':
+ clr = ch - '0';
+ *pclr = clr;
+ break;
+ case 'B':
+ TOGw(Curwin, View_NOBOLD);
+ break;
+ case 'b':
+ TOGw(Curwin, Show_HIBOLD);
+ break;
+ case 'z':
+ TOGw(Curwin, Show_COLORS);
+ break;
+ case 'a':
+ case 'w':
+ win_select(ch);
+ winsclrhlp(Curwin, 1);
+ clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr;
+ tgt = 'T';
+ break;
+ }
+ capsmk(Curwin);
+ } while (kbdAPPLY != ch && kbdABORT != ch);
+
+ if (kbdABORT == ch)
+ winsclrhlp(Curwin, 0);
+ putp(Cap_curs_norm);
+
+#undef kbdABORT
+#undef kbdAPPLY
+}
+
+
+// Manipulate flag(s) for all our windows.
+static void wins_reflag (int what, int flg)
+{
+ WIN_t *w;
+
+ w = Curwin;
+ do {
+ switch (what) {
+ case Flags_TOG:
+ TOGw(w, flg);
+ break;
+ case Flags_SET: /* Ummmm, i can't find anybody */
+ SETw(w, flg); /* who uses Flags_set ... */
+ break;
+ case Flags_OFF:
+ OFFw(w, flg);
+ break;
+ }
+ // a flag with special significance -- user wants to rebalance
+ // display so we gotta' 'off' one number then force on two flags...
+ if (EQUWINS_cwo == flg) {
+ w->rc.maxtasks = 0;
+ SETw(w, Show_IDLEPS | VISIBLE_tsk);
+ }
+ w = w->next;
+ } while (w != Curwin);
+}
+
+
+// using a flag to avoid other code seeing inconsistant state
+static volatile int need_resize;
+static void wins_resize_sighandler (int dont_care_sig)
+{
+ (void)dont_care_sig;
+ need_resize = 1;
+ ZAP_TIMEOUT
+}
+
+
+// Set the screen dimensions and arrange for the real workhorse.
+// (also) catches:
+// SIGWINCH and SIGCONT
+static void wins_resize (void)
+{
+ struct winsize wz;
+ char *env_columns; // Unix98 environment variable COLUMNS
+ char *env_lines; // Unix98 environment variable LINES
+
+ Screen_cols = columns; // <term.h>
+ Screen_rows = lines; // <term.h>
+
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) {
+ Screen_cols = wz.ws_col;
+ Screen_rows = wz.ws_row;
+ }
+
+ if (Batch) Screen_rows = MAXINT;
+
+ env_columns = getenv("COLUMNS");
+ if(env_columns && *env_columns){
+ long t;
+ char *endptr;
+ t = strtol(env_columns, &endptr, 0);
+ if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t;
+ }
+ env_lines = getenv("LINES");
+ if(env_lines && *env_lines){
+ long t;
+ char *endptr;
+ t = strtol(env_lines, &endptr, 0);
+ if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t;
+ }
+
+ // be crudely tolerant of crude tty emulators
+ if (avoid_last_column) Screen_cols--;
+
+ // we might disappoint some folks (but they'll deserve it)
+ if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX;
+
+ // keep our support for output optimization in sync with current reality
+ // note: when we're in Batch mode, we don't really need a Pseudo_scrn and
+ // when not Batch, our buffer will contain 1 extra 'line' since
+ // Msg_row is never represented -- but it's nice to have some space
+ // between us and the great-beyond...
+ Pseudo_cols = Screen_cols + CLRBUFSIZ + 1;
+ if (Batch) Pseudo_size = ROWBUFSIZ + 1;
+ else Pseudo_size = Pseudo_cols * Screen_rows;
+ if( Pseudo_scrn.buf == NULL || Pseudo_size > Pseudo_scrn.mem_size ) {
+ Pseudo_scrn.buf = alloc_r(Pseudo_scrn.buf, Pseudo_size);
+ Pseudo_scrn.mem_size = Pseudo_size;
+ }
+
+ // force rebuild of column headers AND libproc/readproc requirements
+ Frames_libflags = 0;
+}
+
+
+// Set up the raw/incomplete field group windows --
+// they'll be finished off after startup completes.
+// [ and very likely that will override most/all of our efforts ]
+// [ --- life-is-NOT-fair --- ]
+static void windows_stage1 (void)
+{
+ WIN_t *w;
+ int i;
+
+ for (i = 0; i < GROUPSMAX; i++) {
+ w = &Winstk[i];
+ w->winnum = i + 1;
+ w->rc = Rc.win[i];
+ w->captab[0] = Cap_norm;
+ w->captab[1] = Cap_norm;
+ w->captab[2] = w->cap_bold;
+ w->captab[3] = w->capclr_sum;
+ w->captab[4] = w->capclr_msg;
+ w->captab[5] = w->capclr_pmt;
+ w->captab[6] = w->capclr_hdr;
+ w->captab[7] = w->capclr_rowhigh;
+ w->captab[8] = w->capclr_rownorm;
+ w->next = w + 1;
+ w->prev = w - 1;
+ ++w;
+ }
+ /* fixup the circular chains... */
+ Winstk[3].next = &Winstk[0];
+ Winstk[0].prev = &Winstk[3];
+ Curwin = Winstk;
+}
+
+
+// This guy just completes the field group windows after the
+// rcfiles have been read and command line arguments parsed
+static void windows_stage2 (void)
+{
+ int i;
+
+ for (i = 0; i < GROUPSMAX; i++) {
+ win_names(&Winstk[i], Winstk[i].rc.winname);
+ capsmk(&Winstk[i]);
+ }
+ // rely on this next guy to force a call (eventually) to reframewins
+ wins_resize();
+}
+
+
+/*###### Main Screen routines ##########################################*/
+
+// Process keyboard input during the main loop
+static void do_key (unsigned c)
+{
+ // standardized 'secure mode' errors
+ static const char err_secure[] = "\aUnavailable in secure mode";
+ static const char err_num_cpus[] = "\aSorry, terminal is not big enough";
+#ifdef WARN_NOT_SMP
+ // standardized 'smp' errors
+ static const char err_smp[] = "\aSorry, only 1 cpu detected";
+#endif
+
+ switch (c) {
+ case '1':
+ if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) {
+ show_msg(err_num_cpus);
+ break;
+ }
+#ifdef WARN_NOT_SMP
+ if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM);
+ else show_msg(err_smp);
+#else
+ TOGw(Curwin, View_CPUSUM);
+#endif
+ break;
+
+ case 'a':
+ if (Rc.mode_altscr) Curwin = Curwin->next;
+ break;
+
+ case 'A':
+ Rc.mode_altscr = !Rc.mode_altscr;
+ wins_resize();
+ break;
+
+ case 'b':
+ if (VIZCHKc) {
+ if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS))
+ show_msg("\aNothing to highlight!");
+ else {
+ TOGw(Curwin, Show_HIBOLD);
+ capsmk(Curwin);
+ }
+ }
+ break;
+
+ case 'B':
+ TOGw(Curwin, View_NOBOLD);
+ capsmk(Curwin);
+ break;
+
+ case 'c':
+ VIZTOGc(Show_CMDLIN);
+ break;
+
+ case 'd':
+ case 's':
+ if (Secure_mode)
+ show_msg(err_secure);
+ else {
+ float tmp =
+ get_float(fmtmk("Change delay from %.1f to", Rc.delay_time));
+ if (tmp == 0.0) show_msg("\aNot valid");
+ else if (tmp > 0) Rc.delay_time = tmp;
+ }
+ break;
+
+ case 'f':
+ if (VIZCHKc) fields_toggle();
+ break;
+
+ case 'F':
+ case 'O':
+ if (VIZCHKc) fields_sort();
+ break;
+
+ case 'g':
+ if (Rc.mode_altscr) {
+ char tmp[GETBUFSIZ];
+ strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname)));
+ if (tmp[0]) win_names(Curwin, tmp);
+ }
+ break;
+
+ case 'G':
+ win_select(0);
+ break;
+
+ case 'H':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_THREADS);
+ if(Rc.mode_irixps && smp_num_cpus>1 &&
+ !(CHKw(Curwin, Show_THREADS))){
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+ Fieldstab[P_CPU].fmts = " %4.0f";
+ } else {
+ pcpu_max_value = 99.9;
+ Fieldstab[P_CPU].fmts = " %#4.1f";
+ }
+ show_msg(fmtmk("Show threads %s"
+ , CHKw(Curwin, Show_THREADS) ? "On" : "Off"));
+ }
+ break;
+
+ case 'h':
+ case '?':
+ { char ch;
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ /* this string is well above ISO C89's minimum requirements! */
+ show_special(
+ 1,
+ fmtmk(
+ KEYS_help,
+ procps_version,
+ Curwin->grpname,
+ CHKw(Curwin, Show_CTIMES) ? "On" : "Off",
+ Rc.delay_time,
+ Secure_mode ? "On" : "Off",
+ Secure_mode ? "" : KEYS_help_unsecured
+ )
+ );
+ chin(0, &ch, 1);
+ if ('?' == ch || 'h' == ch) {
+ do {
+ putp(Cap_clr_scr);
+ show_special(1, fmtmk(WINDOWS_help
+ , Curwin->grpname
+ , Winstk[0].rc.winname
+ , Winstk[1].rc.winname
+ , Winstk[2].rc.winname
+ , Winstk[3].rc.winname));
+ chin(0, &ch, 1);
+ win_select(ch);
+ } while ('\n' != ch);
+ }
+ putp(Cap_curs_norm);
+ }
+ break;
+
+ case 'i':
+ VIZTOGc(Show_IDLEPS);
+ break;
+
+ case 'I':
+#ifdef WARN_NOT_SMP
+ if (Cpu_tot > 1) {
+ Rc.mode_irixps = !Rc.mode_irixps;
+ show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
+ } else
+ show_msg(err_smp);
+#else
+ Rc.mode_irixps = !Rc.mode_irixps;
+ show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
+#endif
+ if(Rc.mode_irixps && smp_num_cpus>1 &&
+ !(CHKw(Curwin, Show_THREADS))){
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+ Fieldstab[P_CPU].fmts = " %4.0f";
+ } else {
+ pcpu_max_value = 99.9;
+ Fieldstab[P_CPU].fmts = " %#4.1f";
+ }
+ break;
+
+ case 'k':
+ if (Secure_mode) {
+ show_msg(err_secure);
+ } else {
+ int sig, pid = get_int("PID to kill");
+ if (pid > 0) {
+ sig = signal_name_to_number(
+ ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL)));
+ if (sig == -1) sig = DEF_SIGNAL;
+ if (sig && kill(pid, sig))
+ show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno)));
+ }
+ }
+ break;
+
+ case 'l':
+ TOGw(Curwin, View_LOADAV);
+ break;
+
+ case 'm':
+ TOGw(Curwin, View_MEMORY);
+ break;
+
+ case 'n':
+ case '#':
+ if (VIZCHKc) {
+ int num =
+ get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks));
+ if (num > -1) Curwin->rc.maxtasks = num;
+ }
+ break;
+
+ case 'o':
+ if (VIZCHKc) fields_reorder();
+ break;
+
+ case 'q':
+ end_pgm(0);
+
+ case 'r':
+ if (Secure_mode)
+ show_msg(err_secure);
+ else {
+ int val, pid = get_int("PID to renice");
+ if (pid > 0) {
+ val = get_int(fmtmk("Renice PID %d to value", pid));
+ if (setpriority(PRIO_PROCESS, (unsigned)pid, val))
+ show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno)));
+ }
+ }
+ break;
+
+ case 'R':
+ VIZTOGc(Qsrt_NORMAL);
+ break;
+
+ case 'S':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_CTIMES);
+ show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off"));
+ }
+ break;
+
+ case 't':
+ TOGw(Curwin, View_STATES);
+ break;
+
+// case 'u':
+// if (VIZCHKc)
+// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)"));
+// break;
+
+ case 'u':
+// if (!VIZCHKc) break;
+ do {
+ const char *errmsg;
+ const char *answer;
+ answer = ask4str("Which user (blank for all)");
+ // FIXME: do this better:
+ if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
+ selection_type = 0;
+ selection_uid = -1;
+ break;
+ }
+ errmsg = parse_uid(answer, &selection_uid);
+ if (errmsg) {
+ show_msg(errmsg);
+ // Change settings here? I guess not.
+ break;
+ }
+ selection_type = 'u';
+ } while(0);
+ break;
+
+ case 'U':
+// if (!VIZCHKc) break;
+ do {
+ const char *errmsg;
+ const char *answer;
+ answer = ask4str("Which user (blank for all)");
+ // FIXME: do this better:
+ if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
+ selection_type = 0;
+ selection_uid = -1;
+ break;
+ }
+ errmsg = parse_uid(answer, &selection_uid);
+ if (errmsg) {
+ show_msg(errmsg);
+ // Change settings here? I guess not.
+ break;
+ }
+ selection_type = 'U';
+ } while(0);
+ break;
+
+ case 'w':
+ if (Rc.mode_altscr) Curwin = Curwin->prev;
+ break;
+
+ case 'W':
+ { const char *err = rc_write_whatever();
+ if (err)
+ show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err));
+ else
+ show_msg(fmtmk("Wrote configuration to '%s'", Rc_name));
+ }
+ break;
+
+ case 'x':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_HICOLS);
+ capsmk(Curwin);
+ }
+ break;
+
+ case 'y':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_HIROWS);
+ capsmk(Curwin);
+ }
+ break;
+
+ case 'z':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_COLORS);
+ capsmk(Curwin);
+ }
+ break;
+
+ case 'Z':
+ wins_colors();
+ break;
+
+ case '-':
+ if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk);
+ break;
+
+ case '_':
+ if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk);
+ break;
+
+ case '=':
+ Curwin->rc.maxtasks = 0;
+ SETw(Curwin, Show_IDLEPS | VISIBLE_tsk);
+ Monpidsidx = 0;
+ selection_type = '\0';
+ break;
+
+ case '+':
+ if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo);
+ break;
+
+ case '<':
+ if (VIZCHKc) {
+ FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1;
+ while (*p != Curwin->rc.sortindx) --p;
+ if (--p >= Curwin->procflags)
+ Curwin->rc.sortindx = *p;
+ }
+ break;
+
+ case '>':
+ if (VIZCHKc) {
+ FLG_t *p = Curwin->procflags;
+ while (*p != Curwin->rc.sortindx) ++p;
+ if (++p < Curwin->procflags + Curwin->maxpflgs)
+ Curwin->rc.sortindx = *p;
+ }
+ break;
+
+ case 'M': // these keys represent old-top compatability
+ case 'N': // -- grouped here so that if users could ever
+ case 'P': // be weaned, we would just whack this part...
+ case 'T':
+ { static struct {
+ const char *xmsg;
+ const unsigned xkey;
+ const FLG_t sort;
+ } xtab[] = {
+ { "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, },
+ { "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, };
+ int i;
+ for (i = 0; i < MAXTBL(xtab); ++i)
+ if (c == xtab[i].xkey) {
+ Curwin->rc.sortindx = xtab[i].sort;
+// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg));
+ break;
+ }
+ }
+ break;
+
+ case '\n': // just ignore these, they'll have the effect
+ case ' ': // of refreshing display after waking us up !
+ break;
+
+ default:
+ show_msg("\aUnknown command - try 'h' for help");
+ }
+ // The following assignment will force a rebuild of all column headers and
+ // the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are
+ // some keys that COULD require new column headers and/or libproc flags:
+ // 'A' - likely
+ // 'c' - likely when !Mode_altscr, maybe when Mode_altscr
+ // 'F' - maybe, if new field forced on
+ // 'f' - likely
+ // 'G' - likely
+ // 'O' - maybe, if new field forced on
+ // 'o' - maybe, if new field brought into view
+ // 'Z' - likely, if 'Curwin' changed when !Mode_altscr
+ // '-' - likely (restricted to Mode_altscr)
+ // '_' - likely (restricted to Mode_altscr)
+ // '=' - maybe, but only when Mode_altscr
+ // '+' - likely (restricted to Mode_altscr)
+ // ( At this point we have a human being involved and so have all the time )
+ // ( in the world. We can afford a few extra cpu cycles every now & then! )
+ Frames_libflags = 0;
+}
+
+
+// State display *Helper* function to calc and display the state
+// percentages for a single cpu. In this way, we can support
+// the following environments without the usual code bloat.
+// 1) single cpu machines
+// 2) modest smp boxes with room for each cpu's percentages
+// 3) massive smp guys leaving little or no room for process
+// display and thus requiring the cpu summary toggle
+static void summaryhlp (CPU_t *cpu, const char *pfx)
+{
+ // we'll trim to zero if we get negative time ticks,
+ // which has happened with some SMP kernels (pre-2.4?)
+#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz)
+ SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz;
+ float scale;
+
+ u_frme = cpu->u - cpu->u_sav;
+ s_frme = cpu->s - cpu->s_sav;
+ n_frme = cpu->n - cpu->n_sav;
+ i_frme = TRIMz(cpu->i - cpu->i_sav);
+ if ((u_frme == 0) && (i_frme == 0)) i_frme = 100.0;
+ w_frme = cpu->w - cpu->w_sav;
+ x_frme = cpu->x - cpu->x_sav;
+ y_frme = cpu->y - cpu->y_sav;
+ z_frme = cpu->z - cpu->z_sav;
+ tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
+ if (tot_frme < 1) tot_frme = 1;
+ scale = 100.0 / (float)tot_frme;
+
+ // display some kinda' cpu state percentages
+ // (who or what is explained by the passed prefix)
+ show_special(
+ 0,
+ fmtmk(
+ States_fmts,
+ pfx,
+ (float)u_frme * scale,
+ (float)s_frme * scale,
+ (float)n_frme * scale,
+ (float)i_frme * scale,
+ (float)w_frme * scale,
+ (float)x_frme * scale,
+ (float)y_frme * scale,
+ (float)z_frme * scale
+ )
+ );
+ Msg_row += 1;
+
+ // remember for next time around
+ cpu->u_sav = cpu->u;
+ cpu->s_sav = cpu->s;
+ cpu->n_sav = cpu->n;
+ cpu->i_sav = cpu->i;
+ cpu->w_sav = cpu->w;
+ cpu->x_sav = cpu->x;
+ cpu->y_sav = cpu->y;
+ cpu->z_sav = cpu->z;
+
+#undef TRIMz
+}
+
+
+// Begin a new frame by:
+// 1) Refreshing the all important proc table
+// 2) Displaying uptime and load average (maybe)
+// 3) Displaying task/cpu states (maybe)
+// 4) Displaying memory & swap usage (maybe)
+// and then, returning a pointer to the pointers to the proc_t's!
+static proc_t **summary_show (void)
+{
+ static proc_t **p_table = NULL;
+ static CPU_t *smpcpu = NULL;
+
+ // whoa first time, gotta' prime the pump...
+ if (!p_table) {
+ p_table = procs_refresh(NULL, Frames_libflags);
+ putp(Cap_clr_scr);
+ putp(Cap_rmam);
+#ifndef PROF
+ // sleep for half a second
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ select(0, NULL, NULL, NULL, &tv); // ought to loop until done
+#endif
+ } else {
+ putp(Batch ? "\n\n" : Cap_home);
+ }
+ p_table = procs_refresh(p_table, Frames_libflags);
+
+ // Display Uptime and Loadavg
+ if (CHKw(Curwin, View_LOADAV)) {
+ if (!Rc.mode_altscr) {
+ show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime()));
+ } else {
+ show_special(
+ 0,
+ fmtmk(
+ CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line,
+ Curwin->grpname,
+ sprint_uptime()
+ )
+ );
+ }
+ Msg_row += 1;
+ }
+
+ // Display Task and Cpu(s) States
+ if (CHKw(Curwin, View_STATES)) {
+ show_special(
+ 0,
+ fmtmk(
+ STATES_line1,
+ Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied
+ )
+ );
+ Msg_row += 1;
+
+ smpcpu = cpus_refresh(smpcpu);
+
+ if (CHKw(Curwin, View_CPUSUM)) {
+ // display just the 1st /proc/stat line
+ summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");
+ } else {
+ int i;
+ char tmp[SMLBUFSIZ];
+ // display each cpu's states separately
+ for (i = 0; i < Cpu_tot; i++) {
+ snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id);
+ summaryhlp(&smpcpu[i], tmp);
+ }
+ }
+ }
+
+ // Display Memory and Swap stats
+ meminfo();
+ if (CHKw(Curwin, View_MEMORY)) {
+ show_special(0, fmtmk(MEMORY_line1
+ , kb_main_total, kb_main_used, kb_main_free, kb_main_buffers));
+ fflush(stdout);
+ mem_info.main_total = kb_main_total;
+ mem_info.main_used = kb_main_used;
+ mem_info.main_free = kb_main_free;
+ mem_info.main_buffers = kb_main_buffers;
+
+ show_special(0, fmtmk(MEMORY_line2
+ , kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached));
+ Msg_row += 2;
+
+ mem_info.swap_total = kb_swap_total;
+ mem_info.swap_used = kb_swap_used;
+ mem_info.swap_free = kb_swap_free;
+ mem_info.main_cached = kb_main_cached;
+ }
+
+ sendto(sock_desc, (void *)&mem_info, sizeof(mem_info),0,(struct sockaddr *) &smartt_server, sizeof(smartt_server));
+
+ SETw(Curwin, NEWFRAM_cwo);
+ return p_table;
+}
+
+
+#define PAGES_TO_KB(n) (unsigned long)( (n) << page_to_kb_shift )
+
+// the following macro is our means to 'inline' emitting a column -- next to
+// procs_refresh, that's the most frequent and costly part of top's job !
+#define MKCOL(va...) do { \
+ if(likely(!( CHKw(q, Show_HICOLS) && q->rc.sortindx==i ))) { \
+ snprintf(cbuf, sizeof(cbuf), f, ## va); \
+ } else { \
+ snprintf(_z, sizeof(_z), f, ## va); \
+ snprintf(cbuf, sizeof(cbuf), "%s%s%s", \
+ q->capclr_rowhigh, \
+ _z+advance, \
+ !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \
+ ); \
+ advance=0; \
+ pad += q->len_rowhigh; \
+ if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \
+ } \
+} while (0)
+
+// Display information for a single task row.
+static void task_show (const WIN_t *q, const proc_t *p)
+{
+ char rbuf[ROWBUFSIZ];
+ char *rp = rbuf;
+ int j, x, pad;
+
+ *rp = '\0';
+
+ pad = Rc.mode_altscr;
+// if (pad) rp = scat(rp, " ");
+
+ for (x = 0; x < q->maxpflgs; x++) {
+ char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ];
+ FLG_t i = q->procflags[x]; // support for our field/column
+ const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt
+ unsigned s = Fieldstab[i].scale; // string must be altered !
+ unsigned w = Fieldstab[i].width;
+
+ int advance = (x==0) && !Rc.mode_altscr;
+
+ switch (i) {
+ case P_CMD:
+ { char tmp[ROWBUFSIZ];
+ unsigned flags;
+ int maxcmd = q->maxcmdln;
+ if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ else flags = ESC_DEFUNCT;
+ escape_command(tmp, p, sizeof tmp, &maxcmd, flags);
+ MKCOL(q->maxcmdln, q->maxcmdln, tmp);
+ }
+ break;
+ case P_COD:
+ MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s));
+ break;
+ case P_CPN:
+ MKCOL((unsigned)p->processor);
+ break;
+ case P_CPU:
+ { float u = (float)p->pcpu * Frame_tscale;
+ if (u > pcpu_max_value) u = pcpu_max_value;
+ MKCOL(u);
+ }
+ break;
+ case P_DAT:
+ MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s));
+ break;
+ case P_DRT:
+ MKCOL(scale_num((unsigned)p->dt, w, s));
+ break;
+ case P_FLG:
+ { char tmp[TNYBUFSIZ];
+ snprintf(tmp, sizeof(tmp), f, (long)p->flags);
+ for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.';
+ f = tmp;
+ MKCOL("");
+ }
+ break;
+ case P_FLT:
+ MKCOL(scale_num(p->maj_flt, w, s));
+ break;
+ case P_GRP:
+ MKCOL(p->egroup);
+ break;
+ case P_MEM:
+ MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total);
+ break;
+ case P_NCE:
+ MKCOL((int)p->nice);
+ break;
+ case P_PID:
+ MKCOL((unsigned)p->XXXID);
+ break;
+ case P_PPD:
+ MKCOL((unsigned)p->ppid);
+ break;
+ case P_PRI:
+ if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) {
+ f = " RT";
+ MKCOL("");
+ } else
+ MKCOL((int)p->priority);
+ break;
+ case P_RES:
+ MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s));
+ break;
+ case P_SHR:
+ MKCOL(scale_num(PAGES_TO_KB(p->share), w, s));
+ break;
+ case P_STA:
+ MKCOL(p->state);
+ break;
+ case P_SWP:
+ MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s));
+ break;
+ case P_TME:
+ case P_TM2:
+ { TIC_t t = p->utime + p->stime;
+ if (CHKw(q, Show_CTIMES))
+ t += (p->cutime + p->cstime);
+ MKCOL(scale_tics(t, w));
+ }
+ break;
+ case P_TTY:
+ { char tmp[TNYBUFSIZ];
+ dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV);
+ MKCOL(tmp);
+ }
+ break;
+ case P_UID:
+ MKCOL((unsigned)p->euid);
+ break;
+ case P_URE:
+ MKCOL(p->euser);
+ break;
+ case P_URR:
+ MKCOL(p->ruser);
+ break;
+ case P_VRT:
+ MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
+ break;
+ case P_WCH:
+ if (No_ksyms) {
+ f = " %08lx ";
+ MKCOL((long)p->wchan);
+ } else {
+ MKCOL(lookup_wchan(p->wchan, p->XXXID));
+ }
+ break;
+
+ } /* end: switch 'procflag' */
+
+ rp = scat(rp, cbuf+advance);
+ } /* end: for 'maxpflgs' */
+
+ PUFF(
+ "\n%s%.*s%s%s",
+ (CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm,
+ Screen_cols + pad,
+ rbuf,
+ Caps_off,
+ "" /*Cap_clr_eol*/
+ );
+
+#undef MKCOL
+}
+
+
+// Squeeze as many tasks as we can into a single window,
+// after sorting the passed proc table.
+static void window_show (proc_t **ppt, WIN_t *q, int *lscr)
+{
+#ifdef SORT_SUPRESS
+ // the 1 flag that DOES and 2 flags that MAY impact our proc table qsort
+#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES )
+ static FLG_t sav_indx = 0;
+ static int sav_flgs = -1;
+#endif
+ int i, lwin;
+
+ // Display Column Headings -- and distract 'em while we sort (maybe)
+ PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol);
+
+#ifdef SORT_SUPRESS
+ if (CHKw(Curwin, NEWFRAM_cwo)
+ || sav_indx != q->rc.sortindx
+ || sav_flgs != (q->rc.winflags & srtMASK)) {
+ sav_indx = q->rc.sortindx;
+ sav_flgs = (q->rc.winflags & srtMASK);
+#endif
+ if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed!
+ else Frame_srtflg = -1;
+ Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe
+ Frame_cmdlin = CHKw(q, Show_CMDLIN);
+ qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort);
+#ifdef SORT_SUPRESS
+ }
+#endif
+ // account for column headings
+ (*lscr)++;
+ lwin = 1;
+ i = 0;
+
+ while ( ppt[i]->tid != -1 && *lscr < Max_lines && (!q->winlines || (lwin <= q->winlines)) ) {
+ if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state))
+ && good_uid(ppt[i]) ) {
+ // Display a process Row
+ task_show(q, ppt[i]);
+ (*lscr)++;
+ ++lwin;
+ }
+ ++i;
+ }
+ // for this frame that window's toast, cleanup for next time
+ q->winlines = 0;
+ OFFw(Curwin, FLGSOFF_cwo);
+
+#ifdef SORT_SUPRESS
+#undef srtMASK
+#endif
+}
+
+
+/*###### Entry point plus two ##########################################*/
+
+// This guy's just a *Helper* function who apportions the
+// remaining amount of screen real estate under multiple windows
+static void framehlp (int wix, int max)
+{
+ int i, rsvd, size, wins;
+
+ // calc remaining number of visible windows + total 'user' lines
+ for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) {
+ if (CHKw(&Winstk[i], VISIBLE_tsk)) {
+ rsvd += Winstk[i].rc.maxtasks;
+ ++wins;
+ if (max <= rsvd) break;
+ }
+ }
+ if (!wins) wins = 1;
+ // set aside 'rsvd' & deduct 1 line/window for the columns heading
+ size = (max - wins) - rsvd;
+ if (0 <= size) size = max;
+ size = (max - wins) / wins;
+
+ // for remaining windows, set WIN_t winlines to either the user's
+ // maxtask (1st choice) or our 'foxized' size calculation
+ // (foxized adj. - 'fair and balanced')
+ for (i = wix ; i < GROUPSMAX; i++) {
+ if (CHKw(&Winstk[i], VISIBLE_tsk)) {
+ Winstk[i].winlines =
+ Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size;
+ }
+ }
+}
+
+
+// Initiate the Frame Display Update cycle at someone's whim!
+// This routine doesn't do much, mostly he just calls others.
+//
+// (Whoa, wait a minute, we DO caretake those row guys, plus)
+// (we CALCULATE that IMPORTANT Max_lines thingy so that the)
+// (*subordinate* functions invoked know WHEN the user's had)
+// (ENOUGH already. And at Frame End, it SHOULD be apparent)
+// (WE am d'MAN -- clearing UNUSED screen LINES and ensuring)
+// (the CURSOR is STUCK in just the RIGHT place, know what I)
+// (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say)
+// (THAT about THIS function again, Ok? Good that's better.)
+static void frame_make (void)
+{
+ proc_t **ppt;
+ int i, scrlins;
+
+ // note: all libproc flags are managed by
+ // reframewins(), who also builds each window's column headers
+ if (!Frames_libflags) {
+ reframewins();
+ memset(Pseudo_scrn.buf, '\0', Pseudo_size);
+ }
+ Pseudo_row = Msg_row = scrlins = 0;
+ ppt = summary_show();
+ Max_lines = (Screen_rows - Msg_row) - 1;
+
+ if (CHKw(Curwin, EQUWINS_cwo))
+ wins_reflag(Flags_OFF, EQUWINS_cwo);
+
+ // sure hope each window's columns header begins with a newline...
+ putp(tg2(0, Msg_row));
+
+ if (!Rc.mode_altscr) {
+ // only 1 window to show so, piece o' cake
+ Curwin->winlines = Curwin->rc.maxtasks;
+ window_show(ppt, Curwin, &scrlins);
+ } else {
+ // maybe NO window is visible but assume, pieces o' cakes
+ for (i = 0 ; i < GROUPSMAX; i++) {
+ if (CHKw(&Winstk[i], VISIBLE_tsk)) {
+ framehlp(i, Max_lines - scrlins);
+ window_show(ppt, &Winstk[i], &scrlins);
+ }
+ if (Max_lines <= scrlins) break;
+ }
+ }
+ // clear to end-of-screen (critical if last window is 'idleps off'),
+ // then put the cursor in-its-place, and rid us of any prior frame's msg
+ // (main loop must iterate such that we're always called before sleep)
+ PUTT(
+ "%s%s%s%s",
+ scrlins < Max_lines ? "\n" : "",
+ scrlins < Max_lines ? Cap_clr_eos : "",
+ tg2(0, Msg_row),
+ Cap_clr_eol
+ );
+ fflush(stdout);
+}
+
+static void smartt_openserver(void)
+{
+ struct hostent *hp;
+
+ sock_desc = socket(AF_INET,SOCK_DGRAM,0);
+
+ smartt_server.sin_family = AF_INET;
+ smartt_server.sin_port = (4100);
+
+ hp = gethostbyname("127.0.0.1");
+ bcopy (hp->h_addr,&(smartt_server.sin_addr),hp->h_length);
+}
+
+int main_old (int dont_care_argc, char *argv[])
+{
+
+ (void)dont_care_argc;
+ before(*argv);
+ // +-------------+
+ windows_stage1(); // top (sic) slice
+ configs_read(); // > spread etc, <
+ parse_args(&argv[1]); // > lean stuff, <
+ whack_terminal(); // > onions etc. <
+ windows_stage2(); // as bottom slice
+ // +-------------+
+ signal(SIGALRM, end_pgm);
+ signal(SIGHUP, end_pgm);
+ signal(SIGINT, end_pgm);
+ signal(SIGPIPE, end_pgm);
+ signal(SIGQUIT, end_pgm);
+ signal(SIGTERM, end_pgm);
+ signal(SIGTSTP, suspend);
+ signal(SIGTTIN, suspend);
+ signal(SIGTTOU, suspend);
+ signal(SIGCONT, wins_resize_sighandler);
+ signal(SIGWINCH, wins_resize_sighandler);
+
+ for (;;) {
+ if (need_resize){
+ need_resize = 0;
+ wins_resize();
+ }
+ frame_make();
+
+ if (Msg_awaiting) show_msg(Msg_delayed);
+ if (Loops > 0) --Loops;
+ if (!Loops) end_pgm(0);
+
+ tv.tv_sec = Rc.delay_time;
+ tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000;
+
+ if (Batch) {
+ select(0, NULL, NULL, NULL, &tv); // ought to loop until done
+ } else {
+ long file_flags;
+ int rc;
+ char c;
+ fd_set fs;
+ FD_ZERO(&fs);
+ FD_SET(STDIN_FILENO, &fs);
+ file_flags = fcntl(STDIN_FILENO, F_GETFL);
+ if(file_flags==-1) file_flags=0;
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
+
+ // check 1st, in case tv zeroed (by sig handler) before it got set
+ rc = chin(0, &c, 1);
+ if (rc <= 0) {
+ if (rc == 0) end_pgm(0); /* EOF from terminal, may happen if top
+ * erroneously gets detached from it. */
+ fcntl(STDIN_FILENO, F_SETFL, file_flags);
+ select(1, &fs, NULL, NULL, &tv);
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
+ }
+ if (chin(0, &c, 1) > 0) {
+ fcntl(STDIN_FILENO, F_SETFL, file_flags);
+ do_key((unsigned)c);
+ } else {
+ fcntl(STDIN_FILENO, F_SETFL, file_flags);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int main(int dont_care_argc, char *argv[])
+{
+ smartt_openserver();
+ meminfo();
+
+ mem_info.main_total = kb_main_total;
+ mem_info.main_used = kb_main_used;
+ mem_info.main_free = kb_main_free;
+ mem_info.main_buffers = kb_main_buffers;
+ mem_info.swap_total = kb_swap_total;
+ mem_info.swap_used = kb_swap_used;
+ mem_info.swap_free = kb_swap_free;
+ mem_info.main_cached = kb_main_cached;
+
+ sendto(sock_desc, (void *)&mem_info, sizeof(mem_info),0,(struct sockaddr *) &smartt_server, sizeof(smartt_server));
+}
diff --git a/smartt-top/top.h b/smartt-top/top.h
new file mode 100644
index 0000000..7b3fcf5
--- /dev/null
+++ b/smartt-top/top.h
@@ -0,0 +1,586 @@
+// top.h - Header file: show Linux processes
+//
+// Copyright (c) 2002, by: James C. Warner
+// All rights reserved. 8921 Hilloway Road
+// Eden Prairie, Minnesota 55347 USA
+// <warnerjc@worldnet.att.net>
+//
+// This file may be used subject to the terms and conditions of the
+// GNU Library General Public License Version 2, or any later version
+// at your option, as published by the Free Software Foundation.
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Library General Public License for more details.
+//
+// For their contributions to this program, the author wishes to thank:
+// Albert D. Cahalan, <albert@users.sf.net>
+// Craig Small, <csmall@small.dropbear.id.au>
+//
+// Changes by Albert Cahalan, 2002, 2004.
+
+#ifndef _Itop
+#define _Itop
+
+// Defines intended to be experimented with ------------------------
+//#define CASEUP_HEXES // show any hex values in upper case
+//#define CASEUP_SCALE // show scaled time/num suffix upper case
+//#define CASEUP_SUMMK // show memory summary kilobytes with 'K'
+//#define SORT_SUPRESS // *attempt* to reduce qsort overhead
+//#define WARN_NOT_SMP // restrict '1' & 'I' commands to true smp
+
+// Development/Debugging defines -----------------------------------
+//#define ATEOJ_REPORT // report a bunch of stuff, at end-of-job
+//#define PRETEND2_5_X // pretend we're linux 2.5.x (for IO-wait)
+//#define PRETENDNOCAP // use a terminal without essential caps
+//#define STDOUT_IOLBF // disable our own stdout _IOFBF override
+
+#ifdef PRETEND2_5_X
+#define linux_version_code LINUX_VERSION(2,5,43)
+#endif
+
+/*###### Some Miscellaneous constants ##################################*/
+
+// The default delay twix updates
+#define DEF_DELAY 3.0
+
+// The length of time a 'message' is displayed
+#define MSG_SLEEP 2
+
+// The default value for the 'k' (kill) request
+#define DEF_SIGNAL SIGTERM
+
+// Specific process id monitoring support (command line only)
+#define MONPIDMAX 20
+
+// Power-of-two sizes lead to trouble; the largest power of
+// two factor should be the cache line size. It'll mean the
+// array indexing math gets slower, but cache aliasing is
+// avoided.
+#define CACHE_TWEAK_FACTOR 64
+
+// Miscellaneous buffer sizes with liberal values -- mostly
+// just to pinpoint source code usage/dependancies
+#define SCREENMAX ( 512 + CACHE_TWEAK_FACTOR)
+// the above might seem pretty stingy, until you consider that with every
+// one of top's fields displayed we're talking a 160 byte column header --
+// so that will provide for all fields plus a 350+ byte command line
+#define WINNAMSIZ 4
+#define CAPTABMAX 9
+#define PFLAGSSIZ 32
+#define CAPBUFSIZ 32
+#define CLRBUFSIZ 64
+#define GETBUFSIZ 32
+#define TNYBUFSIZ 32
+#define SMLBUFSIZ ( 256 + CACHE_TWEAK_FACTOR)
+#define OURPATHSZ (1024 + CACHE_TWEAK_FACTOR)
+#define MEDBUFSIZ (1024 + CACHE_TWEAK_FACTOR)
+#define BIGBUFSIZ (2048 + CACHE_TWEAK_FACTOR)
+#define USRNAMSIZ GETBUFSIZ
+#define ROWBUFSIZ SCREENMAX + CLRBUFSIZ
+
+
+/*###### Some Miscellaneous Macro definitions ##########################*/
+
+// Yield table size as 'int'
+#define MAXTBL(t) (int)(sizeof(t) / sizeof(t[0]))
+
+// Used as return arguments in *some* of the sort callbacks
+#define SORT_lt ( Frame_srtflg > 0 ? 1 : -1 )
+#define SORT_gt ( Frame_srtflg > 0 ? -1 : 1 )
+#define SORT_eq 0
+
+// Used to create *most* of the sort callback functions
+#define SCB_NUM1(f,n) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ if ( (*P)->n < (*Q)->n ) return SORT_lt; \
+ if (likely( (*P)->n > (*Q)->n )) return SORT_gt; \
+ return SORT_eq; }
+#define SCB_NUM2(f,n1,n2) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ if ( ((*P)->n1 - (*P)->n2) < ((*Q)->n1 - (*Q)->n2) ) return SORT_lt; \
+ if (likely( ((*P)->n1 - (*P)->n2) > ((*Q)->n1 - (*Q)->n2) )) return SORT_gt; \
+ return SORT_eq; }
+#define SCB_NUMx(f,n) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ return Frame_srtflg * ( (*Q)->n - (*P)->n ); }
+#define SCB_STRx(f,s) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ return Frame_srtflg * strcmp((*Q)->s, (*P)->s); }
+
+// The following two macros are used to 'inline' those portions of the
+// display process requiring formatting, while protecting against any
+// potential embedded 'millesecond delay' escape sequences.
+
+// PUTT - Put to Tty (used in many places)
+// - for temporary, possibly interactive, 'replacement' output
+// - may contain ANY valid terminfo escape sequences
+// - need NOT represent an entire screen row
+#define PUTT(fmt,arg...) do { \
+ char _str[ROWBUFSIZ]; \
+ snprintf(_str, sizeof(_str), fmt, ## arg); \
+ putp(_str); \
+ } while (0)
+
+// PUFF - Put for Frame (used in only 3 places)
+// - for more permanent frame-oriented 'update' output
+// - may NOT contain cursor motion terminfo escapes
+// - assumed to represent a complete screen ROW
+// - subject to optimization, thus MAY be discarded
+
+// The evil version (53892 byte stripped top, oddly enough)
+#define _PUFF(fmt,arg...) \
+do { \
+ char _str[ROWBUFSIZ]; \
+ int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \
+ putp ( Batch ? _str : \
+ ({ \
+ char *restrict const _pse = &Pseudo_scrn.buf[Pseudo_row++ * Pseudo_cols]; \
+ memcmp(_pse, _str, _len) ? memcpy(_pse, _str, _len) : "\n"; \
+ }) \
+ ); \
+} while (0)
+
+// The good version (53876 byte stripped top)
+#define PUFF(fmt,arg...) \
+do { \
+ char _str[ROWBUFSIZ]; \
+ char *_ptr; \
+ int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \
+ if (Batch) _ptr = _str; \
+ else { \
+ if (Pseudo_row * Pseudo_cols + _len > Pseudo_size) { \
+ Pseudo_scrn.buf = realloc(Pseudo_scrn.buf, Pseudo_row * Pseudo_cols + _len); \
+ Pseudo_scrn.mem_size = Pseudo_size = Pseudo_row * Pseudo_cols + _len; \
+ } \
+ _ptr = &Pseudo_scrn.buf[Pseudo_row++ * Pseudo_cols]; \
+ if (memcmp(_ptr, _str, _len)) { \
+ memcpy(_ptr, _str, _len); \
+ } else { \
+ _ptr = "\n"; \
+ } \
+ } \
+ putp(_ptr); \
+} while (0)
+
+
+/*------ Special Macros (debug and/or informative) ---------------------*/
+
+// Orderly end, with any sort of message - see fmtmk
+#define debug_END(s) { \
+ static void std_err (const char *); \
+ fputs(Cap_clr_scr, stdout); \
+ std_err(s); \
+ }
+
+// A poor man's breakpoint, if he's too lazy to learn gdb
+#define its_YOUR_fault { *((char *)0) = '!'; }
+
+
+/*###### Some Typedef's and Enum's #####################################*/
+
+// This typedef just ensures consistent 'process flags' handling
+typedef unsigned FLG_t;
+
+// These typedefs attempt to ensure consistent 'ticks' handling
+typedef unsigned long long TIC_t;
+typedef long long SIC_t;
+
+// Sort support, callback funtion signature
+typedef int (*QFP_t)(const void *, const void *);
+
+// This structure consolidates the information that's used
+// in a variety of display roles.
+typedef struct FLD_t {
+ const char keys [4]; // order: New-on New-off Old-on Old-off
+ // misaligned on 64-bit, but part of a table -- oh well
+ const char *head; // name for col heads + toggle/reorder fields
+ const char *fmts; // sprintf format string for field display
+ const int width; // field width, if applicable
+ const int scale; // scale_num type, if applicable
+ const QFP_t sort; // sort function
+ const char *desc; // description for toggle/reorder fields
+ const int lflg; // PROC_FILLxxx flag(s) needed by this field
+} FLD_t;
+
+// This structure stores one piece of critical 'history'
+// information from one frame to the next -- we don't calc
+// and save data that goes unused
+typedef struct HST_t {
+ TIC_t tics;
+ int pid;
+} HST_t;
+
+// This structure stores a frame's cpu tics used in history
+// calculations. It exists primarily for SMP support but serves
+// all environments.
+typedef struct CPU_t {
+ TIC_t u, n, s, i, w, x, y, z; // as represented in /proc/stat
+ TIC_t u_sav, s_sav, n_sav, i_sav, w_sav, x_sav, y_sav, z_sav; // in the order of our display
+ unsigned id; // the CPU ID number
+} CPU_t;
+
+// These 2 types support rcfile compatibility
+typedef struct RCW_t { // the 'window' portion of an rcfile
+ FLG_t sortindx; // sort field, represented as a procflag
+ int winflags, // 'view', 'show' and 'sort' mode flags
+ maxtasks, // user requested maximum, 0 equals all
+ summclr, // color num used in summ info
+ msgsclr, // " in msgs/pmts
+ headclr, // " in cols head
+ taskclr; // " in task rows
+ char winname [WINNAMSIZ], // window name, user changeable
+ fieldscur [PFLAGSSIZ]; // fields displayed and ordered
+} RCW_t;
+
+typedef struct RCF_t { // the complete rcfile (new style)
+ int mode_altscr; // 'A' - Alt display mode (multi task windows)
+ int mode_irixps; // 'I' - Irix vs. Solaris mode (SMP-only)
+ float delay_time; // 'd' or 's' - How long to sleep twixt updates
+ int win_index; // Curwin, as index
+ RCW_t win [4]; // a 'WIN_t.rc' for each of the 4 windows
+} RCF_t;
+
+typedef struct PSEUDO_SCREEN_t {
+ char *buf;
+ int mem_size;
+} PSEUDO_SCREEN_t;
+
+// The scaling 'type' used with scale_num() -- this is how
+// the passed number is interpreted should scaling be necessary
+enum scale_num {
+ SK_no, SK_Kb, SK_Mb, SK_Gb, SK_Tb
+};
+
+// Flags for each possible field
+enum pflag {
+ P_PID, P_PPD, P_URR, P_UID, P_URE, P_GRP, P_TTY,
+ P_PRI, P_NCE,
+ P_CPN, P_CPU, P_TME, P_TM2,
+ P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR,
+ P_FLT, P_DRT,
+ P_STA, P_CMD, P_WCH, P_FLG
+};
+
+
+///////////////////////////////////////////////////////////////////////////
+// Special Section: multiple windows/field groups -------------
+// (kind of a header within a header: constants, macros & types)
+
+#define GROUPSMAX 4 // the max number of simultaneous windows
+#define GRPNAMSIZ WINNAMSIZ+2 // window's name + number as in: '#:...'
+
+#define Flags_TOG 1 // these are used to direct wins_reflag
+#define Flags_SET 2
+#define Flags_OFF 3
+
+// The Persistent 'Mode' flags!
+// These are preserved in the rc file, as a single integer and the
+// letter shown is the corresponding 'command' toggle
+
+// 'View_' flags affect the summary (minimum), taken from 'Curwin'
+#define View_CPUSUM 0x8000 // '1' - show combined cpu stats (vs. each)
+#define View_LOADAV 0x4000 // 'l' - display load avg and uptime summary
+#define View_STATES 0x2000 // 't' - display task/cpu(s) states summary
+#define View_MEMORY 0x1000 // 'm' - display memory summary
+#define View_NOBOLD 0x0001 // 'B' - disable 'bold' attribute globally
+
+// 'Show_' & 'Qsrt_' flags are for task display in a visible window
+#define Show_THREADS 0x10000 // 'H' - show threads in each task
+#define Show_COLORS 0x0800 // 'z' - show in color (vs. mono)
+#define Show_HIBOLD 0x0400 // 'b' - rows and/or cols bold (vs. reverse)
+#define Show_HICOLS 0x0200 // 'x' - show sort column highlighted
+#define Show_HIROWS 0x0100 // 'y' - show running tasks highlighted
+#define Show_CMDLIN 0x0080 // 'c' - show cmdline vs. name
+#define Show_CTIMES 0x0040 // 'S' - show times as cumulative
+#define Show_IDLEPS 0x0020 // 'i' - show idle processes (all tasks)
+#define Qsrt_NORMAL 0x0010 // 'R' - reversed column sort (high to low)
+
+// these flag(s) have no command as such - they're for internal use
+#define VISIBLE_tsk 0x0008 // tasks are showable when in 'Mode_altscr'
+#define NEWFRAM_cwo 0x0004 // new frame (if anyone cares) - in Curwin
+#define EQUWINS_cwo 0x0002 // rebalance tasks next frame (off 'i'/ 'n')
+ // ...set in Curwin, but impacts all windows
+
+// Current-window-only flags -- always turned off at end-of-window!
+#define FLGSOFF_cwo EQUWINS_cwo | NEWFRAM_cwo
+
+// Default flags if there's no rcfile to provide user customizations
+#define DEF_WINFLGS ( \
+ View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY | View_NOBOLD | \
+ Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Qsrt_NORMAL | \
+ VISIBLE_tsk \
+)
+
+ // Used to test/manipulate the window flags
+#define CHKw(q,f) (int)((q)->rc.winflags & (f))
+#define TOGw(q,f) (q)->rc.winflags ^= (f)
+#define SETw(q,f) (q)->rc.winflags |= (f)
+#define OFFw(q,f) (q)->rc.winflags &= ~(f)
+#define VIZCHKc (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \
+ ? 1 : win_warn()
+#define VIZTOGc(f) (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \
+ ? TOGw(Curwin, f) : win_warn()
+
+// This structure stores configurable information for each window.
+// By expending a little effort in its creation and user requested
+// maintainence, the only real additional per frame cost of having
+// windows is an extra sort -- but that's just on ptrs!
+typedef struct WIN_t {
+ struct WIN_t *next, // next window in window stack
+ *prev; // prior window in window stack
+ char *captab [CAPTABMAX]; // captab needed by show_special
+ int winnum, // window's num (array pos + 1)
+ winlines; // task window's rows (volatile)
+ FLG_t procflags [PFLAGSSIZ]; // fieldscur subset, as enum
+ int maxpflgs, // number of procflags (upcase fieldscur)
+ maxcmdln; // max length of a process' command line
+ int len_rownorm, // lengths of the corresponding terminfo
+ len_rowhigh; // strings to avoid repeated strlen calls
+ RCW_t rc; // stuff that gets saved in the rcfile
+ char capclr_sum [CLRBUFSIZ], // terminfo strings built from
+ capclr_msg [CLRBUFSIZ], // above clrs (& rebuilt too),
+ capclr_pmt [CLRBUFSIZ], // but NO recurring costs !!!
+ capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs
+ capclr_rowhigh [CLRBUFSIZ], // are only used when this
+ capclr_rownorm [CLRBUFSIZ]; // window is the 'Curwin'!
+ char cap_bold [CAPBUFSIZ]; // support for View_NOBOLD toggle
+ char grpname [GRPNAMSIZ], // window number:name, printable
+ columnhdr [SCREENMAX], // column headings for procflags
+ colusrnam [USRNAMSIZ]; // if selected by the 'u' command
+} WIN_t;
+
+struct s_mem_info {
+ unsigned long int main_total;
+ unsigned long int main_used;
+ unsigned long int main_free;
+ unsigned long int main_buffers;
+ unsigned long int main_cached;
+ unsigned long int swap_total;
+ unsigned long int swap_used;
+ unsigned long int swap_free;
+};
+
+/*###### Display Support *Data* ########################################*/
+
+// Configuration files support
+#define SYS_RCFILESPEC "/etc/toprc"
+#define RCF_EYECATCHER "RCfile for "
+#define RCF_DEPRECATED "Id:a, "
+
+// The default fields displayed and their order,
+#define DEF_FIELDS "AEHIOQTWKNMbcdfgjplrsuvyzX"
+// Pre-configured field groupss
+#define JOB_FIELDS "ABcefgjlrstuvyzMKNHIWOPQDX"
+#define MEM_FIELDS "ANOPQRSTUVbcdefgjlmyzWHIKX"
+#define USR_FIELDS "ABDECGfhijlopqrstuvyzMKNWX"
+// Used by fields_sort, placed here for peace-of-mind
+#define NUL_FIELDS "abcdefghijklmnopqrstuvwxyz"
+
+
+// The default values for the local config file
+#define DEF_RCFILE { \
+ 0, 1, DEF_DELAY, 0, { \
+ { P_CPU, DEF_WINFLGS, 0, \
+ COLOR_RED, COLOR_RED, COLOR_YELLOW, COLOR_RED, \
+ "Def", DEF_FIELDS }, \
+ { P_PID, DEF_WINFLGS, 0, \
+ COLOR_CYAN, COLOR_CYAN, COLOR_WHITE, COLOR_CYAN, \
+ "Job", JOB_FIELDS }, \
+ { P_MEM, DEF_WINFLGS, 0, \
+ COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLUE, COLOR_MAGENTA, \
+ "Mem", MEM_FIELDS }, \
+ { P_URE, DEF_WINFLGS, 0, \
+ COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW, \
+ "Usr", USR_FIELDS } \
+ } }
+
+
+// Summary Lines specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define LOADAV_line "%s -%s\n"
+#define LOADAV_line_alt "%s\06 -%s\n"
+#define STATES_line1 "Tasks:\03" \
+ " %3u \02total,\03 %3u \02running,\03 %3u \02sleeping,\03 %3u \02stopped,\03 %3u \02zombie\03\n"
+#define STATES_line2x4 "%s\03" \
+ " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle\03\n"
+#define STATES_line2x5 "%s\03" \
+ " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle,\03 %#5.1f%% \02IO-wait\03\n"
+#define STATES_line2x6 "%s\03" \
+ " %#4.1f%% \02us,\03 %#4.1f%% \02sy,\03 %#4.1f%% \02ni,\03 %#4.1f%% \02id,\03 %#4.1f%% \02wa,\03 %#4.1f%% \02hi,\03 %#4.1f%% \02si\03\n"
+#define STATES_line2x7 "%s\03" \
+ "%#5.1f%%\02us,\03%#5.1f%%\02sy,\03%#5.1f%%\02ni,\03%#5.1f%%\02id,\03%#5.1f%%\02wa,\03%#5.1f%%\02hi,\03%#5.1f%%\02si,\03%#5.1f%%\02st\03\n"
+#ifdef CASEUP_SUMMK
+#define MEMORY_line1 "Mem: \03" \
+ " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02buffers\03\n"
+#define MEMORY_line2 "Swap:\03" \
+ " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02cached\03\n"
+#else
+#define MEMORY_line1 "Mem: \03" \
+ " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02buffers\03\n"
+#define MEMORY_line2 "Swap:\03" \
+ " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02cached\03\n"
+#endif
+
+// Keyboard Help specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define KEYS_help \
+ "Help for Interactive Commands\02 - %s\n" \
+ "Window \01%s\06: \01Cumulative mode \03%s\02. \01System\06: \01Delay \03%.1f secs\02; \01Secure mode \03%s\02.\n" \
+ "\n" \
+ " Z\05,\01B\05 Global: '\01Z\02' change color mappings; '\01B\02' disable/enable bold\n" \
+ " l,t,m Toggle Summaries: '\01l\02' load avg; '\01t\02' task/cpu stats; '\01m\02' mem info\n" \
+ " 1,I Toggle SMP view: '\0011\02' single/separate states; '\01I\02' Irix/Solaris mode\n" \
+ "\n" \
+ " f,o . Fields/Columns: '\01f\02' add or remove; '\01o\02' change display order\n" \
+ " F or O . Select sort field\n" \
+ " <,> . Move sort field: '\01<\02' next col left; '\01>\02' next col right\n" \
+ " R,H . Toggle: '\01R\02' normal/reverse sort; '\01H\02' show threads\n" \
+ " c,i,S . Toggle: '\01c\02' cmd name/line; '\01i\02' idle tasks; '\01S\02' cumulative time\n" \
+ " x\05,\01y\05 . Toggle highlights: '\01x\02' sort field; '\01y\02' running tasks\n" \
+ " z\05,\01b\05 . Toggle: '\01z\02' color/mono; '\01b\02' bold/reverse (only if 'x' or 'y')\n" \
+ " u . Show specific user only\n" \
+ " n or # . Set maximum tasks displayed\n" \
+ "\n" \
+ "%s" \
+ " W Write configuration file\n" \
+ " q Quit\n" \
+ " ( commands shown with '.' require a \01visible\02 task display \01window\02 ) \n" \
+ "Press '\01h\02' or '\01?\02' for help with \01Windows\02,\n" \
+ "any other key to continue " \
+ ""
+
+// This guy goes into the help text (maybe)
+#define KEYS_help_unsecured \
+ " k,r Manipulate tasks: '\01k\02' kill; '\01r\02' renice\n" \
+ " d or s Set update interval\n" \
+ ""
+
+// Fields Reorder/Toggle specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions
+// note: the leading newline below serves really dumb terminals;
+// if there's no 'cursor_home', the screen will be a mess
+// but this part will still be functional.
+#define FIELDS_current \
+ "\n%sCurrent Fields\02: \01 %s \04 for window \01%s\06\n%s " \
+ ""
+
+// Some extra explanatory text which accompanies the Fields display.
+// note: the newlines cannot actually be used, they just serve as
+// substring delimiters for the 'display_fields' routine.
+#define FIELDS_xtra \
+ "Flags field:\n" \
+ " 0x00000001 PF_ALIGNWARN\n" \
+ " 0x00000002 PF_STARTING\n" \
+ " 0x00000004 PF_EXITING\n" \
+ " 0x00000040 PF_FORKNOEXEC\n" \
+ " 0x00000100 PF_SUPERPRIV\n" \
+ " 0x00000200 PF_DUMPCORE\n" \
+ " 0x00000400 PF_SIGNALED\n" \
+ " 0x00000800 PF_MEMALLOC\n" \
+ " 0x00002000 PF_FREE_PAGES (2.5)\n" \
+ " 0x00008000 debug flag (2.5)\n" \
+ " 0x00024000 special threads (2.5)\n" \
+ " 0x001D0000 special states (2.5)\n" \
+ " 0x00100000 PF_USEDFPU (thru 2.4)\n" \
+ ""
+/* no room, sacrificed this one: 'Killed for out-of-memory' */
+/* " 0x00001000 PF_MEMDIE (2.5)\n" ....................... */
+
+// Sort Select specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions
+// note: the leading newline below serves really dumb terminals;
+// if there's no 'cursor_home', the screen will be a mess
+// but this part will still be functional.
+#define SORT_fields \
+ "\n%sCurrent Sort Field\02: \01 %c \04 for window \01%s\06\n%s " \
+ ""
+
+// Some extra explanatory text which accompanies the Sort display.
+// note: the newlines cannot actually be used, they just serve as
+// substring delimiters for the 'display_fields' routine.
+#define SORT_xtra \
+ "Note1:\n" \
+ " If a selected sort field can't be\n" \
+ " shown due to screen width or your\n" \
+ " field order, the '<' and '>' keys\n" \
+ " will be unavailable until a field\n" \
+ " within viewable range is chosen.\n" \
+ "\n" \
+ "Note2:\n" \
+ " Field sorting uses internal values,\n" \
+ " not those in column display. Thus,\n" \
+ " the TTY & WCHAN fields will violate\n" \
+ " strict ASCII collating sequence.\n" \
+ " (shame on you if WCHAN is chosen)\n" \
+ ""
+
+// Colors Help specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define COLOR_help \
+ "Help for color mapping\02 - %s\n" \
+ "current window: \01%s\06\n" \
+ "\n" \
+ " color - 04:25:44 up 8 days, 50 min, 7 users, load average:\n" \
+ " Tasks:\03 64 \02total,\03 2 \03running,\03 62 \02sleeping,\03 0 \02stopped,\03\n" \
+ " Cpu(s):\03 76.5%% \02user,\03 11.2%% \02system,\03 0.0%% \02nice,\03 12.3%% \02idle\03\n" \
+ " \01 Nasty Message! \04 -or- \01Input Prompt\05\n" \
+ " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \
+ " 17284 \10pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bash \10\n" \
+ " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R < color -b \07\n" \
+ " 11005 \10? \07 9 0 0.0 0:02.50 2852 1008 S amor -ses \10\n" \
+ " available toggles: \01B\02 =disable bold globally (\01%s\02),\n" \
+ " \01z\02 =color/mono (\01%s\02), \01b\02 =tasks \"bold\"/reverse (\01%s\02)\n" \
+ "\n" \
+ "Select \01target\02 as upper case letter:\n" \
+ " S\02 = Summary Data,\01 M\02 = Messages/Prompts,\n" \
+ " H\02 = Column Heads,\01 T\02 = Task Information\n" \
+ "Select \01color\02 as number:\n" \
+ " 0\02 = black,\01 1\02 = red, \01 2\02 = green,\01 3\02 = yellow,\n" \
+ " 4\02 = blue, \01 5\02 = magenta,\01 6\02 = cyan, \01 7\02 = white\n" \
+ "\n" \
+ "Selected: \01target\02 \01 %c \04; \01color\02 \01 %d \04\n" \
+ " press 'q' to abort changes to window '\01%s\02'\n" \
+ " press 'a' or 'w' to commit & change another, <Enter> to commit and end " \
+ ""
+
+// Windows/Field Group Help specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define WINDOWS_help \
+ "Help for Windows / Field Groups\02 - \"Current Window\" = \01 %s \06\n" \
+ "\n" \
+ ". Use multiple \01windows\02, each with separate config opts (color,fields,sort,etc)\n" \
+ ". The 'current' window controls the \01Summary Area\02 and responds to your \01Commands\02\n" \
+ " . that window's \01task display\02 can be turned \01Off\02 & \01On\02, growing/shrinking others\n" \
+ " . with \01NO\02 task display, some commands will be \01disabled\02 ('i','R','n','c', etc)\n" \
+ " until a \01different window\02 has been activated, making it the 'current' window\n" \
+ ". You \01change\02 the 'current' window by: \01 1\02) cycling forward/backward;\01 2\02) choosing\n" \
+ " a specific field group; or\01 3\02) exiting the color mapping screen\n" \
+ ". Commands \01available anytime -------------\02\n" \
+ " A . Alternate display mode toggle, show \01Single\02 / \01Multiple\02 windows\n" \
+ " G . Choose another field group and make it 'current', or change now\n" \
+ " by selecting a number from: \01 1\02 =%s;\01 2\02 =%s;\01 3\02 =%s; or\01 4\02 =%s\n" \
+ ". Commands \01requiring\02 '\01A\02' mode\01 -------------\02\n" \
+ " g . Change the \01Name\05 of the 'current' window/field group\n" \
+ " \01*\04 a , w . Cycle through all four windows: '\01a\05' Forward; '\01w\05' Backward\n" \
+ " \01*\04 - , _ . Show/Hide: '\01-\05' \01Current\02 window; '\01_\05' all \01Visible\02/\01Invisible\02\n" \
+ " The screen will be divided evenly between task displays. But you can make\n" \
+ " some \01larger\02 or \01smaller\02, using '\01n\02' and '\01i\02' commands. Then later you could:\n" \
+ " \01*\04 = , + . Rebalance tasks: '\01=\05' \01Current\02 window; '\01+\05' \01Every\02 window\n" \
+ " (this also forces the \01current\02 or \01every\02 window to become visible)\n" \
+ "\n" \
+ "In '\01A\02' mode, '\01*\04' keys are your \01essential\02 commands. Please try the '\01a\02' and '\01w\02'\n" \
+ "commands plus the 'G' sub-commands NOW. Press <Enter> to make 'Current' " \
+ ""
+
+
+/*###### For Piece of mind #############################################*/
+
+ /* just sanity check(s)... */
+#if USRNAMSIZ < GETBUFSIZ
+# error "Jeeze, USRNAMSIZ Must NOT be less than GETBUFSIZ !"
+#endif
+
+
+
+#endif /* _Itop */
diff --git a/smartt-top/uptime.1 b/smartt-top/uptime.1
new file mode 100644
index 0000000..a3da7c7
--- /dev/null
+++ b/smartt-top/uptime.1
@@ -0,0 +1,47 @@
+.\" -*-Nroff-*-
+.\"
+.TH UPTIME 1 "26 Jan 1993" "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+uptime \- Tell how long the system has been running.
+.SH SYNOPSIS
+.B uptime
+.br
+.B uptime
+.RB [ \-V ]
+.SH DESCRIPTION
+.B uptime
+gives a one line display of the following information.
+The current time,
+how long the system has been running,
+how many users are currently logged on,
+and the system load averages for the past 1, 5, and 15 minutes.
+
+This is the same information contained in the header line displayed by
+.BR w (1).
+.sp
+System load averages is the average number of processes that are either
+in a runnable or uninterruptable state. A process in a runnable state is
+either using the CPU or waiting to use the CPU. A process in
+uninterruptable state is waiting for some I/O access, eg waiting for
+disk. The averages are taken over the three time intervals.
+Load averages are not normalized for the number of CPUs in a system, so
+a load average of 1 means a single CPU system is loaded all the time
+while on a 4 CPU system it means it was idle 75% of the time.
+.SH FILES
+.TP
+.I /var/run/utmp
+information about who is currently logged on
+.TP
+.I /proc
+process information
+.SH AUTHORS
+.B uptime
+was written by Larry Greenfield <greenfie@gauss.rutgers.edu> and
+Michael K. Johnson <johnsonm@sunsite.unc.edu>.
+
+Please send bug reports to <albert@users.sf.net>
+.SH "SEE ALSO"
+.BR ps (1),
+.BR top (1),
+.BR utmp (5),
+.BR w (1)
diff --git a/smartt-top/uptime.c b/smartt-top/uptime.c
new file mode 100644
index 0000000..6a4d8bd
--- /dev/null
+++ b/smartt-top/uptime.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <string.h>
+#include "proc/whattime.h"
+#include "proc/version.h"
+
+int main(int argc, char *argv[]) {
+ if(argc == 1) {
+ print_uptime();
+ return 0;
+ }
+ if((argc == 2) && (!strcmp(argv[1], "-V"))) {
+ display_version();
+ return 0;
+ }
+ fprintf(stderr, "usage: uptime [-V]\n -V display version\n");
+ return 1;
+}
diff --git a/smartt-top/v b/smartt-top/v
new file mode 100755
index 0000000..f23230b
--- /dev/null
+++ b/smartt-top/v
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# Wow, using $* causes great pain with embedded spaces in arguments.
+# The "$@" won't break that into 2 arguments.
+#
+LD_LIBRARY_PATH=proc exec ./vmstat "$@"
diff --git a/smartt-top/vmstat.8 b/smartt-top/vmstat.8
new file mode 100644
index 0000000..486224b
--- /dev/null
+++ b/smartt-top/vmstat.8
@@ -0,0 +1,202 @@
+.\" This page Copyright (C) 1994 Henry Ware <al172@yfn.ysu.edu>
+.\" Distributed under the GPL, Copyleft 1994.
+.TH VMSTAT 8 "2009 Jan 9" "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual"
+.SH NAME
+vmstat \- Report virtual memory statistics
+.SH SYNOPSIS
+.ft B
+.B vmstat
+.RB [ "\-a" ]
+.RB [ "\-n" ]
+.RI [ delay " [ " count ]]
+.br
+.B vmstat
+.RB [ "\-f" ]
+.RB [ "\-s" ]
+.RB [ "\-m" ]
+.br
+.B vmstat
+.RB [ "\-S unit"]
+.br
+.B vmstat
+.RB [ "\-d"]
+.br
+.B vmstat
+.RB [ "\-D"]
+.br
+.B vmstat
+.RB [ "\-p disk partition"]
+.br
+.B vmstat
+.RB [ "\-V" ]
+.SH DESCRIPTION
+\fBvmstat\fP reports information about processes, memory, paging,
+block IO, traps, disks and cpu activity.
+
+The first report produced gives averages since the last reboot. Additional
+reports give information on a sampling period of length \fIdelay\fP.
+The process and memory reports are instantaneous in either case.
+
+.SS Options
+The \fB\-a\fP switch displays active/inactive memory, given a 2.5.41 kernel or better.
+.PP
+The \fB\-f\fP switch displays the number of forks since boot.
+This includes the fork, vfork, and clone system calls, and is
+equivalent to the total number of tasks created. Each process
+is represented by one or more tasks, depending on thread usage.
+This display does not repeat.
+.PP
+The \fB\-m\fP displays slabinfo.
+.PP
+The \fB\-n\fP switch causes the header to be displayed only once rather than periodically.
+.PP
+The \fB\-s\fP switch displays a table of various event counters
+and memory statistics. This display does not repeat.
+.PP
+.I delay
+is the delay between updates in seconds. If no delay is specified,
+only one report is printed with the average values since boot.
+.PP
+.I count
+is the number of updates. If no count is specified and delay is
+defined, \fIcount\fP defaults to infinity.
+.PP
+The \fB\-d\fP reports disk statistics (2.5.70 or above required)
+.PP
+The \fB-D\fP reports some summary statistics about disk activity.
+.PP
+The \fB\-p\fP followed by some partition name for detailed statistics (2.5.70 or above required)
+.PP
+The \fB\-S\fP followed by k or K or m or M switches changes the units of
+ouput from bytes to outputs between 1000, 1024, 1000000, or 1048576 bytes. Note this does not change the swap (si/so) or block (bi/bo) fields.
+.PP
+The \fB\-V\fP switch results in displaying version information.
+.PP
+.SH FIELD DESCRIPTION FOR VM MODE
+.SS
+.B "Procs"
+.nf
+r: The number of processes waiting for run time.
+b: The number of processes in uninterruptible sleep.
+.fi
+.PP
+.SS
+.B "Memory"
+.nf
+swpd: the amount of virtual memory used.
+free: the amount of idle memory.
+buff: the amount of memory used as buffers.
+cache: the amount of memory used as cache.
+inact: the amount of inactive memory. (\-a option)
+active: the amount of active memory. (\-a option)
+.fi
+.PP
+.SS
+.B "Swap"
+.nf
+si: Amount of memory swapped in from disk (/s).
+so: Amount of memory swapped to disk (/s).
+.fi
+.PP
+.SS
+.B "IO"
+.nf
+bi: Blocks received from a block device (blocks/s).
+bo: Blocks sent to a block device (blocks/s).
+.fi
+.PP
+.SS
+.B "System"
+.nf
+in: The number of interrupts per second, including the clock.
+cs: The number of context switches per second.
+.if
+.PP
+.SS
+.B "CPU "
+These are percentages of total CPU time.
+.nf
+us: Time spent running non\-kernel code. (user time, including nice time)
+sy: Time spent running kernel code. (system time)
+id: Time spent idle. Prior to Linux 2.5.41, this includes IO\-wait time.
+wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle.
+st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown.
+
+.PP
+.SH FIELD DESCRIPTION FOR DISK MODE
+.SS
+.B "Reads"
+.nf
+total: Total reads completed successfully
+merged: grouped reads (resulting in one I/O)
+sectors: Sectors read successfully
+ms: milliseconds spent reading
+.fi
+.PP
+.SS
+.B "Writes"
+.nf
+total: Total writes completed successfully
+merged: grouped writes (resulting in one I/O)
+sectors: Sectors written successfully
+ms: milliseconds spent writing
+.fi
+.PP
+.SS
+.B "IO"
+.nf
+cur: I/O in progress
+s: seconds spent for I/O
+.fi
+
+.PP
+.SH FIELD DESCRIPTION FOR DISK PARTITION MODE
+.nf
+reads: Total number of reads issued to this partition
+read sectors: Total read sectors for partition
+writes : Total number of writes issued to this partition
+requested writes: Total number of write requests made for partition
+
+.fi
+
+.PP
+.SH FIELD DESCRIPTION FOR SLAB MODE
+.nf
+cache: Cache name
+num: Number of currently active objects
+total: Total number of available objects
+size: Size of each object
+pages: Number of pages with at least one active object
+.fi
+
+.SH NOTES
+.B "vmstat "
+does not require special permissions.
+.PP
+These reports are intended to help identify system bottlenecks. Linux
+.B "vmstat "
+does not count itself as a running process.
+.PP
+All linux blocks are currently 1024 bytes. Old kernels may report
+blocks as 512 bytes, 2048 bytes, or 4096 bytes.
+.PP
+Since procps 3.1.9, vmstat lets you choose units (k, K, m, M) default is K (1024 bytes) in the default mode
+.PP
+vmstat uses slabinfo 1.1 FIXME
+.SH FILES
+.ta
+.nf
+/proc/meminfo
+/proc/stat
+/proc/*/stat
+.fi
+
+.SH "SEE ALSO"
+iostat(1), sar(1), mpstat(1), ps(1), top(1), free(1)
+.PP
+.SH BUGS
+Does not tabulate the block io per device or count the number of system calls.
+.SH AUTHORS
+.nf
+Written by Henry Ware <al172@yfn.ysu.edu>.
+Fabian Fr\('ed\('erick <ffrederick@users.sourceforge.net> (diskstat, slab, partitions...)
diff --git a/smartt-top/vmstat.c b/smartt-top/vmstat.c
new file mode 100644
index 0000000..cbbb094
--- /dev/null
+++ b/smartt-top/vmstat.c
@@ -0,0 +1,677 @@
+// old: "Copyright 1994 by Henry Ware <al172@yfn.ysu.edu>. Copyleft same year."
+// most code copyright 2002 Albert Cahalan
+//
+// 27/05/2003 (Fabian Frederick) : Add unit conversion + interface
+// Export proc/stat access to libproc
+// Adapt vmstat helpfile
+// 31/05/2003 (Fabian) : Add diskstat support (/libproc)
+// June 2003 (Fabian) : -S <x> -s & -s -S <x> patch
+// June 2003 (Fabian) : -Adding diskstat against 3.1.9, slabinfo
+// -patching 'header' in disk & slab
+// July 2003 (Fabian) : -Adding disk partition output
+// -Adding disk table
+// -Syncing help / usage
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/dir.h>
+#include <dirent.h>
+
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+
+#define UNIT_B 1
+#define UNIT_k 1000
+#define UNIT_K 1024
+#define UNIT_m 1000000
+#define UNIT_M 1048576
+
+static unsigned long dataUnit=UNIT_K;
+static char szDataUnit[3] = "K";
+
+#define VMSTAT 0
+#define DISKSTAT 0x00000001
+#define VMSUMSTAT 0x00000002
+#define SLABSTAT 0x00000004
+#define PARTITIONSTAT 0x00000008
+#define DISKSUMSTAT 0x00000010
+
+static int statMode=VMSTAT;
+
+#define FALSE 0
+#define TRUE 1
+
+static int a_option; /* "-a" means "show active/inactive" */
+
+static unsigned sleep_time = 1;
+static unsigned long num_updates;
+
+static unsigned int height; // window height
+static unsigned int moreheaders=TRUE;
+
+
+/////////////////////////////////////////////////////////////////////////
+
+static void usage(void) NORETURN;
+static void usage(void) {
+ fprintf(stderr,"usage: vmstat [-V] [-n] [delay [count]]\n");
+ fprintf(stderr," -V prints version.\n");
+ fprintf(stderr," -n causes the headers not to be reprinted regularly.\n");
+ fprintf(stderr," -a print inactive/active page stats.\n");
+ fprintf(stderr," -d prints disk statistics\n");
+ fprintf(stderr," -D prints disk table\n");
+ fprintf(stderr," -p prints disk partition statistics\n");
+ fprintf(stderr," -s prints vm table\n");
+ fprintf(stderr," -m prints slabinfo\n");
+ fprintf(stderr," -S unit size\n");
+ fprintf(stderr," delay is the delay between updates in seconds. \n");
+ fprintf(stderr," unit size k:1000 K:1024 m:1000000 M:1048576 (default is K)\n");
+ fprintf(stderr," count is the number of updates.\n");
+ exit(EXIT_FAILURE);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if 0
+// produce: " 6 ", "123 ", "123k ", etc.
+static int format_1024(unsigned long long val64, char *restrict dst){
+ unsigned oldval;
+ const char suffix[] = " kmgtpe";
+ unsigned level = 0;
+ unsigned val32;
+
+ if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do
+ val32 = val64;
+ return sprintf(dst,"%3u ",val32);
+ }
+
+ while(val64 > 0xffffffffull){
+ level++;
+ val64 /= 1024;
+ }
+
+ val32 = val64;
+
+ while(val32 > 999){
+ level++;
+ oldval = val32;
+ val32 /= 1024;
+ }
+
+ if(val32 < 10){
+ unsigned fract = (oldval % 1024) * 10 / 1024;
+ return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]);
+ }
+ return sprintf(dst, "%3u%c ", val32, suffix[level]);
+}
+
+
+// produce: " 6 ", "123 ", "123k ", etc.
+static int format_1000(unsigned long long val64, char *restrict dst){
+ unsigned oldval;
+ const char suffix[] = " kmgtpe";
+ unsigned level = 0;
+ unsigned val32;
+
+ if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do
+ val32 = val64;
+ return sprintf(dst,"%3u ",val32);
+ }
+
+ while(val64 > 0xffffffffull){
+ level++;
+ val64 /= 1000;
+ }
+
+ val32 = val64;
+
+ while(val32 > 999){
+ level++;
+ oldval = val32;
+ val32 /= 1000;
+ }
+
+ if(val32 < 10){
+ unsigned fract = (oldval % 1000) / 100;
+ return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]);
+ }
+ return sprintf(dst, "%3u%c ", val32, suffix[level]);
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////
+
+static void new_header(void){
+ printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n");
+ printf(
+ "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s\n",
+ "r","b",
+ "swpd", "free", a_option?"inact":"buff", a_option?"active":"cache",
+ "si","so",
+ "bi","bo",
+ "in","cs",
+ "us","sy","id","wa"
+ );
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static unsigned long unitConvert(unsigned int size){
+ float cvSize;
+ cvSize=(float)size/dataUnit*((statMode==SLABSTAT)?1:1024);
+ return ((unsigned long) cvSize);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void new_format(void) {
+ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n";
+ unsigned int tog=0; /* toggle switch for cleaner code */
+ unsigned int i;
+ unsigned int hz = Hertz;
+ unsigned int running,blocked,dummy_1,dummy_2;
+ jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2], cpu_xxx[2], cpu_yyy[2], cpu_zzz[2];
+ jiff duse, dsys, didl, diow, dstl, Div, divo2;
+ unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
+ unsigned int intr[2], ctxt[2];
+ unsigned int sleep_half;
+ unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul;
+ int debt = 0; // handle idle ticks running backwards
+
+ sleep_half=(sleep_time/2);
+ new_header();
+ meminfo();
+
+ getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl,cpu_iow,cpu_xxx,cpu_yyy,cpu_zzz,
+ pgpgin,pgpgout,pswpin,pswpout,
+ intr,ctxt,
+ &running,&blocked,
+ &dummy_1, &dummy_2);
+
+ duse= *cpu_use + *cpu_nic;
+ dsys= *cpu_sys + *cpu_xxx + *cpu_yyy;
+ didl= *cpu_idl;
+ diow= *cpu_iow;
+ dstl= *cpu_zzz;
+ Div= duse+dsys+didl+diow+dstl;
+ divo2= Div/2UL;
+ printf(format,
+ running, blocked,
+ unitConvert(kb_swap_used), unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+ (unsigned)( (*pswpin * kb_per_page * hz + divo2) / Div ),
+ (unsigned)( (*pswpout * kb_per_page * hz + divo2) / Div ),
+ (unsigned)( (*pgpgin * hz + divo2) / Div ),
+ (unsigned)( (*pgpgout * hz + divo2) / Div ),
+ (unsigned)( (*intr * hz + divo2) / Div ),
+ (unsigned)( (*ctxt * hz + divo2) / Div ),
+ (unsigned)( (100*duse + divo2) / Div ),
+ (unsigned)( (100*dsys + divo2) / Div ),
+ (unsigned)( (100*didl + divo2) / Div ),
+ (unsigned)( (100*diow + divo2) / Div ) /* ,
+ (unsigned)( (100*dstl + divo2) / Div ) */
+ );
+
+ for(i=1;i<num_updates;i++) { /* \\\\\\\\\\\\\\\\\\\\ main loop ////////////////// */
+ sleep(sleep_time);
+ if (moreheaders && ((i%height)==0)) new_header();
+ tog= !tog;
+
+ meminfo();
+
+ getstat(cpu_use+tog,cpu_nic+tog,cpu_sys+tog,cpu_idl+tog,cpu_iow+tog,cpu_xxx+tog,cpu_yyy+tog,cpu_zzz+tog,
+ pgpgin+tog,pgpgout+tog,pswpin+tog,pswpout+tog,
+ intr+tog,ctxt+tog,
+ &running,&blocked,
+ &dummy_1,&dummy_2);
+
+ duse= cpu_use[tog]-cpu_use[!tog] + cpu_nic[tog]-cpu_nic[!tog];
+ dsys= cpu_sys[tog]-cpu_sys[!tog] + cpu_xxx[tog]-cpu_xxx[!tog] + cpu_yyy[tog]-cpu_yyy[!tog];
+ didl= cpu_idl[tog]-cpu_idl[!tog];
+ diow= cpu_iow[tog]-cpu_iow[!tog];
+ dstl= cpu_zzz[tog]-cpu_zzz[!tog];
+
+ /* idle can run backwards for a moment -- kernel "feature" */
+ if(debt){
+ didl = (int)didl + debt;
+ debt = 0;
+ }
+ if( (int)didl < 0 ){
+ debt = (int)didl;
+ didl = 0;
+ }
+
+ Div= duse+dsys+didl+diow+dstl;
+ divo2= Div/2UL;
+ printf(format,
+ running, blocked,
+ unitConvert(kb_swap_used),unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+ (unsigned)( ( (pswpin [tog] - pswpin [!tog])*kb_per_page+sleep_half )/sleep_time ), /*si*/
+ (unsigned)( ( (pswpout[tog] - pswpout[!tog])*kb_per_page+sleep_half )/sleep_time ), /*so*/
+ (unsigned)( ( pgpgin [tog] - pgpgin [!tog] +sleep_half )/sleep_time ), /*bi*/
+ (unsigned)( ( pgpgout[tog] - pgpgout[!tog] +sleep_half )/sleep_time ), /*bo*/
+ (unsigned)( ( intr [tog] - intr [!tog] +sleep_half )/sleep_time ), /*in*/
+ (unsigned)( ( ctxt [tog] - ctxt [!tog] +sleep_half )/sleep_time ), /*cs*/
+ (unsigned)( (100*duse+divo2)/Div ), /*us*/
+ (unsigned)( (100*dsys+divo2)/Div ), /*sy*/
+ (unsigned)( (100*didl+divo2)/Div ), /*id*/
+ (unsigned)( (100*diow+divo2)/Div )/*, //wa
+ (unsigned)( (100*dstl+divo2)/Div ) //st */
+ );
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void diskpartition_header(const char *partition_name){
+ printf("%-10s %10s %10s %10s %10s\n",partition_name, "reads ", "read sectors", "writes ", "requested writes");
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static int diskpartition_format(const char* partition_name){
+ FILE *fDiskstat;
+ struct disk_stat *disks;
+ struct partition_stat *partitions, *current_partition=NULL;
+ unsigned long ndisks, j, k, npartitions;
+ const char format[] = "%20u %10llu %10u %10llu\n";
+
+ fDiskstat=fopen("/proc/diskstats","rb");
+ if(!fDiskstat){
+ fprintf(stderr, "Your kernel doesn't support diskstat. (2.5.70 or above required)\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fclose(fDiskstat);
+ ndisks=getdiskstat(&disks,&partitions);
+ npartitions=getpartitions_num(disks, ndisks);
+ for(k=0; k<npartitions; k++){
+ if(!strcmp(partition_name, partitions[k].partition_name)){
+ current_partition=&(partitions[k]);
+ }
+ }
+ if(!current_partition){
+ return -1;
+ }
+ diskpartition_header(partition_name);
+ printf (format,
+ current_partition->reads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes);
+ fflush(stdout);
+ free(disks);
+ free(partitions);
+ for(j=1; j<num_updates; j++){
+ if (moreheaders && ((j%height)==0)) diskpartition_header(partition_name);
+ sleep(sleep_time);
+ ndisks=getdiskstat(&disks,&partitions);
+ npartitions=getpartitions_num(disks, ndisks);
+ current_partition=NULL;
+ for(k=0; k<npartitions; k++){
+ if(!strcmp(partition_name, partitions[k].partition_name)){
+ current_partition=&(partitions[k]);
+ }
+ }
+ if(!current_partition){
+ return -1;
+ }
+ printf (format,
+ current_partition->reads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes);
+ fflush(stdout);
+ free(disks);
+ free(partitions);
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void diskheader(void){
+ printf("disk- ------------reads------------ ------------writes----------- -----IO------\n");
+
+ printf("%5s %6s %6s %7s %7s %6s %6s %7s %7s %6s %6s\n",
+ " ", "total", "merged","sectors","ms","total","merged","sectors","ms","cur","sec");
+
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void diskformat(void){
+ FILE *fDiskstat;
+ struct disk_stat *disks;
+ struct partition_stat *partitions;
+ unsigned long ndisks,i,j,k;
+ const char format[]="%-5s %6u %6u %7llu %7u %6u %6u %7llu %7u %6u %6u\n";
+ if ((fDiskstat=fopen("/proc/diskstats", "rb"))){
+ fclose(fDiskstat);
+ ndisks=getdiskstat(&disks,&partitions);
+ if (!moreheaders) diskheader();
+ for(k=0; k<ndisks; k++){
+ if (moreheaders && ((k%height)==0)) diskheader();
+ printf(format,
+ disks[k].disk_name,
+ disks[k].reads,
+ disks[k].merged_reads,
+ disks[k].reads_sectors,
+ disks[k].milli_reading,
+ disks[k].writes,
+ disks[k].merged_writes,
+ disks[k].written_sectors,
+ disks[k].milli_writing,
+ disks[k].inprogress_IO?disks[k].inprogress_IO/1000:0,
+ disks[k].milli_spent_IO?disks[k].milli_spent_IO/1000:0/*,
+ disks[i].weighted_milli_spent_IO/1000*/
+ );
+ fflush(stdout);
+ }
+ free(disks);
+ free(partitions);
+ for(j=1; j<num_updates; j++){
+ sleep(sleep_time);
+ ndisks=getdiskstat(&disks,&partitions);
+ for(i=0; i<ndisks; i++,k++){
+ if (moreheaders && ((k%height)==0)) diskheader();
+ printf(format,
+ disks[i].disk_name,
+ disks[i].reads,
+ disks[i].merged_reads,
+ disks[i].reads_sectors,
+ disks[i].milli_reading,
+ disks[i].writes,
+ disks[i].merged_writes,
+ disks[i].written_sectors,
+ disks[i].milli_writing,
+ disks[i].inprogress_IO?disks[i].inprogress_IO/1000:0,
+ disks[i].milli_spent_IO?disks[i].milli_spent_IO/1000:0/*,
+ disks[i].weighted_milli_spent_IO/1000*/
+ );
+ fflush(stdout);
+ }
+ free(disks);
+ free(partitions);
+ }
+ }else{
+ fprintf(stderr, "Your kernel doesn't support diskstat (2.5.70 or above required)\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void slabheader(void){
+ printf("%-24s %6s %6s %6s %6s\n","Cache","Num", "Total", "Size", "Pages");
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void slabformat (void){
+ FILE *fSlab;
+ struct slab_cache *slabs;
+ unsigned long nSlab,i,j,k;
+ const char format[]="%-24s %6u %6u %6u %6u\n";
+
+ fSlab=fopen("/proc/slabinfo", "rb");
+ if(!fSlab){
+ fprintf(stderr, "Your kernel doesn't support slabinfo.\n");
+ return;
+ }
+
+ if (!moreheaders) slabheader();
+ nSlab = getslabinfo(&slabs);
+ for(k=0; k<nSlab; k++){
+ if (moreheaders && ((k%height)==0)) slabheader();
+ printf(format,
+ slabs[k].name,
+ slabs[k].active_objs,
+ slabs[k].num_objs,
+ slabs[k].objsize,
+ slabs[k].objperslab
+ );
+ }
+ free(slabs);
+ for(j=1,k=1; j<num_updates; j++) {
+ sleep(sleep_time);
+ nSlab = getslabinfo(&slabs);
+ for(i=0; i<nSlab; i++,k++){
+ if (moreheaders && ((k%height)==0)) slabheader();
+ printf(format,
+ slabs[i].name,
+ slabs[i].active_objs,
+ slabs[i].num_objs,
+ slabs[i].objsize,
+ slabs[i].objperslab
+ );
+ }
+ free(slabs);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void disksum_format(void) {
+
+ FILE *fDiskstat;
+ struct disk_stat *disks;
+ struct partition_stat *partitions;
+ int ndisks, i;
+ unsigned long reads, merged_reads, read_sectors, milli_reading, writes,
+ merged_writes, written_sectors, milli_writing, inprogress_IO,
+ milli_spent_IO, weighted_milli_spent_IO;
+
+ reads=merged_reads=read_sectors=milli_reading=writes=merged_writes= \
+ written_sectors=milli_writing=inprogress_IO=milli_spent_IO= \
+ weighted_milli_spent_IO=0;
+
+ if ((fDiskstat=fopen("/proc/diskstats", "rb"))){
+ fclose(fDiskstat);
+ ndisks=getdiskstat(&disks, &partitions);
+ printf("%13d disks \n", ndisks);
+ printf("%13d partitions \n", getpartitions_num(disks, ndisks));
+
+ for(i=0; i<ndisks; i++){
+ reads+=disks[i].reads;
+ merged_reads+=disks[i].merged_reads;
+ read_sectors+=disks[i].reads_sectors;
+ milli_reading+=disks[i].milli_reading;
+ writes+=disks[i].writes;
+ merged_writes+=disks[i].merged_writes;
+ written_sectors+=disks[i].written_sectors;
+ milli_writing+=disks[i].milli_writing;
+ inprogress_IO+=disks[i].inprogress_IO?disks[i].inprogress_IO/1000:0;
+ milli_spent_IO+=disks[i].milli_spent_IO?disks[i].milli_spent_IO/1000:0;
+ }
+
+ printf("%13lu total reads\n",reads);
+ printf("%13lu merged reads\n",merged_reads);
+ printf("%13lu read sectors\n",read_sectors);
+ printf("%13lu milli reading\n",milli_reading);
+ printf("%13lu writes\n",writes);
+ printf("%13lu merged writes\n",merged_writes);
+ printf("%13lu written sectors\n",written_sectors);
+ printf("%13lu milli writing\n",milli_writing);
+ printf("%13lu inprogress IO\n",inprogress_IO);
+ printf("%13lu milli spent IO\n",milli_spent_IO);
+
+ free(disks);
+ free(partitions);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void sum_format(void) {
+ unsigned int running, blocked, btime, processes;
+ jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
+ unsigned long pgpgin, pgpgout, pswpin, pswpout;
+ unsigned int intr, ctxt;
+
+ meminfo();
+
+ getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
+ &cpu_iow, &cpu_xxx, &cpu_yyy, &cpu_zzz,
+ &pgpgin, &pgpgout, &pswpin, &pswpout,
+ &intr, &ctxt,
+ &running, &blocked,
+ &btime, &processes);
+
+ printf("%13lu %s total memory\n", unitConvert(kb_main_total),szDataUnit);
+ printf("%13lu %s used memory\n", unitConvert(kb_main_used),szDataUnit);
+ printf("%13lu %s active memory\n", unitConvert(kb_active),szDataUnit);
+ printf("%13lu %s inactive memory\n", unitConvert(kb_inactive),szDataUnit);
+ printf("%13lu %s free memory\n", unitConvert(kb_main_free),szDataUnit);
+ printf("%13lu %s buffer memory\n", unitConvert(kb_main_buffers),szDataUnit);
+ printf("%13lu %s swap cache\n", unitConvert(kb_main_cached),szDataUnit);
+ printf("%13lu %s total swap\n", unitConvert(kb_swap_total),szDataUnit);
+ printf("%13lu %s used swap\n", unitConvert(kb_swap_used),szDataUnit);
+ printf("%13lu %s free swap\n", unitConvert(kb_swap_free),szDataUnit);
+ printf("%13Lu non-nice user cpu ticks\n", cpu_use);
+ printf("%13Lu nice user cpu ticks\n", cpu_nic);
+ printf("%13Lu system cpu ticks\n", cpu_sys);
+ printf("%13Lu idle cpu ticks\n", cpu_idl);
+ printf("%13Lu IO-wait cpu ticks\n", cpu_iow);
+ printf("%13Lu IRQ cpu ticks\n", cpu_xxx);
+ printf("%13Lu softirq cpu ticks\n", cpu_yyy);
+ printf("%13Lu stolen cpu ticks\n", cpu_zzz);
+ printf("%13lu pages paged in\n", pgpgin);
+ printf("%13lu pages paged out\n", pgpgout);
+ printf("%13lu pages swapped in\n", pswpin);
+ printf("%13lu pages swapped out\n", pswpout);
+ printf("%13u interrupts\n", intr);
+ printf("%13u CPU context switches\n", ctxt);
+ printf("%13u boot time\n", btime);
+ printf("%13u forks\n", processes);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void fork_format(void) {
+ unsigned int running, blocked, btime, processes;
+ jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
+ unsigned long pgpgin, pgpgout, pswpin, pswpout;
+ unsigned int intr, ctxt;
+
+ getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
+ &cpu_iow, &cpu_xxx, &cpu_yyy, &cpu_zzz,
+ &pgpgin, &pgpgout, &pswpin, &pswpout,
+ &intr, &ctxt,
+ &running, &blocked,
+ &btime, &processes);
+
+ printf("%13u forks\n", processes);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static int winhi(void) {
+ struct winsize win;
+ int rows = 24;
+
+ if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_row > 0)
+ rows = win.ws_row;
+
+ return rows;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[]) {
+ char *partition = NULL;
+ int c;
+
+ while((c = getopt(argc, argv, "VdafmDnp:S:s")) != EOF) switch(c) {
+ case 'V':
+ display_version();
+ exit(0);
+ case 'd':
+ statMode |= DISKSTAT;
+ break;
+ case 'a':
+ /* active/inactive mode */
+ a_option=1;
+ break;
+ case 'f':
+ // FIXME: check for conflicting args
+ fork_format();
+ exit(0);
+ case 'm':
+ statMode |= SLABSTAT;
+ break;
+ case 'D':
+ statMode |= DISKSUMSTAT;
+ break;
+ case 'n':
+ /* print only one header */
+ moreheaders=FALSE;
+ break;
+ case 'p':
+ statMode |= PARTITIONSTAT;
+ partition = optarg;
+ if (memcmp(partition, "/dev/", 5) == 0) partition += 5;
+ break;
+ case 'S':
+ switch(optarg[0]) {
+ case 'b': case 'B': dataUnit = UNIT_B; break;
+ case 'k': dataUnit = UNIT_k; break;
+ case 'K': dataUnit = UNIT_K; break;
+ case 'm': dataUnit = UNIT_m; break;
+ case 'M': dataUnit = UNIT_M; break;
+ default:
+ fprintf(stderr, "-S requires k, K, m or M (default is kb)\n");
+ exit(EXIT_FAILURE);
+ }
+ szDataUnit[0] = optarg[0];
+ break;
+ case 's':
+ statMode |= VMSUMSTAT;
+ break;
+ default:
+ /* no other aguments defined yet. */
+ usage();
+ }
+
+ if (optind < argc) {
+ if ((sleep_time = atoi(argv[optind++])) == 0)
+ usage();
+ num_updates = ULONG_MAX;
+ }
+ if (optind < argc)
+ num_updates = atol(argv[optind++]);
+ if (optind < argc)
+ usage();
+
+ if (moreheaders) {
+ int tmp=winhi()-3;
+ height=((tmp>0)?tmp:22);
+ }
+ setlinebuf(stdout);
+ switch(statMode){
+ case(VMSTAT): new_format();
+ break;
+ case(VMSUMSTAT): sum_format();
+ break;
+ case(DISKSTAT): diskformat();
+ break;
+ case(PARTITIONSTAT): if(diskpartition_format(partition)==-1)
+ printf("Partition was not found\n");
+ break;
+ case(SLABSTAT): slabformat();
+ break;
+ case(DISKSUMSTAT): disksum_format();
+ break;
+ default: usage();
+ break;
+ }
+ return 0;
+}
+
+
diff --git a/smartt-top/w.1 b/smartt-top/w.1
new file mode 100644
index 0000000..d51e490
--- /dev/null
+++ b/smartt-top/w.1
@@ -0,0 +1,108 @@
+.\" -*-Nroff-*-
+.\"
+.TH W 1 "5 October 2009 " " " "Linux User's Manual"
+.SH NAME
+w \- Show who is logged on and what they are doing.
+.SH SYNOPSIS
+.B w
+.RB [ \-husfVo ]
+.RI [ user ]
+.SH DESCRIPTION
+.B w
+displays information about the users currently on the machine,
+and their processes.
+The header shows, in this order, the current time,
+how long the system has been running,
+how many users are currently logged on,
+and the system load averages for the past 1, 5, and 15 minutes.
+
+The following entries are displayed for each user:
+login name, the tty name, the remote host, login time, idle time, JCPU, PCPU,
+and the command line of their current process.
+
+The JCPU time is the time used by all processes attached to the tty. It
+does not include past background jobs, but does include currently
+running background jobs.
+
+The PCPU time is the time used by the current process, named in the "what"
+field.
+
+.PP
+.SH "COMMAND\-LINE OPTIONS"
+.TP 0.5i
+.B "\-h "
+Don't print the header.
+.TP 0.5i
+.B "\-u "
+Ignores the username while figuring out the current process and cpu
+times. To demonstrate this, do a "su" and do a "w" and a "w \-u".
+.TP 0.5i
+.B "\-s "
+Use the short format.
+Don't print the login time, JCPU or PCPU times.
+.TP 0.5i
+.B "\-f "
+Toggle printing the
+.B from
+(remote hostname) field. The default as
+released is for the
+.B from
+field to not be printed, although your system administrator or
+distribution maintainer may have compiled a version in which the
+.B from
+field is shown by default.
+.TP 0.5i
+.B "\-V "
+Display version information.
+.TP 0.5i
+.B "\-o "
+Old style output. Prints blank space for idle times less than one minute.
+.TP 0.5i
+.B "user "
+Show information about the specified user only.
+
+.SH ENVIRONMENT
+.TP
+PROCPS_USERLEN
+Override the default width of the username column. Defaults to 8.
+.TP
+PROCPS_FROMLEN
+Override the default width of the from column. Defaults to 16.
+
+.SH FILES
+.TP
+.I /var/run/utmp
+information about who is currently logged on
+.TP
+.I /proc
+process information
+.PP
+
+.SH NOTES
+The output for Idle, JCPU and PCPU times vaires depending on if you use
+the \-o (old style) option or not. These formats can be confusing if you
+switch between the old style and standard. In the following paragraphs
+days are DD, hours HH, minutes MM, seconds SS and 100ths of seconds CC.
+
+The standard format is DDdays, HH:MMm, MM:SS or SS.CC if the times are
+greater than 2 days, 1hour, or 1 minute respectively.
+
+For the \-o option, the output will be either DDdays, HH:MM, MM:SSm or
+blank if the times are greater than 2 days, 1 hour or 1 minute
+respectively.
+
+.SH "SEE ALSO"
+.BR free (1),
+.BR ps (1),
+.BR top (1),
+.BR uptime (1),
+.BR utmp (5),
+.BR who (1)
+
+.SH AUTHORS
+.B w
+was re-written almost entirely by Charles Blake, based on the version by Larry
+Greenfield <greenfie@gauss.rutgers.edu> and Michael K. Johnson
+<johnsonm@redhat.com>.
+
+Please send bug reports to <albert@users.sf.net>
diff --git a/smartt-top/w.c b/smartt-top/w.c
new file mode 100644
index 0000000..107b9fc
--- /dev/null
+++ b/smartt-top/w.c
@@ -0,0 +1,344 @@
+/* w - show what logged in users are doing. Almost entirely rewritten from
+ * scratch by Charles Blake circa June 1996. Some vestigal traces of the
+ * original may exist. That was done in 1993 by Larry Greenfield with some
+ * fixes by Michael K. Johnson.
+ *
+ * Changes by Albert Cahalan, 2002.
+ */
+#include "proc/version.h"
+#include "proc/whattime.h"
+#include "proc/readproc.h"
+#include "proc/devname.h"
+#include "proc/procps.h"
+#include "proc/sysinfo.h"
+#include "proc/escape.h"
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <locale.h>
+#include <termios.h>
+
+static int ignoreuser = 0; /* for '-u' */
+static int oldstyle = 0; /* for '-o' */
+static proc_t **procs; /* our snapshot of the process table */
+
+typedef struct utmp utmp_t;
+
+#ifdef W_SHOWFROM
+# define FROM_STRING "on"
+#else
+# define FROM_STRING "off"
+#endif
+
+/* Uh... same thing as UT_NAMESIZE */
+#define USERSZ (sizeof u->ut_user)
+
+/* Arbitary setting, not too big for the screen, max host size */
+#define HOSTSZ 40
+
+
+/* This routine is careful since some programs leave utmp strings
+ * unprintable. Always outputs at least fromlen chars padded with spaces
+ * on the right if necessary.
+ */
+static void print_host(const char *restrict host, int len, const int fromlen) {
+ const char *last;
+ int width = 0;
+
+ if (len > fromlen) len = fromlen;
+ last = host + len;
+ for ( ; host < last ; host++){
+ if (isprint(*host) && *host != ' ') {
+ fputc(*host, stdout);
+ ++width;
+ } else {
+ break;
+ }
+ }
+ // space-fill, and a '-' too if needed to ensure the column exists
+ while(width++ < fromlen)
+ fputc(' ',stdout);
+}
+
+/***** compact 7 char format for time intervals (belongs in libproc?) */
+static void print_time_ival7(time_t t, int centi_sec, FILE* fout) {
+ if((long)t < (long)0){ /* system clock changed? */
+ printf(" ? ");
+ return;
+ }
+ if (oldstyle) {
+ if (t >= 48*60*60) /* > 2 days */
+ fprintf(fout, " %2ludays", t/(24*60*60));
+ else if (t >= 60*60) /* > 1 hour */
+ fprintf(fout, " %2lu:%02u ", t/(60*60), (unsigned) ((t/60)%60));
+ else if (t > 60) /* > 1 minute */
+ fprintf(fout, " %2lu:%02um", t/60, (unsigned) t%60);
+ else
+ fprintf(fout, " ");
+ } else {
+ if (t >= 48*60*60) /* > 2 days */
+ fprintf(fout, " %2ludays", t/(24*60*60));
+ else if (t >= 60*60) /* > 1 hour */
+ fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60));
+ else if (t > 60) /* > 1 minute */
+ fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
+ else
+ fprintf(fout, " %2lu.%02us", t, centi_sec);
+ }
+}
+
+/**** stat the device file to get an idle time */
+static time_t idletime(const char *restrict const tty) {
+ struct stat sbuf;
+ if (stat(tty, &sbuf) != 0)
+ return 0;
+ return time(NULL) - sbuf.st_atime;
+}
+
+/***** 7 character formatted login time */
+static void print_logintime(time_t logt, FILE* fout) {
+ char weekday[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
+ month [][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
+ "Aug", "Sep", "Oct", "Nov", "Dec" };
+ time_t curt;
+ struct tm *logtm, *curtm;
+ int today;
+
+ curt = time(NULL);
+ curtm = localtime(&curt);
+ /* localtime returns a pointer to static memory */
+ today = curtm->tm_yday;
+ logtm = localtime(&logt);
+ if (curt - logt > 12*60*60 && logtm->tm_yday != today) {
+ if (curt - logt > 6*24*60*60)
+ fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon],
+ logtm->tm_year % 100);
+ else
+ fprintf(fout, " %3s%02d ", weekday[logtm->tm_wday], logtm->tm_hour);
+ } else {
+ fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min);
+ }
+}
+
+
+/* This function scans the process table accumulating total cpu times for
+ * any processes "associated" with this login session. It also searches
+ * for the "best" process to report as "(w)hat" the user for that login
+ * session is doing currently. This the essential core of 'w'.
+ */
+static const proc_t *getproc(const utmp_t *restrict const u, const char *restrict const tty, unsigned long long *restrict const jcpu, int *restrict const found_utpid) {
+ int line;
+ proc_t **pptr = procs;
+ const proc_t *best = NULL;
+ const proc_t *secondbest = NULL;
+ unsigned uid = ~0U;
+
+ *found_utpid = 0;
+ if(!ignoreuser){
+ char buf[UT_NAMESIZE+1];
+ struct passwd *passwd_data; /* pointer to static data */
+ strncpy(buf,u->ut_user,UT_NAMESIZE);
+ buf[UT_NAMESIZE] = '\0';
+ passwd_data = getpwnam(buf);
+ if(!passwd_data) return NULL;
+ uid = passwd_data->pw_uid;
+ /* OK to have passwd_data go out of scope here */
+ }
+ line = tty_to_dev(tty);
+ *jcpu = 0;
+ for(; *pptr; pptr++) {
+ const proc_t *restrict const tmp = *pptr;
+ if(unlikely(tmp->tgid == u->ut_pid)) {
+ *found_utpid = 1;
+ best = tmp;
+ }
+ if(tmp->tty != line) continue;
+ (*jcpu) += tmp->utime + tmp->stime;
+ secondbest = tmp;
+ /* same time-logic here as for "best" below */
+ if(! (secondbest && tmp->start_time <= secondbest->start_time) ){
+ secondbest = tmp;
+ }
+ if(!ignoreuser && uid != tmp->euid && uid != tmp->ruid) continue;
+ if(tmp->pgrp != tmp->tpgid) continue;
+ if(best && tmp->start_time <= best->start_time) continue;
+ best = tmp;
+ }
+ return best ? best : secondbest;
+}
+
+
+/***** showinfo */
+static void showinfo(utmp_t *u, int formtype, int maxcmd, int from, const int userlen, const int fromlen) {
+ unsigned long long jcpu;
+ int ut_pid_found;
+ unsigned i;
+ char uname[USERSZ + 1] = "",
+ tty[5 + sizeof u->ut_line + 1] = "/dev/";
+ const proc_t *best;
+
+ for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */
+ if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/'))
+ tty[i+5] = u->ut_line[i];
+ else
+ tty[i+5] = '\0';
+
+ best = getproc(u, tty + 5, &jcpu, &ut_pid_found);
+
+ /* just skip if stale utmp entry (i.e. login proc doesn't exist). If there
+ * is a desire a cmdline flag could be added to optionally show it with a
+ * prefix of (stale) in front of cmd or something like that.
+ */
+ if (!ut_pid_found)
+ return;
+
+ strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */
+ if (formtype) {
+ printf("%-*.*s%-9.8s", userlen+1, userlen, uname, u->ut_line);
+ if (from)
+ print_host(u->ut_host, sizeof u->ut_host, fromlen);
+ print_logintime(u->ut_time, stdout);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+ else
+ print_time_ival7(idletime(tty), 0, stdout);
+ print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout);
+ if (best) {
+ unsigned long long pcpu = best->utime + best->stime;
+ print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout);
+ } else
+ printf(" ? ");
+ } else {
+ printf("%-*.*s%-9.8s", userlen+1, userlen, u->ut_user, u->ut_line);
+ if (from)
+ print_host(u->ut_host, sizeof u->ut_host, fromlen);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+ else
+ print_time_ival7(idletime(tty), 0, stdout);
+ }
+ fputs(" ", stdout);
+ if (likely(best)) {
+ char cmdbuf[512];
+ escape_command(cmdbuf, best, sizeof cmdbuf, &maxcmd, ESC_ARGS);
+ fputs(cmdbuf,stdout);
+ } else {
+ printf("-");
+ }
+ fputc('\n', stdout);
+}
+
+/***** main */
+int main(int argc, char **argv) {
+ char *user = NULL, *p;
+ utmp_t *u;
+ struct winsize win;
+ int header=1, longform=1, from=1, args, maxcmd, ch;
+ int userlen = 8;
+ int fromlen = 16;
+ char *env_var;
+
+#ifndef W_SHOWFROM
+ from = 0;
+#endif
+
+ setlocale(LC_ALL, "");
+ for (args=0; (ch = getopt(argc, argv, "hlusfVo")) != EOF; args++)
+ switch (ch) {
+ case 'h': header = 0; break;
+ case 'l': longform = 1; break;
+ case 's': longform = 0; break;
+ case 'f': from = !from; break;
+ case 'V': display_version(); exit(0);
+ case 'u': ignoreuser = 1; break;
+ case 'o': oldstyle = 1; break;
+ default:
+ printf("usage: w -hlsufV [user]\n"
+ " -h skip header\n"
+ " -l long listing (default)\n"
+ " -s short listing\n"
+ " -u ignore uid of processes\n"
+ " -f toggle FROM field (default %s)\n"
+ " -o old-style output\n"
+ " -V display version\n", FROM_STRING);
+ exit(1);
+ }
+
+ if ((argv[optind]))
+ user = (argv[optind]);
+
+ /* Get user field length from environment */
+ if ( (env_var = getenv("PROCPS_USERLEN")) != NULL) {
+ userlen = atoi(env_var);
+ if (userlen < 8 || userlen > USERSZ) {
+ fprintf(stderr, "User length environment PROCPS_USERLEN must be between 8 and %d, ignoring.\n", USERSZ);
+ userlen=8;
+ }
+ }
+ /* Get from field length from environment */
+ if ( (env_var = getenv("PROCPS_FROMLEN")) != NULL) {
+ fromlen = atoi(env_var);
+ if (fromlen < 8 || fromlen > HOSTSZ) {
+ fprintf(stderr, "From length environment PROCPS_FROMLEN must be between 8 and %d, ignoring.\n", HOSTSZ);
+ fromlen=16;
+ }
+ }
+ if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
+ maxcmd = win.ws_col;
+ else if (p = getenv("COLUMNS"))
+ maxcmd = atoi(p);
+ else
+ maxcmd = 80;
+ if (maxcmd < 71) {
+ fprintf(stderr, "%d column window is too narrow\n", maxcmd);
+ exit(1);
+ }
+ maxcmd -= 21 + userlen + (from ? fromlen : 0) + (longform ? 20 : 0);
+ if (maxcmd < 3)
+ fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
+
+ procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT);
+
+ if (header) { /* print uptime and headers */
+ print_uptime();
+ printf("%-*s TTY ",userlen,"USER");
+ if (from)
+ printf("FROM ");
+ if (longform)
+ printf(" LOGIN@ IDLE JCPU PCPU WHAT\n");
+ else
+ printf(" IDLE WHAT\n");
+ }
+
+ utmpname(UTMP_FILE);
+ setutent();
+ if (user) {
+ for (;;) {
+ u = getutent();
+ if (unlikely(!u)) break;
+ if (u->ut_type != USER_PROCESS) continue;
+ if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from, userlen, fromlen);
+ }
+ } else {
+ for (;;) {
+ u = getutent();
+ if (unlikely(!u)) break;
+ if (u->ut_type != USER_PROCESS) continue;
+ if (*u->ut_user) showinfo(u, longform, maxcmd, from, userlen, fromlen);
+ }
+ }
+ endutent();
+
+ return 0;
+}
diff --git a/smartt-top/watch.1 b/smartt-top/watch.1
new file mode 100644
index 0000000..aecbfeb
--- /dev/null
+++ b/smartt-top/watch.1
@@ -0,0 +1,184 @@
+.TH WATCH 1 "2010 Mar 01" " " "Linux User's Manual"
+.SH NAME
+watch \- execute a program periodically, showing output fullscreen
+.SH SYNOPSIS
+.na
+.B watch
+.RB [ \-bdehpvtx ]
+.RB [ \-n
+.IR seconds ]
+.RB [ \-\-beep ]
+.RB [ \-\-color ]
+.RB [ \-\-differences[=\fIcumulative\fP]]
+.RB [ \-\-errexit ]
+.RB [ \-\-exec ]
+.RB [ \-\-help ]
+.RB [ \-\-interval=\fIseconds\fP]
+.RB [ \-\-no\-title ]
+.RB [ \-\-precise ]
+.RB [ \-\-version ]
+.I command
+.SH DESCRIPTION
+.B watch
+runs
+.I command
+repeatedly, displaying its output and errors (the first screenfull). This
+allows you to
+watch the program output change over time. By default, the program is run
+every 2 seconds; use
+.B \-n
+or
+.B \-\-interval
+to specify a different interval. Normally, this interval is interpreted
+as the amout of time between the completion of one run of
+.I command
+and the beginning of the next run. However, with the
+.I \-p
+or
+.I \-\-precise
+option, you can make
+.BR watch
+attempt to run
+.I command
+every
+.I interval
+seconds. Try it with
+.B ntptime
+and notice how the fractional seconds stays
+(nearly) the same, as opposed to normal mode where they continuously
+increase.
+.PP
+The
+.B \-d
+or
+.B \-\-differences
+flag will highlight the differences between successive updates. Using
+.B \-\-differences=\fIcumulative\fP
+makes highlighting "sticky", presenting a running display of all
+positions that have ever changed. The
+.B \-t
+or
+.B \-\-no\-title
+option turns off the header showing the interval, command, and current
+time at the top of the display, as well as the following blank line. The
+.I \-b
+or
+.I \-\-beep
+option causes the command to beep if it has a non-zero exit.
+.PP
+.B watch
+will normally run until interrupted. If you want
+.B watch
+to exit on an error from the program running use the
+.I \-e
+or
+.I \-\-errexit
+options, which will cause
+.B watch
+to exit if the return value from the program is non-zero.
+.PP
+By default \fBwatch\fR will normally not pass escape characters, however
+if you use the \fI\-\-c\fR or \fI\-\-color\fR option, then
+\fBwatch\fR will interpret ANSI color sequences for the foreground.
+
+.SH NOTE
+Note that
+.I command
+is given to "sh \-c"
+which means that you may need to use extra quoting to get the desired effect.
+You can disable this with the
+.I -x
+or
+.I --exec
+option, which passes the command to exec(2) instead.
+.PP
+Note that POSIX option processing is used (i.e., option processing stops at
+the first non\-option argument). This means that flags after
+.I command
+don't get interpreted by
+.BR watch
+itself.
+.SH EXAMPLES
+.PP
+To watch for mail, you might do
+.IP
+watch \-n 60 from
+.PP
+To watch the contents of a directory change, you could use
+.IP
+watch \-d ls \-l
+.PP
+If you're only interested in files owned by user joe, you might use
+.IP
+watch \-d 'ls \-l | fgrep joe'
+.PP
+To see the effects of quoting, try these out
+.IP
+watch echo $$
+.br
+watch echo '$$'
+.br
+watch echo "'"'$$'"'"
+.PP
+To see the effect of precision time keeping, try adding
+.I \-p
+to
+.IP
+watch \-n 10 sleep 1
+.PP
+You can watch for your administrator to install the latest kernel with
+.IP
+watch uname \-r
+.PP
+(Note that
+.I \-p
+isn't guaranteed to work across reboots, especially in the face of
+.B ntpdate
+or other bootup time-changing mechanisms)
+.SH BUGS
+Upon terminal resize, the screen will not be correctly repainted until the
+next scheduled update. All
+.B \-\-differences
+highlighting is lost on that update as well.
+.PP
+Non-printing characters are stripped from program output. Use "cat -v" as
+part of the command pipeline if you want to see them.
+.PP
+Combining Characters that are supposed to display on the character at the
+last column on the screen may display one column early, or they may not
+display at all.
+.PP
+Combining Characters never count as different in
+.I \-\-differences
+mode. Only the base character counts.
+.PP
+Blank lines directly after a line which ends in the last column do not
+display.
+.PP
+.I \-\-precise
+mode doesn't yet have advanced temporal distortion technology to
+compensate for a
+.I command
+that takes more than
+.I interval
+seconds to execute.
+.B watch
+also can get into a state where it rapid-fires as many executions of
+.I command
+as it can to catch up from a previous executions running longer than
+.I interval
+(for example,
+.B netstat
+taking ages on a DNS lookup).
+.SH AUTHORS
+The original
+.B watch
+was written by Tony Rems <rembo@unisoft.com> in 1991, with mods and
+corrections by Francois Pinard. It was reworked and new features added by
+Mike Coleman <mkc@acm.org> in 1999. The beep, exec, and error handling
+features were added by Morty Abzug <morty@frakir.org> in 2008.
+On a not so dark and stormy morning
+in March of 2003, Anthony DeRobertis <asd@suespammers.org> got sick of
+his watches that should update every minute eventually updating many
+seconds after the minute started, and added microsecond precision.
+Unicode support was added in 2009 by Jarrod Lowe <procps@rrod.net>.
diff --git a/smartt-top/watch.c b/smartt-top/watch.c
new file mode 100644
index 0000000..af7229e
--- /dev/null
+++ b/smartt-top/watch.c
@@ -0,0 +1,619 @@
+/* watch -- execute a program repeatedly, displaying output fullscreen
+ *
+ * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
+ * (with mods and corrections by Francois Pinard).
+ *
+ * Substantially reworked, new features (differences option, SIGWINCH
+ * handling, unlimited command length, long line handling) added Apr 1999 by
+ * Mike Coleman <mkc@acm.org>.
+ *
+ * Changes by Albert Cahalan, 2002-2003.
+ * stderr handling, exec, and beep option added by Morty Abzug, 2008
+ * Unicode Support added by Jarrod Lowe <procps@rrod.net> in 2009.
+ */
+
+#define VERSION "0.3.0"
+
+#include <wchar.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <signal.h>
+#include <ncursesw/ncurses.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <termios.h>
+#include <locale.h>
+#include "proc/procps.h"
+#include <errno.h>
+
+#ifdef FORCE_8BIT
+#undef isprint
+#define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
+#endif
+
+static struct option longopts[] = {
+ {"color", no_argument, 0, 'c' },
+ {"differences", optional_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {"interval", required_argument, 0, 'n'},
+ {"beep", no_argument, 0, 'b'},
+ {"errexit", no_argument, 0, 'e'},
+ {"exec", no_argument, 0, 'x'},
+ {"precise", no_argument, 0, 'p'},
+ {"no-title", no_argument, 0, 't'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+};
+
+static char usage[] =
+ "Usage: %s [-bcdhnptvx] [--beep] [--color] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
+
+static char *progname;
+
+static int curses_started = 0;
+static int height = 24, width = 80;
+static int screen_size_changed = 0;
+static int first_screen = 1;
+static int show_title = 2; // number of lines used, 2 or 0
+static int precise_timekeeping = 0;
+
+#define min(x,y) ((x) > (y) ? (y) : (x))
+#define MAX_ANSIBUF 10
+
+static void init_ansi_colors(void)
+{
+ int i;
+ short ncurses_colors[] = {
+ COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE,
+ COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE };
+
+ for (i=0; i< 8; i++)
+ init_pair(i+1, ncurses_colors[i], -1);
+}
+
+static void set_ansi_attribute(const int attrib)
+{
+ switch (attrib)
+ {
+ case -1:
+ return;
+ case 0:
+ standend();
+ return;
+ case 1:
+ attrset(A_BOLD);
+ return;
+ }
+ if (attrib >= 30 && attrib <= 37) {
+ color_set(attrib-29,NULL);
+ return;
+ }
+}
+
+static void process_ansi(FILE *fp)
+{
+ int i,c, num1, num2;
+ char buf[MAX_ANSIBUF];
+ char *nextnum;
+
+
+ c= getc(fp);
+ if (c != '[') {
+ ungetc(c, fp);
+ return;
+ }
+ for(i=0; i<MAX_ANSIBUF; i++)
+ {
+ c = getc(fp);
+ if (c == 'm') //COLOUR SEQUENCE ENDS in 'm'
+ {
+ buf[i] = '\0';
+ break;
+ }
+ if (c < '0' && c > '9' && c != ';')
+ {
+ while(--i >= 0)
+ ungetc(buf[i],fp);
+ return;
+ }
+ buf[i] = (char)c;
+ }
+ num1 = strtol(buf, &nextnum, 10);
+ if (nextnum != buf && nextnum[0] != '\0')
+ num2 = strtol(nextnum+1, NULL, 10);
+ else
+ num2 = -1;
+ set_ansi_attribute(num1);
+ set_ansi_attribute(num2);
+}
+
+static void do_usage(void) NORETURN;
+static void do_usage(void)
+{
+ fprintf(stderr, usage, progname);
+ exit(1);
+}
+
+static void do_exit(int status) NORETURN;
+static void do_exit(int status)
+{
+ if (curses_started)
+ endwin();
+ exit(status);
+}
+
+/* signal handler */
+static void die(int notused) NORETURN;
+static void die(int notused)
+{
+ (void) notused;
+ do_exit(0);
+}
+
+static void
+winch_handler(int notused)
+{
+ (void) notused;
+ screen_size_changed = 1;
+}
+
+static char env_col_buf[24];
+static char env_row_buf[24];
+static int incoming_cols;
+static int incoming_rows;
+
+static void
+get_terminal_size(void)
+{
+ struct winsize w;
+ if(!incoming_cols){ // have we checked COLUMNS?
+ const char *s = getenv("COLUMNS");
+ incoming_cols = -1;
+ if(s && *s){
+ long t;
+ char *endptr;
+ t = strtol(s, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)666)) incoming_cols = (int)t;
+ width = incoming_cols;
+ snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
+ putenv(env_col_buf);
+ }
+ }
+ if(!incoming_rows){ // have we checked LINES?
+ const char *s = getenv("LINES");
+ incoming_rows = -1;
+ if(s && *s){
+ long t;
+ char *endptr;
+ t = strtol(s, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)666)) incoming_rows = (int)t;
+ height = incoming_rows;
+ snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
+ putenv(env_row_buf);
+ }
+ }
+ if (incoming_cols<0 || incoming_rows<0){
+ if (ioctl(2, TIOCGWINSZ, &w) == 0) {
+ if (incoming_rows<0 && w.ws_row > 0){
+ height = w.ws_row;
+ snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
+ putenv(env_row_buf);
+ }
+ if (incoming_cols<0 && w.ws_col > 0){
+ width = w.ws_col;
+ snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
+ putenv(env_col_buf);
+ }
+ }
+ }
+}
+
+/* get current time in usec */
+typedef unsigned long long watch_usec_t;
+#define USECS_PER_SEC (1000000ull)
+watch_usec_t get_time_usec() {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return USECS_PER_SEC*now.tv_sec + now.tv_usec;
+}
+
+// read a wide character from a popen'd stream
+#define MAX_ENC_BYTES 16
+wint_t my_getwc(FILE *s);
+wint_t my_getwc(FILE *s) {
+ char i[MAX_ENC_BYTES]; //assuming no encoding ever consumes more than 16 bytes
+ int byte = 0;
+ int convert;
+ int x;
+ wchar_t rval;
+ while(1) {
+ i[byte] = getc(s);
+ if (i[byte]==EOF) { return WEOF; }
+ byte++;
+ errno = 0;
+ mbtowc(NULL, NULL, 0);
+ convert = mbtowc(&rval, i, byte);
+ x = errno;
+ if(convert > 0) { return rval; } //legal conversion
+ if(byte == MAX_ENC_BYTES) {
+ while(byte > 1) { ungetc(i[--byte], s); } //at least *try* to fix up
+ errno = -EILSEQ;
+ return WEOF;
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int optc;
+ int option_differences = 0,
+ option_differences_cumulative = 0,
+ option_exec = 0,
+ option_beep = 0,
+ option_color = 0,
+ option_errexit = 0,
+ option_help = 0, option_version = 0;
+ double interval = 2;
+ char *command;
+ wchar_t *wcommand = NULL;
+ char **command_argv;
+ int command_length = 0; /* not including final \0 */
+ int wcommand_columns = 0; /* not including final \0 */
+ int wcommand_characters = 0; /* not including final \0 */
+ watch_usec_t next_loop; /* next loop time in us, used for precise time
+ keeping only */
+ int pipefd[2];
+ int status;
+ pid_t child;
+
+ setlocale(LC_ALL, "");
+ progname = argv[0];
+
+ while ((optc = getopt_long(argc, argv, "+bced::hn:pvtx", longopts, (int *) 0))
+ != EOF) {
+ switch (optc) {
+ case 'b':
+ option_beep = 1;
+ break;
+ case 'c':
+ option_color = 1;
+ break;
+ case 'd':
+ option_differences = 1;
+ if (optarg)
+ option_differences_cumulative = 1;
+ break;
+ case 'e':
+ option_errexit = 1;
+ break;
+ case 'h':
+ option_help = 1;
+ break;
+ case 't':
+ show_title = 0;
+ break;
+ case 'x':
+ option_exec = 1;
+ break;
+ case 'n':
+ {
+ char *str;
+ interval = strtod(optarg, &str);
+ if (!*optarg || *str)
+ do_usage();
+ if(interval < 0.1)
+ interval = 0.1;
+ if(interval > ~0u/1000000)
+ interval = ~0u/1000000;
+ }
+ break;
+ case 'p':
+ precise_timekeeping = 1;
+ break;
+ case 'v':
+ option_version = 1;
+ break;
+ default:
+ do_usage();
+ break;
+ }
+ }
+
+ if (option_version) {
+ fprintf(stderr, "%s\n", VERSION);
+ if (!option_help)
+ exit(0);
+ }
+
+ if (option_help) {
+ fprintf(stderr, usage, progname);
+ fputs(" -b, --beep\t\t\t\tbeep if the command has a non-zero exit\n", stderr);
+ fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
+ fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
+ fputs(" -e, --errexit\t\t\t\texit watch if the command has a non-zero exit\n", stderr);
+ fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
+ fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
+ fputs(" -p, --precise\t\t\t\tprecise timing, ignore command run time\n", stderr);
+ fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
+ fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr);
+ fputs(" -x, --exec\t\t\t\tpass command to exec instead of sh\n", stderr);
+ exit(0);
+ }
+
+ if (optind >= argc)
+ do_usage();
+
+ command_argv=&(argv[optind]); /* save for later */
+
+ command = strdup(argv[optind++]);
+ command_length = strlen(command);
+ for (; optind < argc; optind++) {
+ char *endp;
+ int s = strlen(argv[optind]);
+ command = realloc(command, command_length + s + 2); /* space and \0 */
+ endp = command + command_length;
+ *endp = ' ';
+ memcpy(endp + 1, argv[optind], s);
+ command_length += 1 + s; /* space then string length */
+ command[command_length] = '\0';
+ }
+
+ // convert to wide for printing purposes
+ //mbstowcs(NULL, NULL, 0);
+ wcommand_characters = mbstowcs(NULL, command, 0);
+ if(wcommand_characters < 0) {
+ fprintf(stderr, "Unicode Handling Error\n");
+ exit(1);
+ }
+ wcommand = (wchar_t*)malloc((wcommand_characters+1) * sizeof(wcommand));
+ if(wcommand == NULL) {
+ fprintf(stderr, "Unicode Handling Error (malloc)\n");
+ exit(1);
+ }
+ mbstowcs(wcommand, command, wcommand_characters+1);
+ wcommand_columns = wcswidth(wcommand, -1);
+
+
+
+ get_terminal_size();
+
+ /* Catch keyboard interrupts so we can put tty back in a sane state. */
+ signal(SIGINT, die);
+ signal(SIGTERM, die);
+ signal(SIGHUP, die);
+ signal(SIGWINCH, winch_handler);
+
+ /* Set up tty for curses use. */
+ curses_started = 1;
+ initscr();
+ if (option_color) {
+ if (has_colors()) {
+ start_color();
+ use_default_colors();
+ init_ansi_colors();
+ } else
+ option_color = 0;
+ }
+ nonl();
+ noecho();
+ cbreak();
+
+ if (precise_timekeeping)
+ next_loop = get_time_usec();
+
+ for (;;) {
+ time_t t = time(NULL);
+ char *ts = ctime(&t);
+ int tsl = strlen(ts);
+ char *header;
+ FILE *p;
+ int x, y;
+ int oldeolseen = 1;
+
+ if (screen_size_changed) {
+ get_terminal_size();
+ resizeterm(height, width);
+ clear();
+ /* redrawwin(stdscr); */
+ screen_size_changed = 0;
+ first_screen = 1;
+ }
+
+ if (show_title) {
+ // left justify interval and command,
+ // right justify time, clipping all to fit window width
+
+ int hlen = asprintf(&header, "Every %.1fs: ", interval);
+
+ // the rules:
+ // width < tsl : print nothing
+ // width < tsl + hlen + 1: print ts
+ // width = tsl + hlen + 1: print header, ts
+ // width < tsl + hlen + 4: print header, ..., ts
+ // width < tsl + hlen + wcommand_columns: print header, truncated wcommand, ..., ts
+ // width > "": print header, wcomand, ts
+ // this is slightly different from how it used to be
+ if(width >= tsl) {
+ if(width >= tsl + hlen + 1) {
+ mvaddstr(0, 0, header);
+ if(width >= tsl + hlen + 2) {
+ if(width < tsl + hlen + 4) {
+ mvaddstr(0, width - tsl - 4, "... ");
+ }else{
+ if(width < tsl + hlen + wcommand_columns) {
+ // print truncated
+ int avail_columns = width - tsl - hlen;
+ int using_columns = wcommand_columns;
+ int using_characters = wcommand_characters;
+ while(using_columns > avail_columns - 4) {
+ using_characters--;
+ using_columns = wcswidth(wcommand, using_characters);
+ }
+ mvaddnwstr(0, hlen, wcommand, using_characters);
+ mvaddstr(0, width - tsl - 4, "... ");
+ }else{
+ mvaddwstr(0, hlen, wcommand);
+ }
+ }
+ }
+ }
+ mvaddstr(0, width - tsl + 1, ts);
+ }
+
+ free(header);
+ }
+
+ /* allocate pipes */
+ if (pipe(pipefd)<0) {
+ perror("pipe");
+ do_exit(7);
+ }
+
+ /* flush stdout and stderr, since we're about to do fd stuff */
+ fflush(stdout);
+ fflush(stderr);
+
+ /* fork to prepare to run command */
+ child=fork();
+
+ if (child<0) { /* fork error */
+ perror("fork");
+ do_exit(2);
+ } else if (child==0) { /* in child */
+ close (pipefd[0]); /* child doesn't need read side of pipe */
+ close (1); /* prepare to replace stdout with pipe */
+ if (dup2 (pipefd[1], 1)<0) { /* replace stdout with write side of pipe */
+ perror("dup2");
+ exit(3);
+ }
+ dup2(1, 2); /* stderr should default to stdout */
+
+ if (option_exec) { /* pass command to exec instead of system */
+ if (execvp(command_argv[0], command_argv)==-1) {
+ perror("exec");
+ exit(4);
+ }
+ } else {
+ status=system(command); /* watch manpage promises sh quoting */
+
+ /* propagate command exit status as child exit status */
+ if (!WIFEXITED(status)) { /* child exits nonzero if command does */
+ exit(1);
+ } else {
+ exit(WEXITSTATUS(status));
+ }
+ }
+
+ }
+
+ /* otherwise, we're in parent */
+ close(pipefd[1]); /* close write side of pipe */
+ if ((p=fdopen(pipefd[0], "r"))==NULL) {
+ perror("fdopen");
+ do_exit(5);
+ }
+
+
+ for (y = show_title; y < height; y++) {
+ int eolseen = 0, tabpending = 0;
+ wint_t carry = WEOF;
+ for (x = 0; x < width; x++) {
+ wint_t c = L' ';
+ int attr = 0;
+
+ if (!eolseen) {
+ /* if there is a tab pending, just spit spaces until the
+ next stop instead of reading characters */
+ if (!tabpending)
+ do {
+ if(carry == WEOF) {
+ c = my_getwc(p);
+ }else{
+ c = carry;
+ carry = WEOF;
+ }
+ }while (c != WEOF && !isprint(c) && c<128
+ && wcwidth(c) == 0
+ && c != L'\n'
+ && c != L'\t'
+ && (c != L'\033' || option_color != 1));
+ if (c == L'\033' && option_color == 1) {
+ x--;
+ process_ansi(p);
+ continue;
+ }
+ if (c == L'\n')
+ if (!oldeolseen && x == 0) {
+ x = -1;
+ continue;
+ } else
+ eolseen = 1;
+ else if (c == L'\t')
+ tabpending = 1;
+ if (x==width-1 && wcwidth(c)==2) {
+ y++;
+ x = -1; //process this double-width
+ carry = c; //character on the next line
+ continue; //because it won't fit here
+ }
+ if (c == WEOF || c == L'\n' || c == L'\t')
+ c = L' ';
+ if (tabpending && (((x + 1) % 8) == 0))
+ tabpending = 0;
+ }
+ move(y, x);
+ if (option_differences) {
+ cchar_t oldc;
+ in_wch(&oldc);
+ attr = !first_screen
+ && ((wchar_t)c != oldc.chars[0]
+ ||
+ (option_differences_cumulative
+ && (oldc.attr & A_ATTRIBUTES)));
+ }
+ if (attr)
+ standout();
+ addnwstr((wchar_t*)&c,1);
+ if (attr)
+ standend();
+ if(wcwidth(c) == 0) { x--; }
+ if(wcwidth(c) == 2) { x++; }
+ }
+ oldeolseen = eolseen;
+ }
+
+ fclose(p);
+
+ /* harvest child process and get status, propagated from command */
+ if (waitpid(child, &status, 0)<0) {
+ perror("waitpid");
+ do_exit(8);
+ };
+
+ /* if child process exited in error, beep if option_beep is set */
+ if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
+ if (option_beep) beep();
+ if (option_errexit) do_exit(8);
+ }
+
+ first_screen = 0;
+ refresh();
+ if (precise_timekeeping) {
+ watch_usec_t cur_time = get_time_usec();
+ next_loop += USECS_PER_SEC*interval;
+ if (cur_time < next_loop)
+ usleep(next_loop - cur_time);
+ } else
+ usleep(interval * 1000000);
+ }
+
+ endwin();
+
+ return 0;
+}
diff --git a/smartt-trace/README b/smartt-trace/README
new file mode 100644
index 0000000..07af199
--- /dev/null
+++ b/smartt-trace/README
@@ -0,0 +1,12 @@
+Description: Gstreamer based Media Player displays avg-fps, curr fps, drop rate,
+renderred and dropped frame data at specified interval which is configurable.
+It also display width, height and Bitrate of file. Audio codec and video
+codec used for playback is also displayed. This complete metrics data is also
+sent to smart server for tracing.
+
+Usage: media_player [fps-update-interval] {video_file_path}
+
+ fps-update-interval : time-interval in Millisec to get metrics data
+ video_file_path : absolute path of video file e.g. /home/user/video.mp4
+
+Example: media_player 500 /home/user/video.mp4
diff --git a/smartt-trace/smartt-trace.py b/smartt-trace/smartt-trace.py
new file mode 100644
index 0000000..d133670
--- /dev/null
+++ b/smartt-trace/smartt-trace.py
@@ -0,0 +1,271 @@
+from pylab import *
+import time
+import socket
+import string
+
+server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+server_socket.bind(("", 6000))
+
+fig = figure(1,figsize=(16,12));ion();
+subplots_adjust(hspace = 0.3)
+
+sample_limit = 50;
+sample_no = 0;
+xmin = sample_no;
+xmax = sample_no + sample_limit;
+
+
+cc_axis = subplot(421, autoscale_on=False, xlim=(0,sample_limit), ylim=(-1e8,5e8))
+cc_axis.grid(color='k', linestyle=':', linewidth=1)
+cc_axis.set_title('cpu-cycles')
+cc_axis.axhline(y=1e8, xmin=0, xmax=sample_limit,linewidth=1.5, color='r',linestyle="--")
+
+in_axis = subplot(423, autoscale_on=False, xlim=(0,sample_limit), ylim=(-1e7,1e8))
+in_axis.grid(color='k', linestyle=':', linewidth=1)
+in_axis.set_title('instructions')
+in_axis.axhline(y=5e7, xmin=0, xmax=sample_limit,linewidth=1.5, color='r',linestyle="--")
+
+##bc_axis = subplot(423, autoscale_on=False, xlim=(0,sample_limit), ylim=(-1e7,1e8))
+##bc_axis.grid(color='k', linestyle=':', linewidth=1)
+##bc_axis.set_title('bus-cycles')
+
+cm_axis = subplot(425, autoscale_on=False, xlim=(0,sample_limit), ylim=(-1e8, 5e8))
+cm_axis.grid(color='k', linestyle=':', linewidth=1)
+cm_axis.set_title('cache-misses')
+cm_axis.axhline(y=1e8, xmin=0, xmax=sample_limit,linewidth=1.5, color='r',linestyle="--")
+
+##br_axis = subplot(427, autoscale_on=False, xlim=(0,sample_limit), ylim=(-1e6,2e7))
+##br_axis.grid(color='k', linestyle=':', linewidth=1)
+##br_axis.set_title('branches')
+##br_axis.axhline(y=5e6, xmin=0, xmax=sample_limit,linewidth=1.5, color='r',linestyle="--")
+
+mu_axis = subplot(427, autoscale_on=False, xlim=(0,sample_limit), ylim=(900,1e3))
+mu_axis.grid(color='k', linestyle=':', linewidth=1)
+mu_axis.set_title('mem-usage (k)')
+mu_axis.axhline(y=950, xmin=0, xmax=sample_limit,linewidth=1.5, color='r',linestyle="--")
+
+cf_axis = subplot(422, autoscale_on=False, xlim=(0,sample_limit), ylim=(15,30))
+cf_axis.grid(color='k', linestyle=':', linewidth=1)
+cf_axis.set_title('current-fps')
+cf_axis.axhline(y=20, xmin=0, xmax=sample_limit,linewidth=1.5, color='g',linestyle="--")
+
+af_axis = subplot(424, autoscale_on=False, xlim=(0,sample_limit), ylim=(20,40))
+af_axis.grid(color='k', linestyle=':', linewidth=1)
+af_axis.set_title('average-fps')
+af_axis.axhline(y=25, xmin=0, xmax=sample_limit,linewidth=1.5, color='g',linestyle="--")
+
+re_axis = subplot(426, autoscale_on=False, xlim=(0,sample_limit), ylim=(0,5e3))
+re_axis.grid(color='k', linestyle=':', linewidth=1)
+re_axis.set_title('rendered frames')
+
+dr_axis = subplot(428, autoscale_on=False, xlim=(0,sample_limit), ylim=(0,1e2))
+dr_axis.grid(color='k', linestyle=':', linewidth=1)
+dr_axis.set_title('dropped frames')
+
+cc_x = [0,0]; cc_y = [0,0];
+in_x = [0,0]; in_y = [0,0];
+bc_x = [0,0]; bc_y = [0,0];
+cm_x = [0,0]; cm_y = [0,0];
+br_x = [0,0]; br_y = [0,0];
+mu_x = [0,0]; mu_y = [0,0];
+cf_x = [0,0]; cf_y = [0,0];
+af_x = [0,0]; af_y = [0,0];
+re_x = [0,0]; re_y = [0,0];
+dr_x = [0,0]; dr_y = [0,0];
+
+print"UDPServer Waiting for client on port 6000"
+# close the connection
+
+while (1):
+ stat_table, address = server_socket.recvfrom(256)
+ print stat_table
+ system_data = string.split(stat_table)
+ total_items = len(system_data);
+ item_num = 0;
+ pid = system_data[item_num];
+ item_num +=1;
+ time_stamp = system_data[item_num];
+ item_num +=1;
+
+ while total_items > item_num :
+ if system_data[item_num] == "cc":
+ item_num +=1;
+ cpu_cycles = system_data[item_num]
+ item_num +=1;
+ if system_data[item_num] == "(+)":
+ cpu_cycles_wml = 1;
+ elif system_data[item_num] == "(.)":
+ cpu_cycles_wml = 0;
+ item_num +=1;
+
+ del cc_x[0]; del cc_y[0];
+ cc_x.append(sample_no); cc_y.append(cpu_cycles);
+ if (cpu_cycles_wml == 1):
+ cc_axis.plot(sample_no,cpu_cycles,'ro', markersize=5)
+ else:
+ cc_axis.plot(sample_no,cpu_cycles,'bo', markersize=5)
+ cc_axis.plot(cc_x,cc_y,'c-',linewidth=0.5)
+
+ if system_data[item_num] == "in":
+ item_num +=1;
+ instructions = system_data[item_num]
+ item_num +=1;
+ if system_data[item_num] == "(+)":
+ instructions_wml = 1;
+ elif system_data[item_num] == "(.)":
+ instructions_wml = 0;
+ item_num +=1;
+ del in_x[0]; del in_y[0];
+ in_x.append(sample_no); in_y.append(instructions);
+ if (instructions_wml == 1):
+ in_axis.plot(sample_no , instructions,'ro',markersize=5)
+ else:
+ in_axis.plot(sample_no , instructions,'bo',markersize=5)
+ in_axis.plot(in_x,in_y,'c-',linewidth=0.5)
+
+
+##***************- BUS-CYCLES******************************
+
+## if system_data[item_num] == "bc":
+## item_num +=1;
+## bus_cycles = system_data[item_num]
+## item_num +=1;
+## if system_data[item_num] == "(+)":
+## bus_cycles_wml = 1;
+## elif system_data[item_num] == "(.)":
+## bus_cycles_wml = 0;
+## item_num +=1;
+## print "BUS-CYCLES :", bus_cycles
+## print "BUS-CYCLES-WML :", bus_cycles_wml
+## del bc_x[0]; del bc_y[0];
+## bc_x.append(sample_no); bc_y.append(bus_cycles);
+## if (bus_cycles_wml == 1):
+## bc_axis.plot(sample_no , bus_cycles,'ro-',markersize=5)
+## else:
+## bc_axis.plot(sample_no , bus_cycles,'bo-',markersize=5)
+## bc_axis.plot(bc_x,bc_y,'c-',linewidth=0.5)
+
+
+##*************** CACHE-MISSES ******************************
+
+ if system_data[item_num] == "cm":
+ item_num +=1;
+ cache_misses = system_data[item_num]
+ item_num +=1;
+ if system_data[item_num] == "(+)":
+ cache_misses_wml = 1;
+ elif system_data[item_num] == "(.)":
+ cache_misses_wml = 0;
+ item_num +=1;
+ del cm_x[0]; del cm_y[0];
+ cm_x.append(sample_no); cm_y.append(cache_misses);
+ if (cache_misses_wml == 1):
+ cm_axis.plot(sample_no , cache_misses,'ro',markersize=5)
+ else:
+ cm_axis.plot(sample_no , cache_misses,'bo',markersize=5)
+ cm_axis.plot(cm_x,cm_y,'c-',linewidth=0.5)
+
+
+####*************** BRANCHES ******************************
+##
+## if system_data[item_num] == "br":
+## item_num +=1;
+## branches = system_data[item_num]
+## item_num +=1;
+## if system_data[item_num] == "(+)":
+## branches_wml = 1;
+## elif system_data[item_num] == "(.)":
+## branches_wml = 0;
+## item_num +=1;
+## del br_x[0]; del br_y[0];
+## br_x.append(sample_no); br_y.append(branches);
+## if (branches_wml == 1):
+## br_axis.plot(sample_no , branches,'ro',markersize=5)
+## else:
+## br_axis.plot(sample_no , branches,'bo',markersize=5)
+## br_axis.plot(br_x,br_y,'c-',linewidth=0.5)
+
+##*************** MEM-USAGE ******************************
+
+ if system_data[item_num] == "mu":
+ item_num +=1;
+ mem_usage = system_data[item_num];
+ mem_usage = string._int(mem_usage)/1000;
+ item_num +=1;
+ if system_data[item_num] == "(+)":
+ mem_usage_wml = 1;
+ elif system_data[item_num] == "(.)":
+ mem_usage_wml = 0;
+ item_num +=1;
+ del mu_x[0]; del mu_y[0];
+ mu_x.append(sample_no); mu_y.append(mem_usage);
+ if (mem_usage_wml == 1):
+ mu_axis.plot(sample_no , mem_usage,'ro',markersize=5)
+ else:
+ mu_axis.plot(sample_no , mem_usage,'bo',markersize=5)
+ mu_axis.plot(mu_x,mu_y,'c-',linewidth=0.5)
+
+
+##*************** CURRENT-FPS ******************************
+
+
+ if system_data[item_num] == "cf":
+ item_num +=1;
+ current_fps = system_data[item_num]
+ item_num +=1;
+ del cf_x[0]; del cf_y[0];
+ cf_x.append(sample_no); cf_y.append(current_fps);
+ cf_axis.plot(sample_no ,current_fps,'co',markersize=5)
+ cf_axis.plot(cf_x,cf_y,'m-',linewidth=0.5)
+
+##*************** AVERSGE-FPS ******************************
+
+ if system_data[item_num] == "af":
+ item_num +=1;
+ average_fps = system_data[item_num]
+ item_num +=1;
+ del af_x[0]; del af_y[0];
+ af_x.append(sample_no); af_y.append(average_fps);
+ af_axis.plot(sample_no , average_fps,'co',markersize=5)
+ af_axis.plot(af_x,af_y,'m-',linewidth=0.5)
+
+##*************** RENDERED ******************************
+
+ if system_data[item_num] == "re":
+ item_num +=1;
+ rendered = system_data[item_num]
+ item_num +=1;
+ del re_x[0]; del re_y[0];
+ re_x.append(sample_no); re_y.append(rendered);
+ re_axis.plot(sample_no , rendered,'co',markersize=5)
+ re_axis.plot(re_x,re_y,'m-',linewidth=0.5)
+
+##*************** DROPPED ******************************
+
+ if system_data[item_num] == "dr":
+ item_num +=1;
+ dropped = system_data[item_num]
+ item_num +=1;
+ del dr_x[0]; del dr_y[0];
+ dr_x.append(sample_no); dr_y.append(dropped);
+ dr_axis.plot(sample_no , dropped,'co',markersize=5)
+ dr_axis.plot(dr_x,dr_y,'m-',linewidth=0.5)
+
+ hold(1);
+ draw();
+ sample_no +=1;
+ if sample_no > xmax:
+ xmin = sample_no;
+ xmax = sample_no + sample_limit - 1;
+ cc_axis.set_xlim(xmin, xmax)
+ in_axis.set_xlim(xmin, xmax)
+ cm_axis.set_xlim(xmin, xmax)
+## br_axis.set_xlim(xmin, xmax)
+ mu_axis.set_xlim(xmin, xmax)
+ cf_axis.set_xlim(xmin, xmax)
+ af_axis.set_xlim(xmin, xmax)
+ re_axis.set_xlim(xmin, xmax)
+ dr_axis.set_xlim(xmin, xmax)
+
+server_socket.close()
+