pstrace

Форк
0
/
dwarf_call_site.c 
351 строка · 11.1 Кб
1
/*
2
 * dwarf_call_site.cpp
3
 *
4
 *  Created on: Feb 1, 2020
5
 *      Author: nnosov
6
 */
7

8

9
#include <dwarf.h>
10
#include <elfutils/libdw.h>
11

12
#include "dwarf_call_site.h"
13
#include "dwarf_function.h"
14
#include "dwarf_utils.h"
15
#include "dwarf_utils.h"
16
#include "utils/hash_multimap.h"
17

18
// -----------------------------------------------------------------------------------
19
// pst_call_site_param
20
// -----------------------------------------------------------------------------------
21
void pst_call_site_param_init(pst_call_site_param* param)
22
{
23
    // fields
24
    param->param = NULL;
25
    param->name = NULL;
26
    param->value = 0;
27
    pst_dwarf_expr_init(&param->location);
28
    param->allocated = false;
29
}
30

31
pst_call_site_param* pst_call_site_param_new()
32
{
33
    pst_call_site_param* param = pst_alloc(pst_call_site_param);
34
    if(param) {
35
        pst_call_site_param_init(param);
36
        param->allocated = true;
37
    }
38

39
    return param;
40
}
41

42
void pst_call_site_param_fini(pst_call_site_param* param)
43
{
44
    if(param->name) {
45
        pst_free(param->name);
46
        param->name = NULL;
47
    }
48

49
    if(param->allocated) {
50
        pst_free(param);
51
    }
52
}
53

54
// -----------------------------------------------------------------------------------
55
// pst_call_site
56
// -----------------------------------------------------------------------------------
57
static pst_call_site_param* add_param(pst_call_site* site)
58
{
59
    pst_call_site_param* p = pst_call_site_param_new();
60
    pst_call_site_param_init(p);
61
    list_add_bottom(&site->params, &p->node);
62

63
    return p;
64
}
65

66
static void del_param(pst_call_site_param* p)
67
{
68
    list_del(&p->node);
69
    pst_call_site_param_fini(p);
70
}
71

72
static pst_call_site_param* next_param(pst_call_site* site, pst_call_site_param* p)
73
{
74
    list_node* n = (p == NULL) ? list_first(&site->params) : list_next(&p->node);
75
    pst_call_site_param* ret = NULL;
76
    if(n) {
77
        ret = list_entry(n, pst_call_site_param, node);
78
    }
79

80
    return ret;
81
}
82

83
pst_call_site_param* pst_call_site_find(pst_call_site* site, pst_dwarf_expr* expr)
84
{
85
    for(pst_call_site_param* param = next_param(site, NULL); param; param = next_param(site, param)) {
86
        if(pst_dwarf_expr_equal(&param->location, expr)) {
87
            return param;
88
        }
89
    }
90

91
    return NULL;
92
}
93

94
bool call_site_handle_dwarf(pst_call_site* site, Dwarf_Die* child)
95
{
96
    Dwarf_Attribute attr_mem;
97
    Dwarf_Attribute* attr;
98

99
    do {
100
        switch(dwarf_tag(child)) {
101
            case DW_TAG_GNU_call_site_parameter: {
102
                Dwarf_Addr pc;
103
                unw_get_reg(site->ctx->curr_frame, UNW_REG_IP,  &pc);
104

105
                // expression represent where callee parameter will be stored
106
                pst_call_site_param* param = add_param(site);
107
                if(dwarf_hasattr(child, DW_AT_location)) {
108
                    // determine location of parameter in stack/heap or CPU registers
109
                    attr = dwarf_attr(child, DW_AT_location, &attr_mem);
110
                    if(!handle_location(site->ctx, attr, &param->location, pc, NULL)) {
111
                        pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff);
112
                        del_param(param);
113
                        return false;
114
                    }
115
                    pst_log(SEVERITY_DEBUG, "  DW_AT_location: %s", site->ctx->buff);
116
                }
117

118
                // expression represents call parameter's value
119
                if(dwarf_hasattr(child, DW_AT_GNU_call_site_value)) {
120
                    // handle value expression here
121
                    attr = dwarf_attr(child, DW_AT_GNU_call_site_value, &attr_mem);
122
                    pst_decl0(pst_dwarf_expr, loc);
123
                    if(handle_location(site->ctx, attr, &loc, pc, NULL)) {
124
                        param->value = loc.value;
125
                        pst_dwarf_expr_fini(&loc);
126
                        pst_log(SEVERITY_DEBUG, "  DW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", site->ctx->buff, param->value);
127
                    } else {
128
                        pst_log(SEVERITY_WARNING, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff);
129
                        del_param(param);
130
                        pst_dwarf_expr_fini(&loc);
131
                        return false;
132
                    }
133
                }
134
                break;
135
            }
136
            default:
137
                break;
138
        }
139
    } while (dwarf_siblingof (child, child) == 0);
140

141
    return true;
142
}
143

144
void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn)
145
{
146
    list_node_init(&site->node);
147

148
    site->target = tgt;
149
    if(orn) {
150
        site->origin = pst_strdup(orn);
151
    } else {
152
        site->origin = NULL;
153
    }
154
    site->die = NULL;
155
    list_head_init(&site->params);
156
    site->ctx = c;
157
    site->allocated = false;
158
}
159

160
pst_call_site* pst_call_site_new(pst_context* c, uint64_t tgt, const char* orn)
161
{
162
    pst_call_site* nc = pst_alloc(pst_call_site);
163
    if(nc) {
164
        pst_call_site_init(nc, c, tgt, orn);
165
        nc->allocated = true;
166
    }
167

168
    return nc;
169
}
170

171
void pst_call_site_fini(pst_call_site* site)
172
{
173
    if(site->origin) {
174
        pst_free(site->origin);
175
        site->origin = NULL;
176
    }
177

178
    if(site->allocated) {
179
        pst_free(site);
180
    }
181
}
182

183

184
// -----------------------------------------------------------------------------------
185
// pst_call_site_storage
186
// -----------------------------------------------------------------------------------
187

188
// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually.
189
// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it.
190
// probably by invoke by:
191
// Dwarf_Die *scopes;
192
// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes
193
// if (n <= 0) -> FAILURE
194
// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars()
195
// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it
196
// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee
197
// get DIE of return type
198
bool pst_call_site_storage_handle_dwarf(pst_call_site_storage* storage, Dwarf_Die* result, pst_function* fn)
199
{
200
    Dwarf_Die origin;
201
    Dwarf_Attribute attr_mem;
202
    Dwarf_Attribute* attr;
203

204
    pst_log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:");
205
    // reference to DIE which represents callee's parameter if compiler knows where it is at compile time
206
    const char* oname = NULL;
207
    if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) {
208
        oname = dwarf_diename(&origin);
209
        pst_log(SEVERITY_DEBUG, "DW_AT_abstract_origin: '%s'", oname);
210
    }
211

212
    // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression.  For indirect calls or jumps where it is unknown at
213
    // compile time which subprogram will be called the expression computes the address of the subprogram that will be called.
214
    uint64_t target = 0;
215
    if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) {
216
        attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem);
217
        if(attr) {
218
            pst_decl0(pst_dwarf_expr, expr);
219
            if(handle_location(storage->ctx, &attr_mem, &expr, fn->info.pc, fn)) {
220
                target = expr.value;
221
                pst_log(SEVERITY_DEBUG, "DW_AT_GNU_call_site_target: %#lX", target);
222
            }
223
            pst_dwarf_expr_fini(&expr);
224
        }
225
    }
226

227
    if(target == 0 && oname == NULL) {
228
        pst_log(SEVERITY_ERROR, "Cannot determine both call-site target and origin");
229
        return false;
230
    }
231

232
    Dwarf_Die child;
233
    if(dwarf_child (result, &child) == 0) {
234
        pst_call_site* st = pst_call_site_storage_add(storage, target, oname);
235

236
        st->tail_call = (dwarf_hasattr(result, DW_AT_call_tail_call) != 0);
237

238
        // if DW_AT_low_pc attribute is specified, then it's value is actually PC in caller's frame (address of invocation of callee)
239
        if(dwarf_hasattr (result, DW_AT_low_pc) && dwarf_attr(result, DW_AT_low_pc, &attr_mem)) {
240
            if(!dwarf_formaddr(&attr_mem, &st->call_pc)) {
241
                pst_log(SEVERITY_DEBUG, "DW_AT_low_pc: %#lX", st->call_pc);
242
            }
243
        }
244

245
        if(!call_site_handle_dwarf(st, &child)) {
246
            pst_call_site_storage_del(storage, st);
247
            return false;
248
        }
249
    }
250

251
    return true;
252
}
253

254
pst_call_site* storage_call_site_by_origin(pst_call_site_storage* storage, const char* origin)
255
{
256
    pst_call_site* ret = NULL;
257
    hash_node* node = hash_find(&storage->cs_to_origin, origin, strlen(origin));
258
    if(node) {
259
        ret = hash_entry(node, pst_call_site, org_node);
260
    }
261

262
    return ret;
263
}
264

265
pst_call_site* storage_call_site_by_target(pst_call_site_storage* storage, uint64_t target)
266
{
267
    pst_call_site* ret = NULL;
268
    hash_node* node = hash_find(&storage->cs_to_target, (char*)&target, sizeof(target));
269
    if(node) {
270
        ret = hash_entry(node, pst_call_site, tgt_node);
271
    }
272

273
    return ret;
274
}
275

276
pst_call_site* pst_call_site_storage_add(pst_call_site_storage* storage, uint64_t target, const char* origin)
277
{
278
    pst_new(pst_call_site, st, storage->ctx, target, origin);
279
    list_add_bottom(&storage->call_sites, &st->node);
280

281
    if(target) {
282
        hash_add(&storage->cs_to_target, &st->tgt_node, &target, sizeof(target));
283
    } else if(origin) {
284
        hash_add(&storage->cs_to_origin, &st->org_node, origin, strlen(origin));
285
    }
286

287
    return st;
288
}
289

290
void pst_call_site_storage_del(pst_call_site_storage* storage, pst_call_site* st)
291
{
292
    hash_node* node = NULL;
293
    list_del(&st->node);
294

295
    if(st->target) {
296
       node = hash_find(&storage->cs_to_target, &st->target, sizeof(st->target));
297
    } else if(st->origin) {
298
        node = hash_find(&storage->cs_to_origin, st->origin, strlen(st->origin));
299
    }
300

301
    if(node) {
302
        hash_del(node);
303
    }
304

305
    pst_free(st);
306
}
307

308
pst_call_site* pst_call_site_storage_find(pst_call_site_storage* storage, pst_function* callee)
309
{
310
    uint64_t start_pc = storage->ctx->base_addr + callee->info.lowpc;
311
    pst_call_site* cs = storage_call_site_by_target(storage, start_pc);
312
    if(!cs) {
313
        cs = storage_call_site_by_origin(storage, callee->info.name);
314
    }
315

316
    return cs;
317
}
318

319
void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx)
320
{
321
    storage->ctx = ctx;
322
    list_head_init(&storage->call_sites);
323
    hash_head_init(&storage->cs_to_target, HASH_MIN_SHIFT, 0, 0);
324
    hash_head_init(&storage->cs_to_origin, HASH_MIN_SHIFT, 0 ,0);
325
    storage->allocated = false;
326
}
327

328
pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx)
329
{
330
    pst_call_site_storage* ns = pst_alloc(pst_call_site_storage);
331
    if(ns) {
332
        pst_call_site_storage_init(ns, ctx);
333
        ns->allocated = true;
334
    }
335

336
    return ns;
337
}
338

339
void pst_call_site_storage_fini(pst_call_site_storage* storage)
340
{
341
    pst_call_site*  site = NULL;
342
    struct list_node  *pos, *tn;
343
    list_for_each_entry_safe(site, pos, tn, &storage->call_sites, node) {
344
        list_del(&site->node);
345
        pst_call_site_fini(site);
346
    }
347

348
    if(storage->allocated) {
349
        pst_free(storage);
350
    }
351
}
352

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.