2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

pykdump

2
Last updated at Posted at 2019-01-02

Python/CRASH API aka pykdump

カーネルのクラッシュダンプの中のslabをなめるのに、gdbスクリプトを回すと何日もかかる。pykdumpなら数分とか。

https://sourceforge.net/projects/pykdump/ ← SFなのでよく落ちている。クソ。

ドキュメントはソースアーカイブの中にlyx形式で置いてある。整形めんどい。

コンパイル

Pythonのバージョンがあわないとかでロクにコンパイルできない。コンパイル済みのバイナリを使うのが吉。今どきならDockerかなにかでCentOS 6環境を作って、そこにコンパイル済みのバイナリを展開する。ターゲットのカーネルデバッグシンボルも持ってきて、展開。Debianやubuntuのdebファイルは、実体はarアーカイブなので、

$ ar xf linux-image-VERSION-dbgsym_VERSION_amd64.ddeb

とすると、ファイルが3つくらいできて、そのうちdata.tar.xz (現行のバージョンの場合) に欲しいファイルが入っている。

$ crash64 /usr/lib/debug/boot/vmlinux-VERSION dump.20YYMMDDHHMM

crash64 X.X.X
Copyright (C) 2002-2017  Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010  IBM Corporation
Copyright (C) 1999-2006  Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012  Fujitsu Limited
Copyright (C) 2006, 2007  VA Linux Systems Japan K.K.
...

crash64> 

これで起動する。crash64となっているので、.crashrcではなく.crash64rcが読まれる。また、ターゲットのカーネルパッケージがインストールされていない場合、mod -Sではディレクトリを指定する必要がある。

~/.crash64rc
set emacs
mod -S /usr/lib/debug/lib/modules/VERSION
extend /usr/local/lib/mpykdump64.so

スクリプトの書き方

以下を最初に入れる。

from pykdump.API import *

カーネルのグローバル変数を読むには、readSymbol()

nr_pfn_mapped = readSymbol('nr_pfn_mapped')
pfn_mapped = readSymbol('pfn_mapped')

for i in range(0, nr_pfn_mapped):
    for pfn in range(pfn_mapped[i].start, pfn_mapped[i].end-pfn_mapped[i].start):
        # pfnにRAMの物理ページフレーム番号がはいるよ

あるアドレスにある構造体を読むには、readSU()

        pg = readSU('struct page', 0xffffea0000000000+64*pfn)

さっきのと組み合わせると、struct pageをなめられる。イエイ

       if ((pg.flags & 16384) and (pg.flags & 128)): # head && slab
            if (pg.slab_cache == なんとか):
                pgs_なんとか.append(pfn)

なんとかはあらかじめ目的のstruct kmem_cacheのポインタを入れておく。pgs_なんとか は、空で初期化しておく。

from pykdump.API import *

kmalloc_caches = readSymbol('kmalloc_caches')
km192 = kmalloc_caches[2]
pgs_km192 =[]
pfn_mapped = readSymbol('pfn_mapped')
for i in range(0, readSymbol('nr_pfn_mapped')):
    for pfn in range(pfn_mapped[i].start, pfn_mapped[i].end-pfn_mapped[i].start):
        pg = readSU('struct page', 0xffffea0000000000+64*pfn)
        if ((pg.flags & 16384) and (pg.flags & 128)):	# head && slab
            if (pg.slab_cache == km192_a):
                pgs_km192.append(pfn)

slabがslubになってから、crashのkmem -Sが使えなくなった (空きオブジェクトのないスラブがkmem_cacheからリンクされなくなった) ので、上みたいなコードで全部をスキャンしてやらなきゃならん。メンドい。

そうそう、どういうわけかbit fieldsがうまく扱えない。

for pfn in pg_km192:
    pg = readSU('struct page', 0xffffea0000000000+64*pfn)
    pgstart = 0xffff880000000000+pfn*4096
    # pykdump cannot access bitfields
    inuse = pg.counters & 0xffff
    objects = trunc((pg.counters & 0x7fff0000) / 65536)
    print ("SLAB 0x%x MEMORY 0x%x TOTAL %d ALLOCATED %d" % (pg_a, pgstart, objects, inuse))

あとは各ページの中のオブジェクトを表示すればいいのかな。

    usage = [1]*objects
    p = pg.freelist
    while p:
        usage[int((p-pgstart)/km192.size)] = 0
        p = readPtr(p)
    usedcount = 0
    for i in range(0, objects):
        if usage[i]:
            print("[0x%08x]" % (pgstart+i*km192.size))
            usedcount = usedcount + 1
        else:
            print(" 0x%08x " % (pgstart+i*km192.size))

    if usedcount != inuse:
        print("!!! inuse counter disagrees with the freelist")

readPtr()は、指定されたアドレスにあるポインタを整数として拾ってくる。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?