kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

Universal Build-ID

http://fedoraproject.org/wiki/Summer_Coding_2010_ideas_-_Universal_Build-ID

Summary

Build-IDs are currently being put into binaries, shared libraries, core files and related debuginfo files to uniquely identify the build a user or developer is working with. There are a couple of conventions in place to use this information to identify “currently running” or “distro installed” builds. This helps with identifying what was being run and match it to the corresponding package, sources and debuginfo for tools that want to help the user show what is going on (at the moment mostly when things break). We would like to extend this to a more universial approach, that helps people identify historical, local, non- or cross-distro or organisational builds. So that Build-IDs become useful outside the current “static” setup and retain information over time and across upgrades.

Build-ID background

Build-IDs are unique identifiers of “builds”. A build is an executable, a shared library, the kernel, a module, etc. You can also find the build-id in a running process, a core file or a separate debuginfo file.

The main idea behind Build-IDs is to make elf files “self-identifying”. This means that when you have a Build-ID it should uniquely identify a final executable or shared library. The default Build-ID calculation (done through ld –build-id, see the ld manual) calculates a sha1 hash (160 bits/20 bytes) based on all the ELF header bits and section contents in the file. Which means that it is unique among the set of meaningful contents for ELF files and identical when the output file would otherwise have been identical. GCC now passes –build-id to the linker by default.

When an executable or shared library is loaded into memory the Build-ID will also be loaded into memory, a core dump of a process will also have the Build-IDs of the executable and the shared libraries embedded. And when separating debuginfo from the main executable or shared library into .debug files the original Build-ID will also be copied over. This means it is easy to match a core file or a running process to the original executable and shared library builds. And that matching those against the debuginfo files that provide more information for introspection and debugging should be trivial.

Fedora has had full support for build-ids since Fedora Core 8: https://fedoraproject.org/wiki/Releases/FeatureBuildId

Getting Build-IDs

A simple way to get the build-id(s) is through eu-unstrip (part of elfutils).

build-id from an executable, shared library or separate debuginfo file:
$ eu-unstrip -n -e <exec|.sharedlib|.debug>

build-ids of an executable and all shared libraries from a core file:
$ eu-unstrip -n –core

build-ids of an executable and all shared libraries of a running process:
$ eu-unstrip -n –pid

build-id of the running kernel and all loaded modules:
$ eu-unstrip -n -k

the meaning of '?' in Linux kernel panic call trace

  • ‘?’ means that the information about this stack entry is probably not reliable.

The stack output mechanism (see the implementation of dump_trace() function) was unable to prove that the address it has found is a valid return address in the call stack.

‘?’ itself is output by printk_stack_address().

The stack entry may be valid or not. Sometimes one may simply skip it. It may be helpful to investigate the disassembly of the involved module to see which function is called at ClearFunctionName+0x88 (or, on x86, immediately before that position).

Concerning reliability

On x86, when dump_stack() is called, the function that actually examines the stack is print_context_stack() defined in arch/x86/kernel/dumpstack.c. Take a look at its code, I’ll try to explain it below.

I assume DWARF2 stack unwind facilities are not available in your Linux system (most likely, they are not, if it is not OpenSUSE or SLES). In this case, print_context_stack() seems to do the following.

It starts from an address (‘stack’ variable in the code) that is guaranteed to be an address of a stack location. It is actually the address of a local variable in dump_stack().

The function repeatedly increments that address (while (valid_stack_ptr …) { … stack++}) and checks if what it points to could also be an address in the kernel code (if (__kernel_text_address(addr)) …). This way it attempts to find the functions' return addresses pushed on stack when these functions were called.

Of course, not every unsigned long value that looks like a return address is actually a return address. So the function tries to check it. If frame pointers are used in the code of the kernel (%ebp/%rbp registers are employed for that if CONFIG_FRAME_POINTER is set), they can be used to traverse the stack frames of the functions. The return address for a function lies just above the frame pointer (i.e. at %ebp/%rbp + sizeof(unsigned long)). print_context_stack checks exactly that.

If there is a stack frame for which the value ‘stack’ points to is the return address, the value is considered a reliable stack entry. ops->address will be called for it with reliable == 1, it will eventually call printk_stack_address() and the value will be output as a reliable call stack entry. Otherwise the address will be considered unreliable. It will be output anyway but with ‘?’ prepended.

[NB] If frame pointer information is not available (e.g. like it was in Debian 6 by default), all call stack entries will be marked as unreliable for this reason.

The systems with DWARF2 unwinding support (and with CONFIG_STACK_UNWIND set) is a whole another story.

强制内联和强制不内联

1.强制不内联

一个函数,如果代码量比较少的话,用 -O3优化开关的话,gcc有可能将这个函数强制内联(inline)即使,你在函数前没有写inline助记符。
如果是一个手写汇编的函数,那样的话很有可能破坏参数。gcc里有强制不内联的,用法如下

1
void foo() __attribute__((noinline));

但是有的gcc可能会忽略 noinline。
那么你可以将你实现的这个函数写到调用函数之后,就不会被inline了。这是因为编译器gcc只内联当前函数之前可见(实现代码在前)的函数。

2.优化时无法识别inline函数中的ASM汇编

当GCC尝试内联一个函数时,如果该函数中存在内联汇编,则该汇编语句块可能被丢弃;

1
2
3
4
5
6
7
8
9
10
11
12
13
__inline__ __attribute__((always_inline))int Increment(int volatile *add, int inc)
{
    int res;
    __asm__
    (
    "lock \n\t"
    "xaddl %0,(%1)\n\t"
    :"=r"(res)
    :"r"(add),"0"(inc)
    :"memory"
    );
    return res;
}