vgrep便利ですねという話

この記事はLinuxその2 Advent Calendar 2020の4日目の記事です。

今回はtoolネタです。vgrepというgrep系のツールがあって結構便利です。これは今年のOpen Source Summit + Embedded Linux Conference North America 2020でのAsk the Expert SessionでGreg KHさんがコードを調べる時にvgrepを使ってると言っていたので知りました。

使い方はわりと簡単なのでREADME.mdを見ればOKだと思います。

grepとの比較

grep系のコマンドなので速さはどうなの?というところですが、qemu上のこんなriscv64環境(cpu4個、メモリ8GB)で試してみます。fedoraのriscv64版にもvgrepパッケージあります。

masami@fedora-riscv:~/linux-kernel$ uname -a
Linux fedora-riscv 5.10.0-rc7-ktest+ #8 SMP Mon Dec 7 10:35:20 EST 2020 riscv64 riscv64 riscv64 GNU/Linux

vgrepの実行。

masami@fedora-riscv:~/linux-kernel$ sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
masami@fedora-riscv:~/linux-kernel$ time vgrep --no-less "kmem_cache_alloc(" >/dev/null

real    0m49.499s
user    0m20.440s
sys     1m2.571s

find+grep(with xargs)の実行。

masami@fedora-riscv:~/linux-kernel$ sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
masami@fedora-riscv:~/linux-kernel$ time (find . -name "*.c" -o -name "*.h" | xargs grep "kmem_cache_alloc(" >/dev/null)

real    1m0.191s
user    0m4.412s
sys     0m48.670s

find+grepはコマンドをパイプ繋いでいるしというのはありますがvgrep良い感じですね。

使ってみる

まずは普通に検索してみます(ここからは普通にx86_64のデスクトップ環境で)。

masami@moon:~/linux-kernel$ vgrep "kmem_cache_alloc("
Index File                                                     Line Content
    0 Documentation/core-api/memory-allocation.rst              163 cache allocator. The cache should be set up with kmem_cache_create() or
    1 arch/arm64/mm/pgd.c                                        54 pgd_cache = kmem_cache_create("pgd_cache", PGD_SIZE, PGD_SIZE,
    2 arch/powerpc/kernel/rtas_flash.c                          713 flash_block_cache = kmem_cache_create("rtas_flash_cache",
    3 arch/powerpc/kvm/book3s_64_mmu_radix.c                   1442 kvm_pte_cache = kmem_cache_create("kvm-pte", size, size, 0, pte_ctor);
    4 arch/powerpc/kvm/book3s_64_mmu_radix.c                   1448 kvm_pmd_cache = kmem_cache_create("kvm-pmd", size, size, 0, pmd_ctor);
    5 arch/powerpc/kvm/book3s_mmu_hpte.c                        377 hpte_cache = kmem_cache_create("kvm-spt", sizeof(struct hpte_cache),
    6 arch/powerpc/mm/init-common.c                             127 new = kmem_cache_create(name, table_size, align, 0, ctor(shift));
    7 arch/powerpc/perf/hv-24x7.c                              1732 hv_page_cache = kmem_cache_create("hv-page-4096", 4096, 4096, 0, NULL);
    8 arch/powerpc/platforms/cell/spufs/inode.c                 786 spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
    9 arch/powerpc/platforms/pseries/setup.c                    315 dtl_cache = kmem_cache_create("dtl", DISPATCH_LOG_BYTES,
   10 arch/powerpc/sysdev/xive/native.c                         518 xive_provision_cache = kmem_cache_create("xive-provision",
   11 arch/s390/kernel/nmi.c                                     86 mcesa_cache = kmem_cache_create("nmi_save_areas", size, size, 0, NULL);
   12 arch/s390/mm/pgalloc.c                                    557 base_pgt_cache = kmem_cache_create("base_pgt", sz, sz, 0, NULL);
   13 arch/s390/pci/pci.c                                       782 zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb),
   14 arch/s390/pci/pci_dma.c                                   635 dma_region_table_cache = kmem_cache_create("PCI_DMA_region_tables",
   15 arch/s390/pci/pci_dma.c                                   641 dma_page_table_cache = kmem_cache_create("PCI_DMA_page_tables",
   16 arch/sh/kernel/cpu/sh4/sq.c                               379 sq_cache = kmem_cache_create("store_queue_cache",
   17 arch/sh/kernel/dwarf.c                                   1171 dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
   18 arch/sh/kernel/dwarf.c                                   1175 dwarf_reg_cachep = kmem_cache_create("dwarf_regs",
   19 arch/sh/kernel/process.c                                   58 task_xstate_cachep = kmem_cache_create("task_xstate", xstate_size,
   20 arch/sh/mm/pgtable.c                                       22 pgd_cachep = kmem_cache_create("pgd_cache",
   21 arch/sh/mm/pgtable.c                                       26 pmd_cachep = kmem_cache_create("pmd_cache",
   22 arch/sparc/mm/tsb.c                                       345 pgtable_cache = kmem_cache_create("pgtable_cache",
   23 arch/sparc/mm/tsb.c                                       358 tsb_caches[i] = kmem_cache_create(name,
   24 arch/x86/events/intel/lbr.c                              1596 return kmem_cache_create("x86_lbr", size, align, 0, NULL);
   25 arch/x86/kvm/mmu/mmu.c                                   5876 pte_list_desc_cache = kmem_cache_create("pte_list_desc",
   26 arch/x86/kvm/mmu/mmu.c                                   5882 mmu_page_header_cache = kmem_cache_create("kvm_mmu_page_header",
   27 arch/x86/kvm/x86.c                                       7876 x86_fpu_cache = kmem_cache_create("x86_fpu", sizeof(struct fpu),
   28 arch/x86/mm/pgtable.c                                     382 pgd_cache = kmem_cache_create("pgd_cache", PGD_SIZE, PGD_ALIGN,
   29 block/bio-integrity.c                                     471 bip_slab = kmem_cache_create("bio_integrity_payload",
   30 block/bio.c                                               107 slab = kmem_cache_create(bslab->name, sz, ARCH_KMALLOC_MINALIGN,

検索結果はデフォルトではページャとしてlessが使われます。表示内容は次のような感じです。

  1. 見つかったファイルの検索結果の番号
  2. ファイルのpath
  3. 見つかった行の行番号
  4. 検索結果の文字列

これだけだと単に検索しただけですが、この検索結果を利用してさらに操作ができます。ここで検索結果27番のファイルをエディタで見たいとします。その場合は次のように-sオプションに検索結果の番号を引数で渡すとEDITOR環境変数に設定されているエディタで該当ファイルの該当行の辺りを開いてくれます。

masami@moon:~/linux-kernel$ vgrep -s 27

全体的に前後の行をみたいという時(grepコマンドの-A、-Bオプションみたいに)はc/contextコマンドを使います。つぎの例だと検索で見つかった行を中心として前後5行ずつ表示します。

masami@moon:~/linux-kernel$ vgrep -s c5
~~~
--- 26 arch/x86/kvm/mmu/mmu.c ---------------------------------------
5877                                        sizeof(struct pte_list_desc),
5878                                        0, SLAB_ACCOUNT, NULL);
5879    if (!pte_list_desc_cache)
5880            goto out;
5881 
5882    mmu_page_header_cache = kmem_cache_create("kvm_mmu_page_header",
5883                                              sizeof(struct kvm_mmu_page),
5884                                              0, SLAB_ACCOUNT, NULL);
5885    if (!mmu_page_header_cache)
5886            goto out;
5887 
--- 27 arch/x86/kvm/x86.c ------------------------------------------
7871            r = -EOPNOTSUPP;
7872            goto out;
7873    }
7874 
7875    r = -ENOMEM;
7876    x86_fpu_cache = kmem_cache_create("x86_fpu", sizeof(struct fpu),
7877                                      __alignof__(struct fpu), SLAB_ACCOUNT,
7878                                      NULL);
7879    if (!x86_fpu_cache) {
7880            printk(KERN_ERR "kvm: failed to allocate cache for x86 fpu\n");
7881            goto out;

特定のファイル(1つもしくは複数)のみを対象にするなら検索結果の番号をさらに引数として渡します。この例では27と28、それに30番を指定してます。

masami@moon:~/linux-kernel$ vgrep -s c3 27-28,30
--- 27 arch/x86/kvm/x86.c ------------------------------------------
7873    }
7874 
7875    r = -ENOMEM;
7876    x86_fpu_cache = kmem_cache_create("x86_fpu", sizeof(struct fpu),
7877                                      __alignof__(struct fpu), SLAB_ACCOUNT,
7878                                      NULL);
7879    if (!x86_fpu_cache) {
--- 28 arch/x86/mm/pgtable.c ---------------------------------------
 379     * page for pgd. We are able to just allocate a 32-byte for pgd.
 380     * During boot time, we create a 32-byte slab for pgd table allocation.
 381     */
 382    pgd_cache = kmem_cache_create("pgd_cache", PGD_SIZE, PGD_ALIGN,
 383                                  SLAB_PANIC, NULL);
 384 }
 385 
--- 30 block/bio.c ------------------------------------------------
 104    bslab = &bio_slabs[entry];
 105 
 106    snprintf(bslab->name, sizeof(bslab->name), "bio-%d", entry);
 107    slab = kmem_cache_create(bslab->name, sz, ARCH_KMALLOC_MINALIGN,
 108                             SLAB_HWCACHE_ALIGN, NULL);
 109    if (!slab)
 110            goto out_unlock;

ディレクトリごとに何個のhitが合ったかなんてのも見れます。

masami@moon:~/linux-kernel$ vgrep -s tree
Matches Directory
    493 
      1 Documentation
      1 Documentation/core-api
     28 arch
      1 arch/arm64
      1 arch/arm64/mm
      9 arch/powerpc
      1 arch/powerpc/kernel
      3 arch/powerpc/kvm
      1 arch/powerpc/mm
      1 arch/powerpc/perf
      2 arch/powerpc/platforms
      1 arch/powerpc/platforms/cell
      1 arch/powerpc/platforms/cell/spufs
      1 arch/powerpc/platforms/pseries
      1 arch/powerpc/sysdev
      1 arch/powerpc/sysdev/xive
      5 arch/s390
      1 arch/s390/kernel
      1 arch/s390/mm
      3 arch/s390/pci
      6 arch/sh
      4 arch/sh/kernel
      1 arch/sh/kernel/cpu
      1 arch/sh/kernel/cpu/sh4
      2 arch/sh/mm
      2 arch/sparc
      2 arch/sparc/mm
      5 arch/x86
      1 arch/x86/events
      1 arch/x86/events/intel
      3 arch/x86/kvm
      2 arch/x86/kvm/mmu
      1 arch/x86/mm
      6 block
    141 drivers
      1 drivers/acpi
      9 drivers/block
      1 drivers/block/aoe
      4 drivers/block/drbd
      1 drivers/block/xen-blkback
      7 drivers/crypto
      1 drivers/crypto/axis
      2 drivers/crypto/caam

別の検索を行いたい場合は最初にやったみたいにvgrep 検索ワードでも出来ますし、g/grepコマンドを利用して再検索することもできます。

masami@moon:~/linux-kernel$ vgrep -s g "bootconfig"
Index File                                                   Line Content
    0 Documentation/admin-guide/bootconfig.rst                  3 .. _bootconfig:
    1 Documentation/admin-guide/bootconfig.rst                 81 overriding the default value by adding (partial) custom bootconfigs
    2 Documentation/admin-guide/bootconfig.rst                 82 without parsing the default bootconfig.
    3 Documentation/admin-guide/bootconfig.rst                126 /proc/bootconfig
    4 Documentation/admin-guide/bootconfig.rst                129 /proc/bootconfig is a user-space interface of the boot config.
    5 Documentation/admin-guide/bootconfig.rst                143 [initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]
    6 Documentation/admin-guide/bootconfig.rst                149 (``\0``) will be added. Thus the ``size`` is the length of the bootconfig
    7 Documentation/admin-guide/bootconfig.rst                157 loader passes a longer size, the kernel feils to find the bootconfig data.
    8 Documentation/admin-guide/bootconfig.rst                159 To do this operation, Linux kernel provides "bootconfig" command under
    9 Documentation/admin-guide/bootconfig.rst                160 tools/bootconfig, which allows admin to apply or delete the config file
   10 Documentation/admin-guide/bootconfig.rst                163 # make -C tools/bootconfig
   11 Documentation/admin-guide/bootconfig.rst                165 To add your boot config file to initrd image, run bootconfig as below
   12 Documentation/admin-guide/bootconfig.rst                168 # tools/bootconfig/bootconfig -a your-config /boot/initrd.img-X.Y.Z
   13 Documentation/admin-guide/bootconfig.rst                172 # tools/bootconfig/bootconfig -d /boot/initrd.img-X.Y.Z
   14 Documentation/admin-guide/bootconfig.rst                174 Then add "bootconfig" on the normal kernel command line to tell the
   15 Documentation/admin-guide/bootconfig.rst                175 kernel to look for the bootconfig at the end of the initrd file.
   16 Documentation/admin-guide/bootconfig.rst                190 Anyway, since bootconfig command verifies it when appending a boot config
   17 Documentation/admin-guide/bootconfig.rst                237 .. kernel-doc:: include/linux/bootconfig.h
   18 Documentation/admin-guide/bootconfig.rst                238 .. kernel-doc:: lib/bootconfig.c
   19 Documentation/admin-guide/index.rst                      70 bootconfig
   20 Documentation/admin-guide/kernel-parameters.txt         446 bootconfig    [KNL]
   21 Documentation/admin-guide/kernel-parameters.txt         450 See Documentation/admin-guide/bootconfig.rst
   22 Documentation/trace/boottime-trace.rst                   17 this uses bootconfig file to describe tracing feature programming.
   23 Documentation/trace/boottime-trace.rst                   27 .. [1] See :ref:`Documentation/admin-guide/bootconfig.rst <bootconfig>`
   24 Documentation/translations/zh_CN/admin-guide/index.rst   69 bootconfig

vgrepはこんな感じの使い方です。わりとシンプルで使いやすいので気になった人は使ってみてください。