pstrace

Форк
0
/
dwarf_function.c 
482 строки · 16.0 Кб
1
/*
2
 * dwarf_function.cpp
3
 *
4
 *  Created on: Feb 1, 2020
5
 *      Author: nnosov
6
 */
7

8

9
#include <dwarf.h>
10
#include <stdlib.h>
11
#include <libiberty/demangle.h>
12
#include <stdio.h>
13

14

15
#include "dwarf_stack.h"
16
#include "dwarf_utils.h"
17
#include "dwarf_function.h"
18

19
// -----------------------------------------------------------------------------------
20
// pst_function
21
// -----------------------------------------------------------------------------------
22
static bool get_frame(pst_function* fn)
23
{
24
    // get CFI (Call Frame Information) for current module
25
    // from handle_cfi()
26
    Dwarf_Addr mod_bias = 0;
27
    Dwarf_CFI* cfi = dwfl_module_eh_cfi(fn->ctx->module, &mod_bias); // rty .eh_cfi first
28
    if(!cfi) { // then try .debug_fame second
29
        cfi = dwfl_module_dwarf_cfi(fn->ctx->module, &mod_bias);
30
    }
31
    if(!cfi) {
32
        pst_log(SEVERITY_ERROR, "Cannot find CFI for module");
33
        return false;
34
    }
35

36
    // get frame of CFI for address
37
    int result = dwarf_cfi_addrframe (cfi, fn->info.pc - mod_bias, &fn->frame);
38
    if (result != 0) {
39
        pst_log(SEVERITY_ERROR, "Failed to find CFI frame for module");
40
        return false;
41
    }
42

43
    // setup context to match frame
44
    fn->ctx->frame = fn->frame;
45

46
    // get return register and PC range for function
47
    Dwarf_Addr start = fn->info.pc;
48
    Dwarf_Addr end = fn->info.pc;
49
    bool signalp;
50
    int ra_regno = dwarf_frame_info (fn->frame, &start, &end, &signalp);
51
    if(ra_regno >= 0) {
52
        start += mod_bias;
53
        end += mod_bias;
54
        reginfo info; info.regno = ra_regno;
55
        dwfl_module_register_names(fn->ctx->module, regname_callback, &info);
56
        pst_log(SEVERITY_INFO, "Function %s(...): '.eh/debug frame' info: PC range:  => [%#" PRIx64 ", %#" PRIx64 "], return register: %s, in_signal = %s",
57
                fn->info.name, start, end, info.regname, signalp ? "true" : "false");
58
    } else {
59
        pst_log(SEVERITY_WARNING, "Return address register info unavailable (%s)", dwarf_errmsg(0));
60
    }
61

62
    // finally get CFA (Canonical Frame Address)
63
    // Point cfa_ops to dummy to match print_detail expectations.
64
    // (nops == 0 && cfa_ops != NULL => "undefined")
65
    Dwarf_Op dummy;
66
    Dwarf_Op *cfa_ops = &dummy;
67
    size_t cfa_nops;
68
    if(dwarf_frame_cfa(fn->frame, &cfa_ops, &cfa_nops)) {
69
        pst_log(SEVERITY_ERROR, "Failed to get CFA for frame");
70
        return false;
71
    }
72

73
    fn->ctx->print_expr(fn->ctx, cfa_ops, cfa_nops, NULL);
74

75
    bool nret = true;
76
    pst_decl(pst_dwarf_stack, stack, fn->ctx);
77
    if(pst_dwarf_stack_calc(&stack, cfa_ops, cfa_nops, NULL, NULL) && pst_dwarf_stack_get_value(&stack, &fn->info.cfa)) {
78
        pst_log(SEVERITY_INFO, "Function %s(...): CFA expression: %s ==> %#lX", fn->info.name, fn->ctx->buff, fn->info.cfa);
79

80
        // setup context to match CFA for frame
81
        fn->ctx->cfa = fn->info.cfa;
82
    } else {
83
        nret = false;
84
        pst_log(SEVERITY_ERROR, "Failed to calculate CFA expression");
85
    }
86

87
    pst_dwarf_stack_fini(&stack);
88

89
    return nret;
90
}
91

92
static pst_parameter* add_param(pst_function* fn)
93
{
94
    pst_new(pst_parameter, p, fn->ctx);
95
    list_add_bottom(&fn->params, &p->node);
96

97
    return p;
98
}
99

100
static void del_param(pst_parameter* p)
101
{
102
    list_del(&p->node);
103
    pst_free(p);
104
}
105

106
static void clear(pst_function* fn)
107
{
108
    pst_parameter*  param = NULL;
109
    struct list_node  *pos, *tn;
110
    list_for_each_entry_safe(param, pos, tn, &fn->params, node) {
111
        list_del(&param->node);
112
        pst_parameter_fini(param);
113
    }
114

115
    pst_call_site_storage_fini(&fn->call_sites);
116
}
117

118
static bool handle_lexical_block(pst_function* fn, Dwarf_Die* result)
119
{
120
    Dwarf_Attribute attr_mem;
121
    Dwarf_Die origin;
122
    if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) {
123
        Dwarf_Die child;
124
        if(dwarf_child (&origin, &child) == 0) {
125
            do {
126
                switch (dwarf_tag (&child))
127
                {
128
                    case DW_TAG_variable:
129
                    case DW_TAG_formal_parameter:
130
                        pst_log(SEVERITY_DEBUG, "Abstract origin: %s('%s')", dwarf_diename(&origin), dwarf_diename (&child));
131
                        break;
132
                    // Also handle  DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...);
133
                    default:
134
                        break;
135
                }
136
            } while (dwarf_siblingof (&child, &child) == 0);
137
        }
138
    }
139

140
    Dwarf_Die child;
141
    if(dwarf_child (result, &child) == 0) {
142
        do {
143
            switch (dwarf_tag (&child)) {
144
                case DW_TAG_lexical_block:
145
                    handle_lexical_block(fn, &child);
146
                    break;
147
                case DW_TAG_variable: {
148
                    pst_parameter* param = add_param(fn);
149
                    if(!parameter_handle_dwarf(param, &child, fn)) {
150
                        del_param(param);
151
                    }
152
                    break;
153
                }
154
                case DW_TAG_GNU_call_site:
155
                    pst_call_site_storage_handle_dwarf(&fn->call_sites, &child, fn);
156
                    break;
157
                case DW_TAG_inlined_subroutine:
158
                    pst_log(SEVERITY_DEBUG, "Skipping Lexical block tag 'DW_TAG_inlined_subroutine'");
159
                    break;
160
                default:
161
                    pst_log(SEVERITY_WARNING, "Unknown Lexical block tag 0x%X", dwarf_tag(&child));
162
                    break;
163
            }
164
        }while (dwarf_siblingof (&child, &child) == 0);
165
    }
166

167
    return true;
168
}
169

170
pst_parameter* function_next_parameter(pst_function* fn, pst_parameter* p)
171
{
172
    struct list_node* n = (p == NULL) ? list_first(&fn->params) : list_next(&p->node);
173

174
    pst_parameter* ret = NULL;
175
    if(n) {
176
        ret = list_entry(n, pst_parameter, node);
177
    }
178

179
    return ret;
180
}
181

182
void function_print_simple(pst_function* fn)
183
{
184
    fn->ctx->print(fn->ctx, "%s() ", fn->info.name);
185
    if(fn->info.file) {
186
        fn->ctx->print(fn->ctx, "at %s:%d, %p", fn->info.file, fn->info.line, (void*)fn->info.pc);
187
    } else {
188
        fn->ctx->print(fn->ctx, "at %p", (void*)fn->info.pc);
189
    }
190
    fn->ctx->print(fn->ctx, "\n");
191
}
192

193
bool function_print_pretty(pst_function* fn)
194
{
195
    char* at = NULL;
196
    if(fn->info.file) {
197
        if(!asprintf(&at, " at %s:%d, %p", fn->info.file, fn->info.line, (void*)fn->info.pc))
198
            return false;
199
    } else if(!asprintf(&at, " at %p", (void*)fn->info.pc)){
200
        return false;
201
    }
202

203
    // handle return parameter and be safe if function haven't parameters (for example, dwar info for function is absent)
204
    pst_parameter* param = function_next_parameter(fn, NULL);
205
    if(param && (param->info.flags & PARAM_RETURN)) {
206
        // print return value type, function name and start list of parameters
207
        parameter_print(param);
208
        fn->ctx->print(fn->ctx, " %s(", fn->info.name);
209
        param = function_next_parameter(fn, param);
210
    } else {
211
        fn->ctx->print(fn->ctx, "%s(", fn->info.name);
212
    }
213

214
    bool first = true; bool start_variable = false;
215
    for(; param; param = function_next_parameter(fn, param)) {
216
        if(param->info.flags & PARAM_RETURN) {
217
            // print return value type, function name and start list of parameters
218
            parameter_print(param);
219
            fn->ctx->print(fn->ctx, " %s(", fn->info.name);
220
            continue;
221
        }
222

223
        if(param->info.flags & PARAM_VARIABLE) {
224
            if(!start_variable) {
225
                fn->ctx->print(fn->ctx, ")%s\n", at);
226
                fn->ctx->print(fn->ctx, "{\n");
227
                start_variable = true;
228
            }
229
            if(param->info.line) {
230
                fn->ctx->print(fn->ctx, "%04u:   ", param->info.line);
231
            } else {
232
                fn->ctx->print(fn->ctx, "        ");
233
            }
234
            parameter_print(param);
235
            fn->ctx->print(fn->ctx, ";\n");
236
        } else {
237
            if(first) {
238
                first = false;
239
            } else {
240
                fn->ctx->print(fn->ctx, ", ");
241
            }
242
            parameter_print(param);
243
        }
244
    }
245

246
    if(!start_variable) {
247
        fn->ctx->print(fn->ctx, ")%s\n", at);
248
    } else {
249
        fn->ctx->print(fn->ctx, "}\n");
250
    }
251

252
    free(at);
253
    return true;
254
}
255

256
bool function_handle_dwarf(pst_function * fn, Dwarf_Die* d)
257
{
258
    fn->die = d;
259
    get_frame(fn);
260

261
    Dwarf_Attribute attr_mem;
262
    Dwarf_Attribute* attr;
263

264
    // get list of offsets from process base address of continuous memory ranges where function's code resides
265
//  if(dwarf_haspc(d, pc)) {
266
    dwarf_lowpc(d, &fn->info.lowpc);
267
    dwarf_highpc(d, &fn->info.highpc);
268
//  } else {
269
//      pst_log(SEVERITY_ERROR, "Function's '%s' DIE hasn't definitions of memory offsets of function's code", dwarf_diename(d));
270
//      return false;
271
//  }
272

273
    unw_proc_info_t info;
274
    unw_get_proc_info(&fn->context, &info);
275
    fn->ctx->clean_print(fn->ctx);
276

277
    pst_log(SEVERITY_INFO, "Function %s(...): LOW_PC = %#lX, HIGH_PC = %#lX, offset from base address: 0x%lX, START_PC = 0x%lX, offset from start of function: 0x%lX",
278
            dwarf_diename(d), fn->info.lowpc, fn->info.highpc, fn->info.pc - fn->ctx->base_addr, info.start_ip, info.start_ip - fn->ctx->base_addr);
279
    fn->ctx->print_registers(fn->ctx, 0x0, 0x10);
280
    pst_log(SEVERITY_INFO, "Function %s(...): CFA: %#lX %s", dwarf_diename(d), fn->parent ? fn->parent->info.sp : 0, fn->ctx->buff);
281
    pst_log(SEVERITY_INFO, "Function %s(...): %s", dwarf_diename(d), fn->ctx->buff);
282

283
    // determine function's stack frame base
284
    attr = dwarf_attr(fn->die, DW_AT_frame_base, &attr_mem);
285
    if(attr) {
286
        if(dwarf_hasform(attr, DW_FORM_exprloc)) {
287
            Dwarf_Op *expr;
288
            size_t exprlen;
289
            if (dwarf_getlocation (attr, &expr, &exprlen) == 0) {
290
                fn->ctx->print_expr(fn->ctx, expr, exprlen, attr);
291
                pst_decl(pst_dwarf_stack, stack, fn->ctx);
292
                if(pst_dwarf_stack_calc(&stack, expr, exprlen, attr, fn)) {
293
                    uint64_t value;
294
                    if(pst_dwarf_stack_get_value(&stack, &value)) {
295
                        pst_log(SEVERITY_DEBUG, "DW_AT_framebase expression: \"%s\" ==> 0x%lX", fn->ctx->buff, value);
296
                    } else {
297
                        pst_log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_framebase expression: %s", fn->ctx->buff);
298
                    }
299
                } else {
300
                    pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_framebase expression: %s", fn->ctx->buff);
301
                }
302
                pst_dwarf_stack_fini(&stack);
303
            } else {
304
                pst_log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X", attr->form, attr->code);
305
            }
306
        }
307
    }
308

309
    // Get reference to return attribute type of the function
310
    // may be to use dwfl_module_return_value_location() instead
311
    pst_parameter* ret_p = add_param(fn); ret_p->info.flags |= PARAM_RETURN;
312
    if(dwarf_hasattr(fn->die, DW_AT_type)) {
313
        if(!parameter_handle_type(ret_p, fn->die)) {
314
            pst_log(SEVERITY_ERROR, "Failed to handle return parameter type for function %s(...)", fn->info.name);
315
            del_param(ret_p);
316
        }
317
    } else {
318
        parameter_add_type(ret_p, "void", PARAM_TYPE_VOID);
319
    }
320

321
    // handle and save additionally these attributes:
322
    // 1. string of DW_AT_linkage_name (mangled name of program if any)
323
    //      A debugging information entry may have a DW_AT_linkage_name attribute
324
    //      whose value is a null-terminated string containing the object file linkage name
325
    //      associated with the corresponding entity.
326

327
    // 2. flag DW_AT_external attribute
328
    //      A DW_AT_external attribute, which is a flag, if the name of a variable is
329
    //      visible outside of its enclosing compilation unit.
330

331
    // 3. A subroutine entry may contain a DW_AT_main_subprogram attribute which is
332
    //      a flag whose presence indicates that the subroutine has been identified as the
333
    //      starting function of the program. If more than one subprogram contains this flag,
334
    //      any one of them may be the starting subroutine of the program.
335

336

337
    Dwarf_Die result;
338
    if(dwarf_child(fn->die, &result) != 0)
339
        return false;
340

341
    // went through parameters and local variables of the function
342
    do {
343
        switch (dwarf_tag(&result)) {
344
            case DW_TAG_formal_parameter:
345
            case DW_TAG_variable: {
346
                pst_parameter* param = add_param(fn);
347
                if(!parameter_handle_dwarf(param, &result, fn)) {
348
                    del_param(param);
349
                }
350

351
                break;
352
            }
353
            case DW_TAG_GNU_call_site:
354
                pst_call_site_storage_handle_dwarf(&fn->call_sites, &result, fn);
355
                break;
356

357
                //              case DW_TAG_inlined_subroutine:
358
                //                  /* Recurse further down */
359
                //                  HandleFunction(&result);
360
                //                  break;
361
            case DW_TAG_lexical_block: {
362
                handle_lexical_block(fn, &result);
363
                break;
364
            }
365
            case DW_TAG_unspecified_parameters: {
366
                pst_parameter* param = add_param(fn);
367
                param->info.flags |= PARAM_TYPE_UNSPEC;
368
                param->info.name = pst_strdup("...");
369
                break;
370
            }
371

372
            // Also handle:
373
            // DW_AT_inline
374
            default:
375
                pst_log(SEVERITY_WARNING, "Unknown TAG of function: 0x%X", dwarf_tag(&result));
376
                break;
377
        }
378
    } while(dwarf_siblingof(&result, &result) == 0);
379

380
    return true;
381
}
382

383
bool function_unwind(pst_function* fn)
384
{
385
    Dwfl_Line *dwline = dwfl_getsrc(fn->ctx->dwfl, fn->info.pc);
386
    if(dwline != NULL) {
387
        const char* filename = dwfl_lineinfo (dwline, &fn->info.pc, &fn->info.line, NULL, NULL, NULL);
388
        if(filename) {
389
            const char* file = strrchr(filename, '/');
390
            if(file && *file != 0) {
391
                file++;
392
            } else {
393
                file = filename;
394
            }
395
            fn->info.file = pst_strdup(file);
396
        }
397
    }
398

399
    Dwfl_Module* module = dwfl_addrmodule(fn->ctx->dwfl, fn->info.pc);
400
    const char* addrname = dwfl_module_addrname(module, fn->info.pc);
401
    if(addrname) {
402
        char* demangle_name = cplus_demangle(addrname, 0);
403
        char* function_name = NULL;
404
        if(asprintf(&function_name, "%s%s", demangle_name ? demangle_name : addrname, demangle_name ? "" : "()") == -1) {
405
            pst_log(SEVERITY_ERROR, "Failed to allocate memory");
406
            return false;
407
        }
408

409
        char* str = strchr(function_name, '(');
410
        if(str) {
411
            *str = 0;
412
        }
413
        fn->info.name = pst_strdup(function_name);
414
        free(function_name);
415

416
        if(demangle_name) {
417
            free(demangle_name);
418
        }
419
    }
420

421
    return true;
422
}
423

424
void pst_function_init(pst_function* fn, pst_context* _ctx, pst_function* _parent)
425
{
426
    list_node_init(&fn->node);
427

428
    // user visible fields
429
    fn->info.pc = 0;
430
    fn->info.lowpc = 0;
431
    fn->info.highpc = 0;
432
    fn->info.name = NULL;
433
    fn->info.line = -1;
434
    fn->info.file = NULL;
435
    fn->info.sp = 0;
436
    fn->info.flags = 0;
437

438
    // internal fields
439
    bzero(&fn->context, sizeof(fn->context));
440
    fn->die = NULL;
441
    list_head_init(&fn->params);
442
    pst_call_site_storage_init(&fn->call_sites, _ctx);
443

444
    memcpy(&fn->context, &_ctx->cursor, sizeof(fn->context));
445
    fn->parent = _parent;
446
    fn->frame = NULL;
447
    fn->ctx = _ctx;
448
    fn->allocated = false;
449
}
450

451
pst_function* pst_function_new(pst_context* _ctx, pst_function* _parent)
452
{
453
    pst_function* fn = pst_alloc(pst_function);
454
    if(fn) {
455
        pst_function_init(fn, _ctx, _parent);
456
        fn->allocated = true;
457
    }
458

459
    return fn;
460
}
461

462
void pst_function_fini(pst_function* fn)
463
{
464
    clear(fn);
465

466
    if(fn->frame) {
467
        // use free() here because it was allocated out of our control by libdw
468
        free(fn->frame);
469
    }
470

471
    if(fn->info.name) {
472
        pst_free(fn->info.name);
473
    }
474

475
    if(fn->info.file) {
476
        pst_free(fn->info.file);
477
    }
478

479
    if(fn->allocated) {
480
        pst_free(fn);
481
    }
482
}
483

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

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

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

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