为什么主题行出现分段错误



我正在为 RocksDB 创建一个本机节点扩展,我已经确定了一个我无法解释的问题。所以我有以下功能完美的代码段:

std::string v;
ROCKSDB_STATUS_THROWS(db->Get(*options, k, &v));
napi_value result;
NAPI_STATUS_THROWS(napi_create_buffer_copy(env, v.size(), v.c_str(), nullptr, &result));
return result;

但是当我引入一个优化来减少一个额外的memcpy时,我得到段错误:

std::string *v = new std::string();
ROCKSDB_STATUS_THROWS(db->Get(*options, k, v)); // <============= I get segfaults here
napi_value result;
NAPI_STATUS_THROWS(napi_create_external_buffer(env, v->size(), (void *)v->c_str(), rocksdb_get_finalize, v, &result));
return result;

下面是Get方法签名:

rocksdb::Status rocksdb::DB::Get(const rocksdb::ReadOptions &options, const rocksdb::Slice &key, std::string *value)

有什么想法为什么会发生这个问题?

提前谢谢你!

编辑

可以肯定的是,我还检查了以下版本(它也失败了(:

std::string *v = new std::string();
ROCKSDB_STATUS_THROWS(db->Get(*options, k, v));
napi_value result;
NAPI_STATUS_THROWS(napi_create_buffer_copy(env, v->size(), v->c_str(), nullptr, &result));
delete v;

编辑

根据评论中的要求提供更完整的示例:

#include <napi-macros.h>
#include <node_api.h>
#include <rocksdb/db.h>
#include <rocksdb/convenience.h>
#include <rocksdb/write_batch.h>
#include <rocksdb/cache.h>
#include <rocksdb/filter_policy.h>
#include <rocksdb/cache.h>
#include <rocksdb/comparator.h>
#include <rocksdb/env.h>
#include <rocksdb/options.h>
#include <rocksdb/table.h>
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
...
/**
* Runs when a rocksdb_get return value instance is garbage collected.
*/
static void rocksdb_get_finalize(napi_env env, void *data, void *hint)
{
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get_finalize (started)";
if (hint)
{
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get_finalize (finished)";
delete (std::string *)hint;
}
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get_finalize (finished)";
}
/**
* Gets key / value pair from a database.
*/
NAPI_METHOD(rocksdb_get)
{
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (started)";
NAPI_ARGV(3);
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (getting db argument)";
rocksdb::DB *DECLARE_FROM_EXTERNAL_ARGUMENT(0, db);
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (getting k argument)";
DECLARE_SLICE_FROM_BUFFER_ARGUMENT(1, k);
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (getting options argument)";
rocksdb::ReadOptions *DECLARE_FROM_EXTERNAL_ARGUMENT(2, options);
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (declaring v variable)";
std::string *v = new std::string();
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (getting value from database)";
ROCKSDB_STATUS_THROWS(db->Get(*options, k, v));
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (wrapping value with js wrapper)";
napi_value result;
NAPI_STATUS_THROWS(napi_create_external_buffer(env, v->size(), (void *)v->c_str(), rocksdb_get_finalize, v, &result));
LOG_IF(logging_enabled, INFO) << LOCATION << " rocksdb_get (finished)";
return result;
}

启动上述方法的代码在 TypeScript 中实现,在 NodeJS 中运行,以下是完整列表:

import path from 'path';
import { bindings as rocks, Unique, BatchContext } from 'rocksdb';
import { MapOf } from '../types';
import { Command, CommandOptions, CommandOptionDeclaration, Persist, CommandEnvironment } from '../command';
// tslint:disable-next-line: no-empty-interface
export interface PullCommandOptions {
}
@Command
export class ExampleCommandNameCommand implements Command {
public get description(): string {
return "[An example command description]";
}
public get options(): CommandOptions<CommandOptionDeclaration> {
const result: MapOf<PullCommandOptions, CommandOptionDeclaration> = new Map();
return result;
}
public async run(environment: CommandEnvironment, opts: CommandOptions<unknown>): Promise<void> {
// let options = opts as unknown as PullCommandOptions;
let window = global as any;
window.rocks = rocks;
const configPath = path.resolve('log.conf');
const configPathBuffer = Buffer.from(configPath);
rocks.logger_config(configPathBuffer);
rocks.logger_start();
let db = window.db = rocks.rocksdb_open(Buffer.from('test.db', 'utf-8'), rocks.rocksdb_options_init());
let readOptions = window.readOptions = rocks.rocksdb_read_options_init();
let writeOptions = window.writeOptions = rocks.rocksdb_write_options_init();
// ===== The line below launches the C++ method
rocks.rocksdb_put(db, Buffer.from('Zookie'), Buffer.from('Cookie'), writeOptions);
// ===== The line above launches the C++ method
console.log(rocks.rocksdb_get(db, Buffer.from('Zookie'), readOptions).toString());
let batch: Unique<BatchContext> | null = rocks.rocksdb_batch_init();
rocks.rocksdb_batch_put(batch, Buffer.from('Cookie'), Buffer.from('Zookie'));
rocks.rocksdb_batch_put(batch, Buffer.from('Pookie'), Buffer.from('Zookie'));
rocks.rocksdb_batch_put(batch, Buffer.from('Zookie'), Buffer.from('Zookie'));
rocks.rocksdb_batch_put(batch, Buffer.from('Hookie'), Buffer.from('Zookie'));
await rocks.rocksdb_batch_write_async(db, batch, writeOptions);
batch = null;
let proceed = true;
while (proceed) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}

基本上,这段代码代表了KeyValueDatabase->Get("Some key"(方法的实现,您将一个字符串传递到其中,您将获得一个字符串作为回报。但很明显,问题是围绕new std::string()电话跳舞,我想我可能会得到一些解释,为什么这样做不好?如何在没有副本的情况下将字符串值从一个字符串移动到另一个字符串?

但是当我引入一个优化来减少一个额外的内存时

目前还不清楚您认为您正在优化哪些额外的memcpy

如果字符串很短,并且您正在使用std::string进行短字符串优化,那么您确实会优化出短memcpy。但是,动态分配然后删除std::string可能比memcpy昂贵得多。

如果字符串很长,你实际上根本不优化任何内容,而是无缘无故地使代码变慢。

我得到段错误:

添加v = new std::string; ... ; delete v;引入SIGSEGV这一事实可能表明您还有其他一些堆损坏,在您稍微改变之前,这些损坏仍然不会被注意到。瓦尔格林德是你的朋友。

相关内容

  • 没有找到相关文章

最新更新