Skip to content

Commit

Permalink
修复日志打印调用栈问题 (oceanbase#482)
Browse files Browse the repository at this point in the history
### What problem were solved in this pull request?

Issue Number: close oceanbase#381

Problem:
当前lbt输出的信息无法直接使用addr2line获取栈信息。

### What is changed and how it works?
使用backtrace调用获取到栈信息偏移量时,通过/proc/self/maps减去当前区间的起始地址,然后输出。
  • Loading branch information
hnwyllmm authored Nov 15, 2024
1 parent d0bae1b commit 7c0137f
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 38 deletions.
145 changes: 145 additions & 0 deletions deps/common/log/backtrace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by WangYunlai on 2024
//

#include <stdint.h>
#include <stdio.h>
#include <execinfo.h>
#include <inttypes.h>
#include "common/lang/vector.h"

namespace common {

struct ProcMapSegment
{
uint64_t start_address;
uint64_t end_address;
};

class ProcMap
{
public:
ProcMap() = default;
~ProcMap() = default;

int parse();
int parse_file(const char *file_name);

const ProcMapSegment *get_segment(const uint64_t address) const;

uint64_t get_offset(const uint64_t address) const;

private:
vector<ProcMapSegment> segments_;
};

int ProcMap::parse()
{
const char *file_name = "/proc/self/maps";
return parse_file(file_name);
}

int ProcMap::parse_file(const char *file_name)
{
FILE *fp = fopen(file_name, "r");
if (fp == nullptr) {
return -1;
}

ProcMapSegment segment;
char line[1024] = {0};
uint64_t start, end, inode, offset, major, minor;
char perms[8];
char path[257];

while (fgets(line, sizeof(line), fp) != nullptr) {

int ret = sscanf(line, "%" PRIx64 "-%" PRIx64 " %4s %" PRIx64 " %" PRIx64 ":%" PRIx64 "%" PRIu64 "%255s",
&start, &end, perms, &offset, &major, &minor, &inode, path);
if (ret < 8 || perms[2] != 'x') {
continue;
}

segment.start_address = start;
segment.end_address = end;

segments_.push_back(segment);
}

fclose(fp);
return 0;
}

const ProcMapSegment *ProcMap::get_segment(const uint64_t address) const
{
for (const auto &segment : segments_) {
if (address >= segment.start_address && address < segment.end_address) {
return &segment;
}
}

return nullptr;
}

uint64_t ProcMap::get_offset(const uint64_t address) const
{
const ProcMapSegment *segment = get_segment(address);
if (segment == nullptr) {
return address;
}

return address - segment->start_address;
}

//////////////////////////////////////////////////////////////////////////
static ProcMap g_proc_map;
int backtrace_init()
{
return g_proc_map.parse();
}

const char *lbt()
{
constexpr int buffer_size = 100;
void *buffer[buffer_size];

constexpr int bt_buffer_size = 8192;
thread_local char backtrace_buffer[bt_buffer_size];

int size = backtrace(buffer, buffer_size);

char **symbol_array = nullptr;
#ifdef LBT_SYMBOLS
/* 有些环境下,使用addr2line 无法根据地址输出符号 */
symbol_array = backtrace_symbols(buffer, size);
#endif // LBT_SYMBOLS

int offset = 0;
for (int i = 0; i < size && offset < bt_buffer_size - 1; i++) {
uint64_t address = reinterpret_cast<uint64_t>(buffer[i]);
address = g_proc_map.get_offset(address);
const char *format = (0 == i) ? "0x%lx" : " 0x%lx";
offset += snprintf(backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, format, address);

if (symbol_array != nullptr) {
offset += snprintf(backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, " %s", symbol_array[i]);
}
}

if (symbol_array != nullptr) {
free(symbol_array);
}
return backtrace_buffer;
}

} // namespace common
23 changes: 23 additions & 0 deletions deps/common/log/backtrace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by WangYunlai on 2024
//

#pragma once

namespace common {

int backtrace_init();

const char *lbt();

} // namespace common
37 changes: 3 additions & 34 deletions deps/common/log/log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ See the Mulan PSL v2 for more details. */
//

#include <assert.h>
#include <execinfo.h>
#include <stdarg.h>
#include <stdio.h>

Expand All @@ -22,6 +21,8 @@ See the Mulan PSL v2 for more details. */
#include "common/lang/iostream.h"
#include "common/lang/new.h"
#include "common/log/log.h"
#include "common/log/backtrace.h"

namespace common {

Log *g_log = nullptr;
Expand Down Expand Up @@ -347,40 +348,8 @@ int LoggerFactory::init_default(
return 0;
}

backtrace_init();
return init(log_file, &g_log, log_level, console_level, rotate_type);
}

const char *lbt()
{
constexpr int buffer_size = 100;
void *buffer[buffer_size];

constexpr int bt_buffer_size = 8192;
thread_local char backtrace_buffer[bt_buffer_size];

int size = backtrace(buffer, buffer_size);

char **symbol_array = nullptr;
#ifdef LBT_SYMBOLS
/* 有些环境下,使用addr2line 无法根据地址输出符号 */
symbol_array = backtrace_symbols(buffer, size);
#endif // LBT_SYMBOLS

int offset = 0;
for (int i = 0; i < size && offset < bt_buffer_size - 1; i++) {
const char *format = (0 == i) ? "0x%lx" : " 0x%lx";
offset += snprintf(
backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, format, reinterpret_cast<intptr_t>(buffer[i]));

if (symbol_array != nullptr) {
offset += snprintf(backtrace_buffer + offset, sizeof(backtrace_buffer) - offset, " %s", symbol_array[i]);
}
}

if (symbol_array != nullptr) {
free(symbol_array);
}
return backtrace_buffer;
}

} // namespace common
39 changes: 36 additions & 3 deletions docs/docs/dev-env/miniob-how-to-debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ Table::scan_record
Table::create_index
```
### 打印日志调试
## 打印日志调试
miniob提供的日志接口
### miniob提供的日志接口
```c++
deps/common/log/log.h:
Expand All @@ -81,7 +81,40 @@ LOG_FILE_LEVEL=5
LOG_CONSOLE_LEVEL=1
```

### gdb调试
### 在日志中输出调用栈

`lbt` 函数可以获取当前调用栈,可以在日志中输出调用栈,方便定位问题。

比如
```c++
LOG_DEBUG("debug lock %p, lbt=%s", &lock_, lbt());
```
可能得到下面的日志
```
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
```
可以用`addr2line`工具来解析地址,比如
```bash
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
?? ??:0
common::lbt() at /root/miniob/deps/common/log/backtrace.cpp:118
common::DebugMutex::unlock() at /root/miniob/deps/common/lang/mutex.cpp:273 (discriminator 25)
Frame::write_unlatch(long) at /root/miniob/src/observer/storage/buffer/frame.cpp:113
Frame::write_unlatch() at /root/miniob/src/observer/storage/buffer/frame.cpp:88
RecordPageHandler::cleanup() at /root/miniob/src/observer/storage/record/record_manager.cpp:262
RecordPageHandler::~RecordPageHandler() at /root/miniob/src/observer/storage/record/record_manager.cpp:96
RowRecordPageHandler::~RowRecordPageHandler() at /root/miniob/src/observer/storage/record/record_manager.h:280
RowRecordPageHandler::~RowRecordPageHandler() at /root/miniob/src/observer/storage/record/record_manager.h:280
...
```

> 注意:./bin/observer 是你的可执行文件路径,这里是一个示例,实际路径可能不同。
## gdb调试

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

Expand Down
1 change: 1 addition & 0 deletions src/observer/storage/buffer/disk_buffer_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ RC DiskBufferPool::allocate_page(Frame **frame)
hdr_frame_->set_lsn(lsn);

LOG_DEBUG("allocate a new page without extend buffer pool. page num=%d, buffer pool=%d", i, id());

lock_.unlock();
return get_this_page(i, frame);
}
Expand Down
2 changes: 1 addition & 1 deletion src/observer/storage/buffer/frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,6 @@ string Frame::to_string() const
{
stringstream ss;
ss << "frame id:" << frame_id().to_string() << ", dirty=" << dirty() << ", pin=" << pin_count()
<< ", lsn=" << lsn();
<< ", lsn=" << lsn() << ", this=" << this;
return ss.str();
}

0 comments on commit 7c0137f

Please sign in to comment.