Skip to content
Permalink
Browse files
dtrace: sdt provider core components
This implements the core (linked-in) machinery needed for SDT
tracepoints:

 - generate empty stub function calls __dtrace_probe_* for each probe
   point and perf-event probe point, and record their section-relative
   offset in tables in special symbols in the output; calls to
   is-enabling probes (conditionals of the form
   if (DTRACE_FOO_ENABLED(probe-name))) are translated as well
 - similarly record the names and types of arguments to probes in
   special sections
 - parse both of these at load time, and substitute in nops over the top
   of the stub functions, remembering their locations: is-enabled probes
   get 0-returns patched over the top
 - on probe enabling, patch invalid-operation traps over the top of
   those stub functions; handle these by calling the probe, then return
   as if the trap had never happened

The provider module itself is added in the next commit.

Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Signed-off-by: Tomas Jedlicka <tomas.jedlicka@oracle.com>
Signed-off-by: Eugene Loh <eugene.loh@oracle.com>
Signed-off-by: David Mc Lean <david.mclean@oracle.com>
Signed-off-by: Vincent Lim <vincent.lim@oracle.com>
  • Loading branch information
kvanhees authored and nickalcock committed Nov 7, 2021
1 parent 2f31b95 commit 9bb7076b2261cf2470e94df26762679a950a0b1e
Show file tree
Hide file tree
Showing 30 changed files with 2,500 additions and 19 deletions.
@@ -165,3 +165,9 @@ x509.genkey

# Documentation toolchain
sphinx_*/
#
# Generated DTrace SDT files
#
*.sdtinfo.c
*.sdtinfo.h
*.sdtstub.S
@@ -1956,6 +1956,7 @@ clean: $(clean-dirs)
-o -name '*.dtb' -o -name '*.dtbo' -o -name '*.dtb.S' -o -name '*.dt.yaml' \
-o -name '*.dwo' -o -name '*.lst' \
-o -name '*.su' -o -name '*.mod' \
-o -name '*.sdtinfo.c' -o -name '*.sdtinfo.h' -o -name '*.sdtstub.S' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name '*.lex.c' -o -name '*.tab.[ch]' \
-o -name '*.asn1.[ch]' \
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Dynamic Tracing for Linux - SDT Implementation defines
*
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

/*
* Note: The contents of this file are private to the implementation of the
* DTrace subsystem and are subject to change at any time without notice.
*/

#ifndef _X86_64_SDT_ARCH_H
#define _X86_64_SDT_ARCH_H

#define SDT_AFRAMES 4

#endif /* _X86_64_SDT_ARCH_H */
@@ -12,16 +12,22 @@

typedef uint8_t asm_instr_t;

#define ASM_CALL_SIZE 5

typedef int (*prov_exit_f)(void);

/*
* Structure to hold DTrace specific information about modules (including the
* core kernel module). Note that each module (and the main kernel) already
* has one field that relates to probing:
* has three fields that relate to probing:
* - sdt_probes: description of SDT probes in the module
* - sdt_probec: number of SDT probes in the module
* - pdata: pointer to a dtrace_module struct (for DTrace)
*/
struct dtrace_module {
int enabled_cnt;
size_t sdt_probe_cnt;
size_t fbt_probe_cnt;
prov_exit_f prov_exit; /* Called with module_mutex held */
};

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */

/* Copyright (C) 2016 Oracle, Inc. */

#ifndef _X86_DTRACE_SDT_ARCH_H
#define _X86_DTRACE_SDT_ARCH_H

#define __DTRACE_SDT_ISENABLED_PROTO void
#define __DTRACE_SDT_ISENABLED_ARGS

#endif /* _X86_DTRACE_SDT_ARCH_H */
@@ -6,11 +6,24 @@
#ifndef _X86_DTRACE_UTIL_H
#define _X86_DTRACE_UTIL_H

#define DTRACE_INVOP_NOPS 0x0f /* 5-byte NOP sequence */
#define DTRACE_INVOP_MOV_RSP_RBP 0x48 /* mov %rsp, %rbp = 48 89 e5 */
#define DTRACE_INVOP_PUSH_BP 0x55 /* push %rbp = 55 */
#define DTRACE_INVOP_NOP 0x90 /* nop = 90 */
#define DTRACE_INVOP_LEAVE 0xc9 /* leave = c9 */
#define DTRACE_INVOP_RET 0xc3 /* ret = c3 */

#ifndef __ASSEMBLY__

#include <asm/dtrace_arch.h>
#include <asm/ptrace.h>

extern int dtrace_invop_add(uint8_t (*func)(struct pt_regs *));
extern void dtrace_invop_remove(uint8_t (*func)(struct pt_regs *));

extern void dtrace_invop_enable(asm_instr_t *, asm_instr_t);
extern void dtrace_invop_disable(asm_instr_t *, asm_instr_t);

#endif

#endif /* _X86_DTRACE_UTIL_H */
@@ -9,6 +9,7 @@
#include <linux/compiler.h>
#include <asm/paravirt.h>
#include <asm/bitops.h>
#include <linux/sdt.h>

/*
* Your basic SMP spinlocks, allowing only a single CPU anywhere
@@ -25,6 +25,7 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
*/
#define POKE_MAX_OPCODE_SIZE 5

extern void add_nops(void *insns, unsigned int len);
extern void text_poke_early(void *addr, const void *opcode, size_t len);

/*
@@ -101,7 +101,7 @@ const unsigned char * const x86_nops[ASM_NOP_MAX+1] =
};

/* Use this to add nops to a buffer, then text_poke the whole buffer. */
static void __init_or_module add_nops(void *insns, unsigned int len)
void __init_or_module add_nops(void *insns, unsigned int len)
{
while (len > 0) {
unsigned int noplen = len;
@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* FILE: dtrace_sdt.c
* DESCRIPTION: Dynamic Tracing: SDT registration code (arch-specific)
*
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
*/

#include <linux/kernel.h>
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/dtrace_os.h>
#include <linux/sdt.h>
#include <linux/slab.h>
#include <linux/sync_core.h>
#include <linux/vmalloc.h>
#include <asm/nmi.h>
#include <asm/nops.h>
#include <asm/dtrace_arch.h>
#include <asm/text-patching.h>

static uint8_t nops[ASM_CALL_SIZE];
static uint8_t movs[ASM_CALL_SIZE];

#define DT_OP_REX_RAX 0x48
#define DT_OP_XOR_EAX_0 0x33
#define DT_OP_XOR_EAX_1 0xc0

/* This code is based on apply_alternatives and text_poke_early. It needs to
* run before SMP is initialized in order to avoid SMP problems with patching
* code that might be accessed on another CPU.
*/
void __init_or_module dtrace_sdt_nop_multi(asm_instr_t **addrs,
int *is_enabled, int cnt)
{
int i;
asm_instr_t *addr;
unsigned long flags;

stop_nmi();
local_irq_save(flags);

for (i = 0; i < cnt; i++) {
addr = addrs[i];
if (likely(!is_enabled[i]))
memcpy(addr, nops, sizeof(nops));
else
memcpy(addr, movs, sizeof(movs));
}

sync_core();
local_irq_restore(flags);
restart_nmi();
}

void __init dtrace_sdt_init_arch(void)
{
/*
* A little unusual, but potentially necessary. While we could use a
* single NOP sequence of length ASM_CALL_SIZE, we need to consider the
* fact that when a SDT probe point is enabled, a single invalid opcode
* is written on the first byte of this NOP sequence. By using a
* sequence of a 1-byte NOP, followed by a (ASM_CALL_SIZE - 1) byte NOP
* sequence, we play it pretty safe.
*/
add_nops(nops, 1);
add_nops(nops + 1, ASM_CALL_SIZE - 1);

/*
* Is-enabled probe points contain an "xor %rax, %rax" when disabled.
*/
movs[0] = DT_OP_REX_RAX;
movs[1] = DT_OP_XOR_EAX_0;
movs[2] = DT_OP_XOR_EAX_1;
add_nops(movs + 3, ASM_CALL_SIZE - 3);
}

0 comments on commit 9bb7076

Please sign in to comment.