Skip to content

Commit 4c59e58

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
libbpf: Add x86-specific USDT arg spec parsing logic
Add x86/x86_64-specific USDT argument specification parsing. Each architecture will require their own logic, as all this is arch-specific assembly-based notation. Architectures that libbpf doesn't support for USDTs will pr_warn() with specific error and return -ENOTSUP. We use sscanf() as a very powerful and easy to use string parser. Those spaces in sscanf's format string mean "skip any whitespaces", which is pretty nifty (and somewhat little known) feature. All this was tested on little-endian architecture, so bit shifts are probably off on big-endian, which our CI will hopefully prove. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Reviewed-by: Alan Maguire <alan.maguire@oracle.com> Reviewed-by: Dave Marchevsky <davemarchevsky@fb.com> Link: https://lore.kernel.org/bpf/20220404234202.331384-6-andrii@kernel.org
1 parent 999783c commit 4c59e58

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

tools/lib/bpf/usdt.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,8 +1168,113 @@ static int parse_usdt_spec(struct usdt_spec *spec, const struct usdt_note *note,
11681168
return 0;
11691169
}
11701170

1171+
/* Architecture-specific logic for parsing USDT argument location specs */
1172+
1173+
#if defined(__x86_64__) || defined(__i386__)
1174+
1175+
static int calc_pt_regs_off(const char *reg_name)
1176+
{
1177+
static struct {
1178+
const char *names[4];
1179+
size_t pt_regs_off;
1180+
} reg_map[] = {
1181+
#if __x86_64__
1182+
#define reg_off(reg64, reg32) offsetof(struct pt_regs, reg64)
1183+
#else
1184+
#define reg_off(reg64, reg32) offsetof(struct pt_regs, reg32)
1185+
#endif
1186+
{ {"rip", "eip", "", ""}, reg_off(rip, eip) },
1187+
{ {"rax", "eax", "ax", "al"}, reg_off(rax, eax) },
1188+
{ {"rbx", "ebx", "bx", "bl"}, reg_off(rbx, ebx) },
1189+
{ {"rcx", "ecx", "cx", "cl"}, reg_off(rcx, ecx) },
1190+
{ {"rdx", "edx", "dx", "dl"}, reg_off(rdx, edx) },
1191+
{ {"rsi", "esi", "si", "sil"}, reg_off(rsi, esi) },
1192+
{ {"rdi", "edi", "di", "dil"}, reg_off(rdi, edi) },
1193+
{ {"rbp", "ebp", "bp", "bpl"}, reg_off(rbp, ebp) },
1194+
{ {"rsp", "esp", "sp", "spl"}, reg_off(rsp, esp) },
1195+
#undef reg_off
1196+
#if __x86_64__
1197+
{ {"r8", "r8d", "r8w", "r8b"}, offsetof(struct pt_regs, r8) },
1198+
{ {"r9", "r9d", "r9w", "r9b"}, offsetof(struct pt_regs, r9) },
1199+
{ {"r10", "r10d", "r10w", "r10b"}, offsetof(struct pt_regs, r10) },
1200+
{ {"r11", "r11d", "r11w", "r11b"}, offsetof(struct pt_regs, r11) },
1201+
{ {"r12", "r12d", "r12w", "r12b"}, offsetof(struct pt_regs, r12) },
1202+
{ {"r13", "r13d", "r13w", "r13b"}, offsetof(struct pt_regs, r13) },
1203+
{ {"r14", "r14d", "r14w", "r14b"}, offsetof(struct pt_regs, r14) },
1204+
{ {"r15", "r15d", "r15w", "r15b"}, offsetof(struct pt_regs, r15) },
1205+
#endif
1206+
};
1207+
int i, j;
1208+
1209+
for (i = 0; i < ARRAY_SIZE(reg_map); i++) {
1210+
for (j = 0; j < ARRAY_SIZE(reg_map[i].names); j++) {
1211+
if (strcmp(reg_name, reg_map[i].names[j]) == 0)
1212+
return reg_map[i].pt_regs_off;
1213+
}
1214+
}
1215+
1216+
pr_warn("usdt: unrecognized register '%s'\n", reg_name);
1217+
return -ENOENT;
1218+
}
1219+
1220+
static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg)
1221+
{
1222+
char *reg_name = NULL;
1223+
int arg_sz, len, reg_off;
1224+
long off;
1225+
1226+
if (sscanf(arg_str, " %d @ %ld ( %%%m[^)] ) %n", &arg_sz, &off, &reg_name, &len) == 3) {
1227+
/* Memory dereference case, e.g., -4@-20(%rbp) */
1228+
arg->arg_type = USDT_ARG_REG_DEREF;
1229+
arg->val_off = off;
1230+
reg_off = calc_pt_regs_off(reg_name);
1231+
free(reg_name);
1232+
if (reg_off < 0)
1233+
return reg_off;
1234+
arg->reg_off = reg_off;
1235+
} else if (sscanf(arg_str, " %d @ %%%ms %n", &arg_sz, &reg_name, &len) == 2) {
1236+
/* Register read case, e.g., -4@%eax */
1237+
arg->arg_type = USDT_ARG_REG;
1238+
arg->val_off = 0;
1239+
1240+
reg_off = calc_pt_regs_off(reg_name);
1241+
free(reg_name);
1242+
if (reg_off < 0)
1243+
return reg_off;
1244+
arg->reg_off = reg_off;
1245+
} else if (sscanf(arg_str, " %d @ $%ld %n", &arg_sz, &off, &len) == 2) {
1246+
/* Constant value case, e.g., 4@$71 */
1247+
arg->arg_type = USDT_ARG_CONST;
1248+
arg->val_off = off;
1249+
arg->reg_off = 0;
1250+
} else {
1251+
pr_warn("usdt: unrecognized arg #%d spec '%s'\n", arg_num, arg_str);
1252+
return -EINVAL;
1253+
}
1254+
1255+
arg->arg_signed = arg_sz < 0;
1256+
if (arg_sz < 0)
1257+
arg_sz = -arg_sz;
1258+
1259+
switch (arg_sz) {
1260+
case 1: case 2: case 4: case 8:
1261+
arg->arg_bitshift = 64 - arg_sz * 8;
1262+
break;
1263+
default:
1264+
pr_warn("usdt: unsupported arg #%d (spec '%s') size: %d\n",
1265+
arg_num, arg_str, arg_sz);
1266+
return -EINVAL;
1267+
}
1268+
1269+
return len;
1270+
}
1271+
1272+
#else
1273+
11711274
static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg)
11721275
{
11731276
pr_warn("usdt: libbpf doesn't support USDTs on current architecture\n");
11741277
return -ENOTSUP;
11751278
}
1279+
1280+
#endif

0 commit comments

Comments
 (0)