[powerpc]oprofileの実装を追う

2012/07/25Linux::powerpcimport
oprofileがなぜ存在するのか。
ftraceとは何が異なるのかを確認するため、実装を追いかけた。
アーキテクチャ依存する部位も含むため、大枠はこれで理解するといいかもしれない。
QorIQをターゲットとして捜査したので、ほかのpower architectureは各自で追いかけていただきたい。

参考:
http://ssvb.github.com/2011/08/23/yet-another-oprofile-tutorial.html

参照したファイル類

linux-2.6/drivers/oprofile/oprof.c
linux-2.6/drivers/oprofile/oprofile_stats.c
linux-2.6/drivers/oprofile/oprofile_files.c
linux-2.6/drivers/oprofile/oprofile_stats.c
linux-2.6/drivers/oprofile/event_buffer.c
linux-2.6/drivers/oprofile/buffer_sync.c
linux-2.6/drivers/oprofile/cpu_buffer.c
linux-2.6/include/linux/ring_buffer.h
linux-2.6/kernel/trace/ring_buffer_benchmark.c
linux-2.6/kernel/trace/ring_buffer.c
linux-2.6/arch/powerpc/oprofile/common.c
linux-2.6/arch/powerpc/kernel/head_booke.h
linux-2.6/arch/powerpc/kernel/head_fsl_booke.S
linux-2.6/arch/powerpc/kernel/cpu_setup_fsl_booke.S
linux-2.6/arch/powerpc/kernel/traps.c
linux-2.6/arch/powerpc/kernel/pmc.c

platform initialization

linux-2.6/arch/powerpc/kernel/cpu_setup_fsl_booke.S

_GLOBAL(__setup_cpu_e500v1)
_GLOBAL(__setup_cpu_e500v2)
	mflr	r4
	bl	__e500_icache_setup
	bl	__e500_dcache_setup
	bl	__setup_e500_ivors
	mtlr	r4
	blr

FILE: linux-2.6/arch/powerpc/kernel/cpu_setup_fsl_booke.S

"PerformanceMonitor"へ飛ぶようにセットしてる。
/* Adjust or setup IVORs for e500v1/v2 */
_GLOBAL(__setup_e500_ivors)
	li	r3,DebugCrit@l
	mtspr	SPRN_IVOR15,r3
	li	r3,SPEUnavailable@l
	mtspr	SPRN_IVOR32,r3
	li	r3,SPEFloatingPointData@l
	mtspr	SPRN_IVOR33,r3
	li	r3,SPEFloatingPointRound@l
	mtspr	SPRN_IVOR34,r3
	li	r3,PerformanceMonitor@l
	mtspr	SPRN_IVOR35,r3
	sync
	blr
	/* Performance Monitor */
	EXCEPTION(0x2060, PerformanceMonitor, performance_monitor_exception, EXC_XFER_STD)

linux-2.6/arch/powerpc/kernel/traps.c

PerformanceMonitor()を用意、
void performance_monitor_exception(struct pt_regs *regs)
{
	perf_irq(regs);
}

linux-2.6/arch/powerpc/kernel/pmc.c

perf_irq() が定義されている。以下の関数で、ユーザ定義?の関数に差し替えることができる。
oprofile driverで差し替えとる。
int reserve_pmc_hardware(perf_irq_t new_perf_irq)

linux-2.6/arch/powerpc/oprofile/common.c

static void op_handle_interrupt(struct pt_regs *regs)
{
	model->handle_interrupt(regs, ctr);
}
初期化処理で、ハンドルを奪う。
static int op_powerpc_setup(void)
{
	/* Grab the hardware */
	err = reserve_pmc_hardware(op_handle_interrupt);
driver登録時点で、以下の初期化が行われている。
int __init oprofile_arch_init(struct oprofile_operations *ops)

		case PPC_OPROFILE_FSL_EMB:
			model = &op_model_fsl_emb;


linux-2.6/arch/powerpc/oprofile/op_model_fsl_emb.c

static void fsl_emb_handle_interrupt(struct pt_regs *regs,
				    struct op_counter_config *ctr)
oprofile_add_ext_sample()でイベント登録。
イベントがなければぬける。
うっひょ-

linux-2.6/drivers/oprofile/cpu_buffer.c

カウントはこちらで行う。
static inline void
__oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
			  unsigned long event, int is_kernel)
{
	struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
	unsigned long backtrace = oprofile_backtrace_depth;

	/*
	 * if log_sample() fail we can't backtrace since we lost the
	 * source of this event
	 */
	if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event))
		/* failed */
		return;

	if (!backtrace)
		return;

	oprofile_begin_trace(cpu_buf);
	oprofile_ops.backtrace(regs, backtrace);
	oprofile_end_trace(cpu_buf);
}

void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
			     unsigned long event, int is_kernel)
{
	__oprofile_add_ext_sample(pc, regs, event, is_kernel);
}

集計は?

ユーザ空間でoprofiledとかctrlが間接的に呼び出す。
上記kernel空間で取得したPC/backtraceデータを用いて、どの区間を何ステップ走行したか、で
実行時間を推定している。
集計結果だけを見ているようでは、厳密な測定結果とはいえないだろう。
ざっくりとみる分には、それでもいいかもしれないですね。
profile取得のオーバヘッドと、サンプリング周期と測定対象の解像度とを十分に考慮してくださいね。

sirq/hoghogeってなに?

2012/07/05Linux::taskimport

"sirq-XXX"とは?

コレ参考になる。筑波大学の講義か。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2011/2012-02-14/

要約すると??

優先度の高いtaskで、driverのbottom halfやprotocol stackの処理を行う。
kernel threadで、SMPならcoreごとに名前が異なる。("ksoftirqd/0","ksoftirqd/1"とか)

ソースコードを追う

FILE: kernel/softirq.c

static const char *softirq_names [] =
{
  [HI_SOFTIRQ]		= "high",
  [SCHED_SOFTIRQ]	= "sched",
  [TIMER_SOFTIRQ]	= "timer",
  [NET_TX_SOFTIRQ]	= "net-tx",
  [NET_RX_SOFTIRQ]	= "net-rx",
  [BLOCK_SOFTIRQ]	= "block",
  [BLOCK_IOPOLL_SOFTIRQ]= "block-iopoll",
  [TASKLET_SOFTIRQ]	= "tasklet",
  [HRTIMER_SOFTIRQ]	= "hrtimer",
  [RCU_SOFTIRQ]		= "rcu",
};


static struct notifier_block __cpuinitdata cpu_nfb = {
	.notifier_call = cpu_callback
};

static __init int spawn_ksoftirqd(void)
{
	void *cpu = (void *)(long)smp_processor_id();
	int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);

	BUG_ON(err == NOTIFY_BAD);
	cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
	register_cpu_notifier(&cpu_nfb);
	return 0;
}
early_initcall(spawn_ksoftirqd);
展開すると以下イメージで処理される。
cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
		for (i = 0; i < NR_SOFTIRQS; i++) {
			per_cpu(ksoftirqd, hotcpu)[i].nr = i;
			per_cpu(ksoftirqd, hotcpu)[i].cpu = hotcpu;
			per_cpu(ksoftirqd, hotcpu)[i].tsk = NULL;
		}
		for (i = 0; i < NR_SOFTIRQS; i++) {
			p = kthread_create(run_ksoftirqd,
					   &per_cpu(ksoftirqd, hotcpu)[i],
					   "sirq-%s/%d", softirq_names[i],
					   hotcpu);
			if (IS_ERR(p)) {
				printk("ksoftirqd %d for %i failed\n", i,
				       hotcpu);
				return NOTIFY_BAD;
			}
			kthread_bind(p, hotcpu);
			per_cpu(ksoftirqd, hotcpu)[i].tsk = p;
		}
cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
		for (i = 0; i < NR_SOFTIRQS; i++) {
			param.sched_priority = MAX_RT_PRIO-1;
			p = per_cpu(ksoftirqd, hotcpu)[i].tsk;
			sched_setscheduler(p, SCHED_FIFO, &param);
			per_cpu(ksoftirqd, hotcpu)[i].tsk = NULL;
			kthread_stop(p);
		}
		takeover_tasklets(hotcpu);

2012/04/12(木)PowerPC固有

"SPE use in kernel()"メッセージが出てくる

kernel configurationで、SPEを有効にしていない。
無効命令ハンドラに飛んできて、あきまへんと云ってくる。

IPには命令のアドレスが入っている模様。場所の特定が可能だろう。
∵emulateしたあと+4しているので。

http://lxr.free-electrons.com/source/arch/powerpc/kernel/traps.c?v=2.6.33;a=powerpc
http://lxr.free-electrons.com/source/arch/powerpc/kernel/process.c?v=2.6.33;a=powerpc#L198
http://lxr.free-electrons.com/source/arch/powerpc/kernel/head_fsl_booke.S?a=powerpc
http://lxr.free-electrons.com/source/arch/powerpc/kernel/entry_32.S

BookE系であたりました。致命傷ではないですが、気持ち悪いです。
直しておきましょう。(使わない、といいながら使ってるやんけ、という状態だったはず)

loaderの挙動

2011/11/20Linux::ldimport

ELFセクションについて..

TEXTREL : .text内の再配置(コード書き換え)を行う. static linkerと同じことをやるってことか...

用語/単語?

もっと詳しく調べないといけない。とりあえず暫定.. PIC : Position Independent Code *.sだして, call function名 を探してみるといい. @PLTつきの呼び出しがあるくさい. (relaxと同じような効果か?)


トレース

ld-linux.so がELFファイルのローダ本体. [Mr.Dan Williams:http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html]曰く、[ここ:http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html]でld-linux.soのstartup部分について解説されています。

So, the files included as part of the librtld.os object are the things included in dl-allobjs.os, rtld-libc.a, as well as libgcc.so. Note the arguments -( and -) which are sent to the linker with the -Wl, prefix. Those arguments tell the linker to iterate over the archives until a steady state is reached. dl-allobjs.os and rtld-libc.a contain the object files for the shared library, along with the entry point _start. _start is defined in rtld.c, from a macro (RTLD_START) included in the header file dl-machine.h. The macro expands into inlined assembly which define the _start symbol, and calls the function _dl_start. Since ld-linux.so.2 has a _start symbol, it is able to to be run directly.

man pageを見てみると...

概要
ダイナミックリンカは、いくつか動的にリンクされたプログラムやライブラリを実行することによって
間接的に実行、
(ダイナミックリンカへのコマンドラインオプションを渡すことができないことと、
 ELFの場合には、プログラム中のセクション".interp"に保存されている動的リンカが実行されます)
 または、直接プログラムが実行される。

説明
プログラムld.soとld-linux.so*は、プログラムの実行準備し、プログラムに必要な共有ライブラリと実行するプログラムを見つけ出し、ロードし、それを実行する。
Linuxバイナリは、コンパイル時にld(1)に対して"-static"オプションを与えない限り、dunamic linking(実行時にリンクする)を要求する。
ld.soは、ずいぶん昔に使われたフォーマットのa.outバイナリをハンドリングする。
ld-linux.so*は、近年皆が使っているELFフォーマットをハンドリングする。(/lib/ld-linux.so.1 for libc5, /lib/ld-linux.so.2 for glibc2), 
どちらも同じ挙動で、同じサポートファイル(/etc/ld.so.conf)、サポートプログラム(ldd(1),ldconfig(8))を使います。

プログラムに必要なshared librariesは、以下の順に検索されます。
1. (ELF形式のみ) もしあれば、バイナリのdynamic section属性の付いたDT_RPATH内に与えられたディレクトリを使い、
	DT_RUNPATH属性が無い場合。(Use of DT_RPATH is deprecated)
2.環境変数 LD_LIBRARY_PATH を使う。ただし、set-user-ID/set-group-ID がセットされたバイナリは除く。
3.default pathである、"/lib", "/usr/lib"を探す。
ただし、リンカオプション"-z nodeflib"をつけていた場合はスキップされる。

ld.so understands the string $ORIGIN (or equivalently ${ORIGIN}) in an rpath specification
 (DT_RPATH or DT_RUNPATH) to mean the directory containing the application executable.
ld.soは、DT_RPATH or DT_RUNPATHといったrpath仕様で"$ORIGIN"または同等の"${ORIGIN}"文字列を理解する。
実行可能なアプリケーションを含むディレクトリを意味する

Thus, an application located in somedir/app could be compiled with gcc -Wl,-rpath,'$ORIGIN/../lib'
 so that it finds an associated shared library in somedir/lib no matter
  where somedir is located in the directory hierarchy.
somedir/appにおかれたアプリケーションは、gccの"-Wl,-rpath,'$ORIGIN/../lib'"オプションでコンパイルできる。
ディレクトリ階層内のsomdirに。..?


This facilitates the creation of "turn-key" applications
 that do not need to be installed into special directories,
  but can instead be unpacked into any directory and still find their own shared libraries.
これは特別なディレクトリにインストールする必要がない"ターンキー"アプリケーションの作成 &#8203;&#8203;が容易ではなく、
任意のディレクトリに展開されると、まだ自分自身の共有ライブラリを見つけることができます。 
???


迷ったらソース読みましょう

glibc-2.11/elf/Makefile

# ld.so uses those routines, plus some special stuff for being the program
# interpreter and operating independent of libc.
rtld-routines   := rtld $(dl-routines) dl-sysdep dl-environ dl-minimal
all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
include ../Rules

check-abi: check-abi-ld
update-abi: update-abi-ld

ifeq (yes,$(build-shared))
# Make sure these things are built in the `make lib' pass so they can be used
# to run programs during the `make others' pass.
lib-noranlib: $(objpfx)$(rtld-installed-name) \
              $(addprefix $(objpfx),$(extra-objs))
endif

# Command to link into a larger single relocatable object.
reloc-link = $(LINK.o) -nostdlib -nostartfiles -r

$(objpfx)dl-allobjs.os: $(all-rtld-routines:%=$(objpfx)%.os)
        $(reloc-link) -o $@ $^

# Link together the dynamic linker into a single relocatable object.
# First we do a link against libc_pic.a just to get a link map,
# and discard the object produced by that link.  From the link map
# we can glean all the libc modules that need to go into the dynamic
# linker.  Then we do a recursive make that goes into all the subdirs
# those modules come from and builds special rtld-foo.os versions that
# are compiled with special flags, and puts these modules into rtld-libc.a
# for us.  Then we do the real link using rtld-libc.a instead of libc_pic.a.

$(objpfx)librtld.map: $(objpfx)dl-allobjs.os $(common-objpfx)libc_pic.a
        @-rm -f $@T
        $(reloc-link) -o $@.o '-Wl,-(' $^ -lgcc '-Wl,-)' -Wl,-Map,$@T
        rm -f $@.o
        mv -f $@T $@

$(objpfx)librtld.mk: $(objpfx)librtld.map Makefile
        LC_ALL=C \
        sed -n 's@^$(common-objpfx)\([^(]*\)(\([^)]*\.os\)) *.*$$@\1 \2@p' \
            $< | \
        while read lib file; do \
          case $$lib in \
          libc_pic.a) \
            LC_ALL=C fgrep -l /$$file \
                  $(common-objpfx)stamp.os $(common-objpfx)*/stamp.os | \
            LC_ALL=C \
            sed 's@^$(common-objpfx)\([^/]*\)/stamp\.os$$@rtld-\1'" +=$$file@"\
            ;; \
          */*.a) \
            echo rtld-$${lib%%/*} += $$file ;; \
          *) echo "Wasn't expecting $$lib($$file)" >&2; exit 1 ;; \
          esac; \
        done > $@T
        echo rtld-subdirs = `LC_ALL=C sed 's/^rtld-\([^ ]*\).*$$/\1/' $@T \
                             | LC_ALL=C sort -u` >> $@T
        mv -f $@T $@

$(objpfx)rtld-libc.a: $(objpfx)librtld.mk FORCE
        $(MAKE) -f $< -f rtld-Rules

$(objpfx)librtld.os: $(objpfx)dl-allobjs.os $(objpfx)rtld-libc.a
        $(LINK.o) -nostdlib -nostartfiles -r -o $@ '-Wl,-(' $^ -lgcc '-Wl,-)' \
                  -Wl,-Map,$@.map

generated += librtld.map librtld.mk rtld-libc.a librtld.os.map

z-now-yes = -Wl,-z,now

$(objpfx)ld.so: $(objpfx)librtld.os $(ld-map)
        @rm -f $@.lds
        $(LINK.o) -nostdlib -nostartfiles -shared $(z-now-$(bind-now))  \
                  $(LDFLAGS-rtld) -Wl,-z,defs -Wl,--verbose 2>&1 |      \
                  LC_ALL=C \
                  sed -e '/^=========/,/^=========/!d;/^=========/d'    \
                      -e 's/\. = .* + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' \
                  > $@.lds
        $(LINK.o) -nostdlib -nostartfiles -shared -o $@                 \
                  $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now))      \
                  $(filter-out $(map-file),$^) $(load-map-file)         \
                  -Wl,-soname=$(rtld-installed-name) -T $@.lds
        rm -f $@.lds
        readelf -s $@ \
          | $(AWK) '($$7 ~ /^UND(|EF)$$/ && $$1 != "0:" && $$4 != "REGISTER") { print; p=1 } END { exit p != 0 }'

# interp.c exists just to get this string into the libraries.
CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' \
                  -DNOT_IN_libc=1
$(objpfx)interp.os: $(common-objpfx)config.make

ifneq (ld.so,$(rtld-installed-name))
# Make sure ld.so.1 exists in the build directory so we can link
# against it.
$(objpfx)$(rtld-installed-name): $(objpfx)ld.so
        rm -f $@
        ln -s $(<F) $@
generated += $(rtld-installed-name)
endif

# Build a file mentioning all trustworthy directories to look for shared
# libraries when using LD_LIBRARY_PATH in a setuid program.  The user can
# add directories to the list by defining $(user-defined-trusted-dirs)
# before starting make.
$(objpfx)trusted-dirs.h: $(objpfx)trusted-dirs.st; @:
$(objpfx)trusted-dirs.st: Makefile $(..)Makeconfig
        $(make-target-directory)
        echo "$(subst :, ,$(default-rpath) $(user-defined-trusted-dirs))"    \
        | $(AWK) -f gen-trusted-dirs.awk > ${@:st=T};
        echo '#define DL_DST_LIB "$(notdir $(slibdir))"' >> ${@:st=T}
        $(move-if-change) ${@:st=T} ${@:st=h}
        touch $@
CPPFLAGS-dl-load.c = -I$(objpfx). -I$(csu-objpfx).

ifeq (yes,$(build-shared))
$(inst_slibdir)/$(rtld-version-installed-name): $(objpfx)ld.so $(+force)
        $(make-target-directory)
        $(do-install-program)

$(inst_slibdir)/$(rtld-installed-name): \
  $(inst_slibdir)/$(rtld-version-installed-name) \
  $(inst_slibdir)/libc-$(version).so
        $(make-shlib-link)

# Special target called by parent to install just the dynamic linker.
.PHONY: ldso_install
ldso_install: $(inst_slibdir)/$(rtld-installed-name)
endif


common-ldd-rewrite = -e 's%@RTLD@%$(slibdir)/$(rtld-installed-name)%g' \
                     -e 's%@VERSION@%$(version)%g' \
                     -e 's%@PKGVERSION@%$(PKGVERSION)%g' \
                     -e 's%@REPORT_BUGS_TO@%$(REPORT_BUGS_TO)%g'
sh-ldd-rewrite = $(common-ldd-rewrite) -e 's%@BASH@%/bin/sh%g;s/\$$"/"/g'
bash-ldd-rewrite = $(common-ldd-rewrite) -e 's%@BASH@%$(BASH)%g' \
                   -e 's%@TEXTDOMAINDIR@%$(msgcatdir)%g'



参考文献