summaryrefslogtreecommitdiff
path: root/src/core_dump_handler/dlt_cdh_crashid.c
blob: c2d81e4358d20b233dba3d51f7c814c8ac824c69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
 * SPDX license identifier: MPL-2.0
 *
 * Copyright (C) 2011-2015, BMW AG
 *
 * This file is part of GENIVI Project DLT - Diagnostic Log and Trace.
 *
 * This Source Code Form is subject to the terms of the
 * Mozilla Public License (MPL), v. 2.0.
 * If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * For further information see http://www.genivi.org/.
 */

/*!
 * \author Magneti Marelli http://www.magnetimarelli.com
 * \author Lutz Helwing <lutz_helwing@mentor.com>
 *
 * \copyright Copyright © 2011-2015 BMW AG. \n
 * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
 *
 * \file dlt_cdh_crashid.c
 */

#include <stdlib.h>
#include <syslog.h>
#include <sys/procfs.h>
#include <sys/user.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <asm/prctl.h>
#include <inttypes.h>

#include "dlt_cdh.h"
#include "dlt_cdh_cpuinfo.h"

#ifdef HAS_CITYHASH_C
#   include "city_c.h"
#endif

/*ARM32 specific */
/*#define REG_FRAME_POINTER   11 */
/*#define REG_INSTR_POINTER   12 */
/*#define REG_STACK_POINTER   13 */
/*#define REG_LINK_REGISTER   14 */
/*#define REG_PROC_COUNTER    15 */

#ifdef HAS_CITYHASH_C
static cdh_status_t crashid_cityhash(proc_info_t *p_proc);
#endif

cdh_status_t get_phdr_num(proc_info_t *p_proc, unsigned int p_address, int *phdr_num)
{
    int i = 0;

    if (phdr_num == NULL)
        return CDH_NOK;

    for (i = 0; i < p_proc->m_Ehdr.e_phnum; i++)
        if ((p_proc->m_pPhdr[i].p_vaddr < p_address)
            && (p_proc->m_pPhdr[i].p_vaddr + p_proc->m_pPhdr[i].p_memsz > p_address)) {
            *phdr_num = i;
            return CDH_OK;
        }

    *phdr_num = -1;

    return CDH_NOK;
}

/* Thanks to libunwind for the following definitions, which helps to */
#define ALIGN(x, a) (((x) + (a) - 1UL) & ~((a) - 1UL))
#define NOTE_SIZE(_hdr) (sizeof (_hdr) + ALIGN((_hdr).n_namesz, 4) + (_hdr).n_descsz)

cdh_status_t get_crashed_registers(proc_info_t *p_proc)
{
    int found = CDH_NOK; /* CDH_OK, when we find the page note associated to PID of crashed process */
    unsigned int offset = 0;

    /* TODO: if no notes were found m_note_page_size was not set to 0 which leads to a crash in this loop because it is then used */
    /* uninitialised here => this is an x86_64 issue */
    while (found != CDH_OK && offset < p_proc->m_note_page_size) {
        /* Crash mentioned in TODO dlt_cdh_coredump.c line 163 */
        ELF_Nhdr *ptr_note = (ELF_Nhdr *)(p_proc->m_Nhdr + offset);

        if (ptr_note->n_type == NT_PRSTATUS) {
            /* The first PRSTATUS note is the one of the crashed thread */
            prstatus_t *prstatus = (prstatus_t *)((char *)ptr_note + sizeof(ELF_Nhdr) + ALIGN(ptr_note->n_namesz, 4));

            p_proc->m_crashed_pid = prstatus->pr_pid;

            get_registers(prstatus, &p_proc->m_registers);
            found = CDH_OK;
        }

        offset += NOTE_SIZE(*ptr_note);
    }

    return found;
}

#ifdef HAS_CITYHASH_C

cdh_status_t crashid_cityhash(proc_info_t *p_proc)
{
#   define CRASHID_BUF_SIZE         MAX_PROC_NAME_LENGTH + sizeof(uint64_t)

    char cityhash_in[CRASHID_BUF_SIZE];
    uint64_t cityhash_result = 0;
    memcpy(cityhash_in, p_proc->name, MAX_PROC_NAME_LENGTH);
    memcpy(cityhash_in + MAX_PROC_NAME_LENGTH, &p_proc->m_crashid_phase1, sizeof(uint64_t));

    cityhash_result = CityHash64(cityhash_in, CRASHID_BUF_SIZE);
    memcpy(p_proc->m_crashid, &cityhash_result, sizeof(uint64_t));

    return CDH_OK;
#   undef CRASHID_BUF_SIZE
}

#endif /* HAS_CITYHASH_C */

cdh_status_t create_crashid(proc_info_t *p_proc)
{
    uint32_t final_lr = 0;
    uint32_t final_pc = 0;
    int pc_phnum = 0;
    int lr_phnum = 0;

    /* translate address from virtual address (process point of view) to offset in the stack memory page */
#define ADDRESS_REBASE(__x, __phdr_num)               (__x - p_proc->m_pPhdr[__phdr_num].p_vaddr)
    /* read value in the stack at position offset: +/- sizeof(), depends on stack growing upward or downward */
#define READ_STACK_VALUE(__offset, __type)  (*(__type *)(stack_page + __offset - sizeof(__type)))

    get_phdr_num(p_proc, p_proc->m_registers.pc, &pc_phnum);
    final_pc = ADDRESS_REBASE(p_proc->m_registers.pc, pc_phnum);

    get_phdr_num(p_proc, p_proc->m_registers.lr, &lr_phnum);

    if (lr_phnum >= 0)
        final_lr = ADDRESS_REBASE(p_proc->m_registers.lr, lr_phnum);

    p_proc->m_crashid_phase1 = p_proc->signal << 24;
    p_proc->m_crashid_phase1 |= (uint64_t)final_lr;
    p_proc->m_crashid_phase1 <<= 32;
    p_proc->m_crashid_phase1 |= (uint64_t)final_pc;

#ifdef HAS_CITYHASH_C
    crashid_cityhash(p_proc);
#else
    memcpy(p_proc->m_crashid, &p_proc->m_crashid_phase1, sizeof(uint64_t));
#endif

    syslog(LOG_INFO,
           "Crash in \"%s\", thread=\"%s\", pid=%d, crashID=%" PRIx64 ", based on signal=%d, PC=0x%x, caller=0x%x",
           p_proc->name,
           p_proc->threadname,
           p_proc->pid,
           *((uint64_t *)p_proc->m_crashid),
           p_proc->signal,
           final_pc, final_lr
           );

    return CDH_OK;
}

int write_crashid_to_filesystem(proc_info_t *p_proc)
{
    FILE *crashid_file = NULL;

    if ((crashid_file = fopen(CRASHID_FILE, "wt")) == NULL) {
        syslog(LOG_ERR, "(pid=%d) cannot write crashid to %s: %s", p_proc->pid, CRASHID_FILE, strerror(errno));
        return CDH_NOK;
    }

    fprintf(crashid_file, "%" PRIx64, *(uint64_t *)p_proc->m_crashid);
    fclose(crashid_file);

    return CDH_OK;
}

cdh_status_t treat_crash_data(proc_info_t *p_proc)
{
    if (get_crashed_registers(p_proc) != CDH_OK) {
        syslog(LOG_ERR, "registers not found in notes");
        return CDH_NOK;
    }

    if (create_crashid(p_proc) != CDH_OK) {
        syslog(LOG_ERR, "crashid not generated");
        return CDH_NOK;
    }

    write_crashid_to_filesystem(p_proc);

    return CDH_OK;
}