/* SPDX-License-Identifier: GPL-2.0 */
/*
* Convert sample address to data type using DWARF debug info.
*
* Written by Namhyung Kim <namhyung@kernel.org>
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "annotate.h"
#include "annotate-data.h"
#include "debuginfo.h"
#include "debug.h"
#include "dso.h"
#include "dwarf-regs.h"
#include "evsel.h"
#include "evlist.h"
#include "map.h"
#include "map_symbol.h"
#include "strbuf.h"
#include "symbol.h"
#include "symbol_conf.h"
#include "thread.h"
#define pr_debug_dtp(fmt, ...) \
do { \
if (debug_type_profile) \
pr_info(fmt, ##__VA_ARGS__); \
else \
pr_debug3(fmt, ##__VA_ARGS__); \
} while (0)
static void pr_debug_type_name(Dwarf_Die *die)
{
struct strbuf sb;
char *str;
if (!debug_type_profile && verbose < 3)
return;
strbuf_init(&sb, 32);
die_get_typename_from_type(die, &sb);
str = strbuf_detach(&sb, NULL);
pr_info(" type=%s (die:%lx)\n", str, (long)dwarf_dieoffset(die));
free(str);
}
/*
* Type information in a register, valid when @ok is true.
* The @caller_saved registers are invalidated after a function call.
*/
struct type_state_reg {
Dwarf_Die type;
bool ok;
bool caller_saved;
};
/* Type information in a stack location, dynamically allocated */
struct type_state_stack {
struct list_head list;
Dwarf_Die type;
int offset;
int size;
bool compound;
};
/* FIXME: This should be arch-dependent */
#define TYPE_STATE_MAX_REGS 16
/*
* State table to maintain type info in each register and stack location.
* It'll be updated when new variable is allocated or type info is moved
* to a new location (register or stack). As it'd be used with the
* shortest path of basic blocks, it only maintains a single table.
*/
struct type_state {
/* state of general purpose registers */
struct type_state_reg regs[TYPE_STATE_MAX_REGS];
/* state of stack location */
struct list_head stack_vars;
/* return value register */
int ret_reg;
};
static bool has_reg_type(struct type_state *state, int reg)
{
return (unsigned)reg < ARRAY_SIZE(state->regs);
}
static void init_type_state(struct type_state *state, struct arch *arch)
{
memset(state, 0, sizeof(*state));
INIT_LIST_HEAD(&state->stack_vars);
if (arch__is(arch, "x86")) {
state->regs[0].caller_saved = true;
state->regs[1].caller_saved = true;
state->regs[2].caller_saved = true;
state->regs[4].caller_saved = true;
state->regs[5].caller_saved = true;
state->regs[8].caller_saved = true;
state->regs[9].caller_saved = true;
state->regs[10].caller_saved = true;
state->regs[11].caller_saved = true;
state->ret_reg = 0;
}
}
static void exit_type_state(struct type_state *state)
{
struct type_state_stack *stack, *tmp;
list_for_each_entry_safe(stack, tmp, &state->stack_vars, list) {
list_del(&stack->list);
free(stack