kk Blog —— 通用基础

date [-d @int|str] [+%s|"+%F %T"]

高精度定时器 high-cpu-load

http://stackoverflow.com/questions/1125297/nanosleep-high-cpu-usage

I noticed that a little test program which calls nanosleep is showing a huge difference in CPU usage when run on Linux machines with a kernel newer than 2.6.22.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <time.h>
int main (void)
{
	struct timespec sleepTime;
	struct timespec returnTime;
	sleepTime.tv_sec = 0;
	sleepTime.tv_nsec = 1000;
	while (1)
	{
		nanosleep(&sleepTime, &returnTime); // usleep(1); 同样异常
	}
	return 0;
}

(Yes, I realise this program does nothing)

If I compile this and run it on an openSUSE 10.3 machine (2.6.22.19-0.2-default), the program does not even show up on the process list generated by “top”, indicating to me that it is using very little CPU time. If I run it on an openSUSE 11.1 machine (2.6.27.23-0.1-default), top shows the program taking 40% of the CPU time. Running on Fedora 9 (2.6.25-14.fc9.i686) and Fedora 10 also showed the same high CPU usage in “top”.

Has there been a change in the kernel that affects this?


Answers

This is due to the introduction of NO_HZ into the mainline scheduler.

Previously, your 1,000 ns sleep was usually sleeping for a whole tick - 1,000,000 ns. Now, when the machine is otherwise idle, it’s actually only sleeping for what you asked for. So it’s running the while() loop and syscall around 1,000 times more frequently - hence a lot more CPU usage. If you increase tv_nsec you should see a reduction in the CPU usage.


1
2
3
4
5
6
7
int nanosleep(const struct timespec *req, struct timespec *rem);

struct timespec
{
	time_t  tv_sec;         /* seconds */
	long    tv_nsec;        /* nanoseconds */
};

这个函数功能是暂停某个进程直到你规定的时间后恢复,参数req就是你要暂停的时间,其中req->tv_sec是以秒为单位,而tv_nsec以毫 微秒为单位(10的-9次方秒)。由于调用nanosleep是是进程进入TASK_INTERRUPTIBLE,这种状态是会相应信号而进入 TASK_RUNNING状态的,这就意味着有可能会没有等到你规定的时间就因为其它信号而唤醒,此时函数返回-1,切还剩余的时间会被记录在rem中。

crash vs gdb work

贴自https://www.redhat.com/archives/crash-utility/2014-October/msg00002.html
Yes, sure. GDB works very differently from crash. There main conceptual
difference is that GDB only handles with VIRTUAL addresses, while the
crash utility first translates everything to PHYSICAL addresses.
Consequently, GDB ignores the PhysAddr field in ELF program headers,
and crash ignores the VirtAddr field.

I have looked at some of my ELF dump files, and it seems to me that
VirtAddr is not filled correctly, except for kernel text and static
data (address range 0xffffffff80000000-0xffffffff9fffffff). Your linked
list is most likely allocated in the direct mapping
(0xffff880000000000-0xffffc7ffffffffff). However, I found out that the
virtual addresses for the direct mapping segments are wrong, e.g. my
dump file specifies it at 0xffff810000000000 (hypervisor area). This is
most likely a bug in the kernel code that implements /proc/vmcore.

But that’s beside the point. Why? The Linux kernel maps many physical
pages more than once into the virtual address space. It would be waste
of space if you saved it multiple times (for each virtual address that
maps to it). The crash utility can translate each virtual address to
the physical address and map it onto ELF segments using PhysAddr.
Incidentally, the PhysAddr fields are correct in my dump files…

I’m glad you’re interested in using GDB to read kernel dump files,
especially if you’re willing to make it work for real. I have proposed
more than once that the crash utility be re-implemented in pure gdb.
Last time I looked (approx. 1.5 years ago) the main missing pieces were:

  1. Use of physical addresses (described above)
  2. Support for multiple virtual address spaces (for different process contexts)
  3. Ability to read compressed kdump files
  4. Ability to use 64-bit files on 32-bit platforms (to handle PAE)

HTH,
Petr Tesarik

静态编译crash + xbt + bt -H

要在centos6上编译,为了能在centos5用,用静态编译
有两个显示函数参数的patch,但是不一定能起作用
patch1:

https://github.com/jhammond/xbt https://www.redhat.com/archives/crash-utility/2013-September/msg00010.html

patch2:

https://github.com/hziSot/crash-stack-parser https://github.com/hziSot/crash-stack-parser/blob/master/crash-parse-stack-7.0.1.patch

一、依赖包:

yum install bison zlib zlib-static glibc-static elfutils-devel elfutils-devel-static elfutils-libelf-devel-static ncurses ncurses-static crash-devel

二、patch1: xbt 显示参数

patch: https://github.com/hziSot/crash-stack-parser
make CFLAGS+=–static LDFLAGS+=–static

三、patch2: bt -H 显示参数

1
2
3
4
5
6
7
8
9
10
11
12
13
依赖:有些没有静态包,要自己编译安装:
liblzma.a: http://tukaani.org/xz/xz-5.0.7.tar.bz2
libbz2.a:  http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
下载代码:git clone https://github.com/jhammond/xbt.git xbt.git
把xbt.git/xbt_crash.c中函数xbt_func前的static删了
把xbt.git/xbt_crash.c中函数xmod_init的register_extension删了
把 xbt 命令加到global_data.c        函数x86_64_exception_frame已经在其他库中定义了,所以要换个名字
编译xbt代码:make   ==  rm -rf *.so
把 xbt.git/xbt_crash.o  xbt.git/xbt_dwarf.o  xbt.git/xbt_dwfl.o  xbt.git/xbt_eval.o  xbt.git/xbt_frame_print.o 加到 Makefile 的 OBJECT_FILES= 中
make CFLAGS+=--static LDFLAGS+="--static -lc  -lm -ldl -ldw -lebl -lelf -lbz2 -llzma"


注意:-lelf -lebl要放在-ldw后面。

数A到数B之间的统计

Problem 1896 神奇的魔法数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Accept: 98    Submit: 307
Time Limit: 1000 mSec    Memory Limit : 32768 KB


Problem Description


John定义了一种“神奇的魔法数”。 不含前导零且相邻两个数字之差至少为m的正整数被称为“神奇的魔法数”。特别的,对于任意的m,数字1..9都是“神奇的魔法数”。
John想知道,对于给定的m,在正整数a和b之间,包括a和b,总共有多少个“神奇的魔法数”?

Input


第一行一个数字T(1<=T<=100),表示测试数据组数。
接下来T行,每行代表一组测试数据,包括三个整数a,b,m。(1<=a<=b<=2,000,000,000, 0<=m<=9)

Output


对于每组测试数据,输出一行表示“神奇的魔法数”的个数。

Sample Input

7 1 10 2 1 20 3 1 100 0 10 20 4 20 30 5 1 10 9 11 100 9

Sample Output

9 15 100 5 3 9 1

Source福州大学第七届程序设计竞赛
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>

int n,m,d,dp[13][13],sum[13],dn[13],dm[13];


// DFS的时候这两个地方根据不同要求写。
int dfs(int da[], int dep, int all)
{
	int i,j,ret=0;
	if (dep == 0) return 1;
	for (i=0;i<da[dep];i++)
	{
		if (all > 0 || i > 0) {
			if (all == 0 || i-da[dep+1]>=d || i-da[dep+1]<=-d)
				ret += dp[dep][i];
		} else
			ret += sum[dep-1];
	}
	if (all == 0 || da[dep]-da[dep+1]>=d || da[dep]-da[dep+1]<=-d)
		ret += dfs(da, dep-1, all+da[dep]);
	return ret;
}

int main()
{
	int i,j,k,l,T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d %d %d", &m, &n, &d);
		for (i=0;i<13;i++)
			for (j=0;j<13;j++) dp[i][j] = 0;
		sum[0] = 0; sum[1] = 9;
		for (i=0;i<10;i++) dp[1][i] = 1;
		for (i=2;i<13;i++) {
			sum[i] = sum[i-1];
			for (j=0;j<10;j++) {
				for (k=0;k<10;k++)
					if (j-k>=d || j-k<=-d)
						dp[i][j] += dp[i-1][k];
				if (j > 0)
					sum[i] += dp[i][j];
			}
		}
//        for (i=0;i<=2;i++)
//            for (j=0;j<10;j++) printf("%d %d %d\n", i, j, dp[i][j]);
		i = 1; k = n;
		while (i < 13) {
			dn[i] = k % 10; k /= 10;
			i++;
		}
		i = 1; k = m-1;
		while (i < 13) {
			dm[i] = k % 10; k /= 10;
			i++;
		}
		n = dfs(dn, 11, 0);
		if (m == 1)
			m = 0;
		else
			m = dfs(dm, 11, 0);
		printf("%d\n", n-m);
	}
	return 0;
}

How many 0’s?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Time Limit: 1000MS
Memory Limit: 65536KTotal Submissions: 2997
Accepted: 1603

Description

A Benedict monk No.16 writes down the decimal representations of all natural numbers between and including m and n, m ≤ n. How many 0's will he write down?

Input

Input consists of a sequence of lines. Each line contains two unsigned 32-bit integers m and n, m ≤ n. The last line of input has the value of m negative and this line should not be processed.

Output

For each line of input print one line of output with one integer number giving the number of 0's written down by the monk.

Sample Input

10 11
100 200
0 500
1234567890 2345678901
0 4294967295
-1 -1

Sample Output

1
22
92
987654304
3825876150

Source

Waterloo Local Contest, 2006.5.27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.util.*;
import java.math.*;
import java.io.*;

public class Main {
	static long val,n,m,dp[][]=new long[13][13],a[]=new long[13],dn[]=new long[13], dm[]=new long[13], sum[]=new long[13];
	static long dfs(long dnm[], int dep, long all)
	{
		int i, j, k;
		long ret=0;
		if (dep == 0) return 0;
		for (i=0;i<dnm[dep];i++) {
			if (all > 0 || i > 0)
				ret += dp[dep][i]; // 需要计算前导0
			else
				ret += sum[dep-1]; // 不需要计算前导0
		}
		if (all > 0 && dnm[dep] == 0)
			ret += val % a[dep] + 1;
		ret += dfs(dnm, dep-1, all+dnm[dep]);
		return ret;
	}

	public static void main(String[] args) {
		int i,j,k,l;
		Scanner cin = new Scanner(System.in);
		a[1] = 10;
		for (i=2;i<13;i++) a[i] = a[i-1]*10;
		for (i=0;i<13;i++)
			for (j=0;j<13;j++) dp[i][j] = 0;
		dp[1][0] = 1;
		sum[0] = sum[1] = 0;
		for (i=2;i<13;i++) {
			sum[i] = sum[i-1];
			for (j=0;j<10;j++) {
				for (k=0;k<10;k++)
					dp[i][j] += dp[i-1][k];
				dp[i][j] += j==0 ? a[i-1] : 0;
				if (j > 0)
					sum[i] += dp[i][j];
			}
		}
		while (true) {
			m = cin.nextLong();
			n = cin.nextLong();
			if (m == -1 || n == -1) break;
			for (i=0;i<13;i++) dn[i] = dm[i] = 0;
			i = 1;
			val = n;
			while (val > 0) {
				dn[i] = val % 10;
				val /= 10;
				i++;
			}
			i = 1;
			val = m-1;
			while (val > 0) {
				dm[i] = val % 10;
				val /= 10;
				i++;
			}
			val = n;
			n = dfs(dn, 12, 0) + 1; // 0 还有一个0
			val = m-1;
			m = dfs(dm, 12, 0) + 1;
			if (val < 0) m = 0;
			System.out.println(n-m);
		}
	}
}

修改elf文件标记的源码路径debugedit,find-debuginfo

1
2
yum install rpm-build
sudo apt-get install rpm

/usr/lib/rpm/debugedit 用来改变源码查找路径。

1
2
3
4
5
6
7
8
9
10
11
$ /usr/lib/rpm/debugedit
Usage: debugedit [OPTION...]
  -b, --base-dir=STRING      base build directory of objects
  -d, --dest-dir=STRING      directory to rewrite base-dir into
  -l, --list-file=STRING     file where to put list of source and header file
	                     names
  -i, --build-id             recompute build ID note and print ID on stdout

Help options:
  -?, --help                 Show this help message
  --usage                    Display brief usage message

base-dir 长度要大等于 dest-dir
-i 输出build-id
-l 输出源编译文件位置,便于有需要的人打包

debugedit 会在.debug_info .debug_abbrev .debug_line .debug_str中将base_dir目录替换为dest_dir目录。
* 需要注意,如果base_dir是路径中除文件名的部分,则.debug_line中的The Directory Table的目录和.debug_info中的DW_AT_comp_dir(指向.debug_str的内容)不会替换。
如:
.debug_line中的Table中有一个目录为/root/Desktop,如果用 -b /root/Desktop则匹配不上这条。
* 因为:debugedit在匹配的时候在base_dir和dest_dir后面加了一个'/‘
其他部分能替换是因为他们存的是文件路径,不是文件夹路径


内核处理debuginfo的时候,只会替换DW_AT_comp_dir。因为DW_AT_name是一个相对地址


可以修改debugedit源码,base_dir、dest_dir后面不再默认添加'/‘,只是单纯的把base_dir替换成dest_dir

http://vault.centos.org/6.7/os/Source/SPackages/rpm-4.8.0-47.el6.src.rpm

http://vault.centos.org/5.11/updates/SRPMS/rpm-4.4.2.3-36.el5_11.src.rpm

删除tool/debugedit.c中的下面代码即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  if (base_dir != NULL && base_dir[strlen (base_dir)-1] != '/')
    {
      p = malloc (strlen (base_dir) + 2);
      strcpy (p, base_dir);
      strcat (p, "/");
      free (base_dir);
      base_dir = p;
    }
  if (dest_dir != NULL && dest_dir[strlen (dest_dir)-1] != '/')
    {
      p = malloc (strlen (dest_dir) + 2);
      strcpy (p, dest_dir);
      strcat (p, "/");
      free (dest_dir);
      dest_dir = p;
    }

debugedit -l在输出.debug_line的时候会去除base_dir、dest_dir前缀,由于他们不是以/结尾,所以输出的文件会以/开头,类似/net/ipv4/tcp_input.c,下一步按这个目录去copy文件时就copy不到。所以应该把开头的/去掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
diff --git a/tools/debugedit.c b/tools/debugedit.c
index 064ac0a..bda05db 100644
--- a/tools/debugedit.c
+++ b/tools/debugedit.c
@@ -588,9 +588,9 @@ edit_dwarf2_line (DSO *dso, uint_32 off, char *comp_dir, int phase)
          if (base_dir == NULL)
            p = s;
          else if (has_prefix (s, base_dir))
-           p = s + strlen (base_dir);
+           { p = s + strlen (base_dir); if (*p == '/') p++; }
          else if (has_prefix (s, dest_dir))
-           p = s + strlen (dest_dir);
+           { p = s + strlen (dest_dir); if (*p == '/') p++; }

          if (p)
            {

debugedit_el6

debugedit_el5


.debug_str段保存着所有全局变量的名字,以0x00作为每一个全局变量名的结束。
在其它段来调用名字时,是以其在.debug_str段的偏移量来实现的
gcc -g /root/Desktop/a.c -o /root/Desktop/a.out
用绝对路径编译,在.debug_str段中就会存下源文件路径,.debug_info的DW_TAG_compile_unit中的DW_AT_name对应.debug_str中的偏移。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ objdump --dwarf=str a.out
....
  0x00000000 474e5520 4320342e 342e3720 32303132 GNU C 4.4.7 2012
  0x00000010 30333133 20285265 64204861 7420342e 0313 (Red Hat 4.
  0x00000020 342e372d 3429006c 6f6e6720 756e7369 4.7-4).long unsi
  0x00000030 676e6564 20696e74 002f726f 6f742f44 gned int./root/D
  0x00000040 65736b74 6f702f61 2e630075 6e736967 esktop/a.c.unsig
  0x00000050 6e656420 63686172 006d6169 6e006c6f ned char.main.lo
  0x00000060 6e672069 6e74002f 726f6f74 2f446573 ng int./root/Des
  0x00000070 6b746f70 0073686f 72742075 6e736967 ktop.short unsig
  0x00000080 6e656420 696e7400 73686f72 7420696e ned int.short in
  0x00000090 7400                                t.


$ objdump --dwarf=info a.out
.....
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
	< c>   DW_AT_producer    : (indirect string, offset: 0x0): GNU C 4.4.7 20120313 (Red Hat 4.4.7-4)
	<10>   DW_AT_language    : 1        (ANSI C)
	<11>   DW_AT_name        : (indirect string, offset: 0x39): /root/Desktop/a.c
	<15>   DW_AT_comp_dir    : (indirect string, offset: 0x67): /root/Desktop
	<19>   DW_AT_low_pc      : 0x4004c4
	<21>   DW_AT_high_pc     : 0x40051c
	<29>   DW_AT_stmt_list   : 0x0

1
$ /usr/lib/rpm/debugedit -b /root/Desktop -d /usr/src /root/Desktop/a.out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ objdump --dwarf=str a.out
...
  0x00000000 474e5520 4320342e 342e3720 32303132 GNU C 4.4.7 2012
  0x00000010 30333133 20285265 64204861 7420342e 0313 (Red Hat 4.
  0x00000020 342e372d 3429006c 6f6e6720 756e7369 4.7-4).long unsi
  0x00000030 676e6564 20696e74 002f7573 722f7372 gned int./usr/sr
  0x00000040 632f612e 63002f61 2e630075 6e736967 c/a.c./a.c.unsig
  0x00000050 6e656420 63686172 006d6169 6e006c6f ned char.main.lo
  0x00000060 6e672069 6e74002f 726f6f74 2f446573 ng int./root/Des
  0x00000070 6b746f70 0073686f 72742075 6e736967 ktop.short unsig
  0x00000080 6e656420 696e7400 73686f72 7420696e ned int.short in
  0x00000090 7400                                t.


$ objdump --dwarf=info a.out

...
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
	< c>   DW_AT_producer    : (indirect string, offset: 0x0): GNU C 4.4.7 20120313 (Red Hat 4.4.7-4)
	<10>   DW_AT_language    : 1        (ANSI C)
	<11>   DW_AT_name        : (indirect string, offset: 0x39): /usr/src/a.c
	<15>   DW_AT_comp_dir    : (indirect string, offset: 0x67): /root/Desktop
	<19>   DW_AT_low_pc      : 0x4004c4
	<21>   DW_AT_high_pc     : 0x40051c
	<29>   DW_AT_stmt_list   : 0x0