Skip to content

Commit 7c0137f

Browse files
authored
修复日志打印调用栈问题 (#482)
### What problem were solved in this pull request? Issue Number: close #381 Problem: 当前lbt输出的信息无法直接使用addr2line获取栈信息。 ### What is changed and how it works? 使用backtrace调用获取到栈信息偏移量时,通过/proc/self/maps减去当前区间的起始地址,然后输出。
1 parent d0bae1b commit 7c0137f

File tree

6 files changed

+209
-38
lines changed

6 files changed

+209
-38
lines changed

deps/common/log/backtrace.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
2+
miniob is licensed under Mulan PSL v2.
3+
You can use this software according to the terms and conditions of the Mulan PSL v2.
4+
You may obtain a copy of Mulan PSL v2 at:
5+
http://license.coscl.org.cn/MulanPSL2
6+
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
7+
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
8+
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9+
See the Mulan PSL v2 for more details. */
10+
11+
//
12+
// Created by WangYunlai on 2024
13+
//
14+
15+
#include <stdint.h>
16+
#include <stdio.h>
17+
#include <execinfo.h>
18+
#include <inttypes.h>
19+
#include "common/lang/vector.h"
20+
21+
namespace common {
22+
23+
struct ProcMapSegment
24+
{
25+
uint64_t start_address;
26+
uint64_t end_address;
27+
};
28+
29+
class ProcMap
30+
{
31+
public:
32+
ProcMap() = default;
33+
~ProcMap() = default;
34+
35+
int parse();
36+
int parse_file(const char *file_name);
37+
38+
const ProcMapSegment *get_segment(const uint64_t address) const;
39+
40+
uint64_t get_offset(const uint64_t address) const;
41+
42+
private:
43+
vector<ProcMapSegment> segments_;
44+
};
45+
46+
int ProcMap::parse()
47+
{
48+
const char *file_name = "/proc/self/maps";
49+
return parse_file(file_name);
50+
}
51+
52+
int ProcMap::parse_file(const char *file_name)
53+
{
54+
FILE *fp = fopen(file_name, "r");
55+
if (fp == nullptr) {
56+
return -1;
57+
}
58+
59+
ProcMapSegment segment;
60+
char line[1024] = {0};
61+
uint64_t start, end, inode, offset, major, minor;
62+
char perms[8];
63+
char path[257];
64+
65+
while (fgets(line, sizeof(line), fp) != nullptr) {
66+
67+
int ret = sscanf(line, "%" PRIx64 "-%" PRIx64 " %4s %" PRIx64 " %" PRIx64 ":%" PRIx64 "%" PRIu64 "%255s",
68+
&start, &end, perms, &offset, &major, &minor, &inode, path);
69+
if (ret < 8 || perms[2] != 'x') {
70+
continue;
71+
}
72+
73+
segment.start_address = start;
74+
segment.end_address = end;
75+
76+
segments_.push_back(segment);
77+
}
78+
79+
fclose(fp);
80+
return 0;
81+
}
82+
83+
const ProcMapSegment *ProcMap::get_segment(const uint64_t address) const
84+
{
85+
for (const auto &segment : segments_) {
86+
if (address >= segment.start_address && address < segment.end_address) {
87+
return &segment;
88+
}
89+
}
90+
91+
return nullptr;
92+
}
93+
94+
uint64_t ProcMap::get_offset(const uint64_t address) const
95+
{
96+
const ProcMapSegment *segment = get_segment(address);
97+
if (segment == nullptr) {
98+
return address;
99+
}
100+
101+
return address - segment->start_address;
102+
}
103+
104+
//////////////////////////////////////////////////////////////////////////
105+
static ProcMap g_proc_map;
106+
int backtrace_init()
107+
{
108+
return g_proc_map.parse();
109+
}
110+
111+
const char *lbt()
112+
{
113+
constexpr int buffer_size = 100;
114+
void *buffer[buffer_size];
115+
116+
constexpr int bt_buffer_size = 8192;
117+
thread_local char backtrace_buffer[bt_buffer_size];
118+
119+
int size = backtrace(buffer, buffer_size);
120+
121+
char **symbol_array = nullptr;
122+
#ifdef LBT_SYMBOLS
123+
/* 有些环境下,使用addr2line 无法根据地址输出符号 */
124+
symbol_array = backtrace_symbols(buffer, size);
125+
#endif // LBT_SYMBOLS
126+
127+
int offset = 0;
128+
for (int i = 0; i < size && offset < bt_buffer_size - 1; i++) {
129+
uint64_t address = reinterpret_cast<uint64_t>(buffer[i]);
130+
address = g_proc_map.get_offset(address);
131+
const char *format = (0 == i) ? "0x%lx" : " 0x%lx";
132+
offset += snprintf(backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, format, address);
133+
134+
if (symbol_array != nullptr) {
135+
offset += snprintf(backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, " %s", symbol_array[i]);
136+
}
137+
}
138+
139+
if (symbol_array != nullptr) {
140+
free(symbol_array);
141+
}
142+
return backtrace_buffer;
143+
}
144+
145+
} // namespace common

deps/common/log/backtrace.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
2+
miniob is licensed under Mulan PSL v2.
3+
You can use this software according to the terms and conditions of the Mulan PSL v2.
4+
You may obtain a copy of Mulan PSL v2 at:
5+
http://license.coscl.org.cn/MulanPSL2
6+
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
7+
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
8+
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9+
See the Mulan PSL v2 for more details. */
10+
11+
//
12+
// Created by WangYunlai on 2024
13+
//
14+
15+
#pragma once
16+
17+
namespace common {
18+
19+
int backtrace_init();
20+
21+
const char *lbt();
22+
23+
} // namespace common

deps/common/log/log.cpp

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ See the Mulan PSL v2 for more details. */
1313
//
1414

1515
#include <assert.h>
16-
#include <execinfo.h>
1716
#include <stdarg.h>
1817
#include <stdio.h>
1918

@@ -22,6 +21,8 @@ See the Mulan PSL v2 for more details. */
2221
#include "common/lang/iostream.h"
2322
#include "common/lang/new.h"
2423
#include "common/log/log.h"
24+
#include "common/log/backtrace.h"
25+
2526
namespace common {
2627

2728
Log *g_log = nullptr;
@@ -347,40 +348,8 @@ int LoggerFactory::init_default(
347348
return 0;
348349
}
349350

351+
backtrace_init();
350352
return init(log_file, &g_log, log_level, console_level, rotate_type);
351353
}
352354

353-
const char *lbt()
354-
{
355-
constexpr int buffer_size = 100;
356-
void *buffer[buffer_size];
357-
358-
constexpr int bt_buffer_size = 8192;
359-
thread_local char backtrace_buffer[bt_buffer_size];
360-
361-
int size = backtrace(buffer, buffer_size);
362-
363-
char **symbol_array = nullptr;
364-
#ifdef LBT_SYMBOLS
365-
/* 有些环境下,使用addr2line 无法根据地址输出符号 */
366-
symbol_array = backtrace_symbols(buffer, size);
367-
#endif // LBT_SYMBOLS
368-
369-
int offset = 0;
370-
for (int i = 0; i < size && offset < bt_buffer_size - 1; i++) {
371-
const char *format = (0 == i) ? "0x%lx" : " 0x%lx";
372-
offset += snprintf(
373-
backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, format, reinterpret_cast<intptr_t>(buffer[i]));
374-
375-
if (symbol_array != nullptr) {
376-
offset += snprintf(backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, " %s", symbol_array[i]);
377-
}
378-
}
379-
380-
if (symbol_array != nullptr) {
381-
free(symbol_array);
382-
}
383-
return backtrace_buffer;
384-
}
385-
386355
} // namespace common

docs/docs/dev-env/miniob-how-to-debug.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ Table::scan_record
5252
Table::create_index
5353
```
5454
55-
### 打印日志调试
55+
## 打印日志调试
5656
57-
miniob提供的日志接口
57+
### miniob提供的日志接口
5858
5959
```c++
6060
deps/common/log/log.h:
@@ -81,7 +81,40 @@ LOG_FILE_LEVEL=5
8181
LOG_CONSOLE_LEVEL=1
8282
```
8383

84-
### gdb调试
84+
### 在日志中输出调用栈
85+
86+
`lbt` 函数可以获取当前调用栈,可以在日志中输出调用栈,方便定位问题。
87+
88+
比如
89+
```c++
90+
LOG_DEBUG("debug lock %p, lbt=%s", &lock_, lbt());
91+
```
92+
93+
可能得到下面的日志
94+
95+
```
96+
unlock@mutex.cpp:273] >> debug unlock 0xffffa40fe8c0, lbt=0x4589c 0x5517f8 0x5329e0 0x166308 0x162c2c 0x210438 0x204ee0 0x2373b0 0x2373d0 0x203efc 0x203d88 0x223f6c 0x242fc8 0x274810 0x32bd58 0xca028 0x2dbcf8 0x2da614 0xbbf30 0x2cb908 0x2d4408 0x2d43dc 0x2d435c 0x2d431c 0x2d42d4 0x2d4270 0x2d4244 0x2d4224 0xd31fc 0x7d5c8 0xe5edc
97+
```
98+
99+
可以用`addr2line`工具来解析地址,比如
100+
101+
```bash
102+
addr2line -pCfe ./bin/observer 0x4589c 0x5517f8 0x5329e0 0x166308 0x162c2c 0x210438 0x204ee0 0x2373b0 0x2373d0 0x203efc 0x203d88 0x223f6c 0x242fc8 0x274810 0x32bd58 0xca028 0x2dbcf8 0x2da614 0xbbf30 0x2cb908 0x2d4408 0x2d43dc 0x2d435c 0x2d431c 0x2d42d4 0x2d4270 0x2d4244 0x2d4224 0xd31fc 0x7d5c8 0xe5edc
103+
?? ??:0
104+
common::lbt() at /root/miniob/deps/common/log/backtrace.cpp:118
105+
common::DebugMutex::unlock() at /root/miniob/deps/common/lang/mutex.cpp:273 (discriminator 25)
106+
Frame::write_unlatch(long) at /root/miniob/src/observer/storage/buffer/frame.cpp:113
107+
Frame::write_unlatch() at /root/miniob/src/observer/storage/buffer/frame.cpp:88
108+
RecordPageHandler::cleanup() at /root/miniob/src/observer/storage/record/record_manager.cpp:262
109+
RecordPageHandler::~RecordPageHandler() at /root/miniob/src/observer/storage/record/record_manager.cpp:96
110+
RowRecordPageHandler::~RowRecordPageHandler() at /root/miniob/src/observer/storage/record/record_manager.h:280
111+
RowRecordPageHandler::~RowRecordPageHandler() at /root/miniob/src/observer/storage/record/record_manager.h:280
112+
...
113+
```
114+
115+
> 注意:./bin/observer 是你的可执行文件路径,这里是一个示例,实际路径可能不同。
116+
117+
## gdb调试
85118

86119
调试工具有很多种,但是它们的关键点都是类似的,比如关联到进程、运行时查看变量值、单步运行、跟踪变量等。GDB是在Linux环境中常用的调试工具。其它环境上也有类似的工具,比如LLDB,或者Windows可能使用Visual Studio直接启动调试。Java的调试工具是jdb。
87120

src/observer/storage/buffer/disk_buffer_pool.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ RC DiskBufferPool::allocate_page(Frame **frame)
375375
hdr_frame_->set_lsn(lsn);
376376

377377
LOG_DEBUG("allocate a new page without extend buffer pool. page num=%d, buffer pool=%d", i, id());
378+
378379
lock_.unlock();
379380
return get_this_page(i, frame);
380381
}

src/observer/storage/buffer/frame.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,6 @@ string Frame::to_string() const
262262
{
263263
stringstream ss;
264264
ss << "frame id:" << frame_id().to_string() << ", dirty=" << dirty() << ", pin=" << pin_count()
265-
<< ", lsn=" << lsn();
265+
<< ", lsn=" << lsn() << ", this=" << this;
266266
return ss.str();
267267
}

0 commit comments

Comments
 (0)