NodeJS C++插件开发(四) - Visual Studio Code调试C++插件教程

663 阅读3分钟

NodeJS C++插件开发(四) - Visual Studio Code调试C++插件教程


如需转载请标明出处
QQ技术交流群:129518033

文章目录

相关阅读:
NodeJS C++插件开发(一) - 简介
NodeJS C++插件开发(二) - 示例代码hello world

NodeJS C++插件开发(三) - 调试概述

前言

NodeJS C++插件开发(三) - 调试概述中介绍了几种NodeJS C++插件的调试方法,本文将以第三方库的C++插件的代码示例详细介绍如何使用Visual Studio Code调试C++插件。

1. 安装必要环境

  • 安装NodeJS(14.16.0)
  • 安装node-gyp依赖-python
  • 安装node-gyp依赖-编译器
    windows:Microsoft Visual C++ (MSVC) (Visual Studio 2019)
    linux: gcc g++ gdb make
  • 安装node-gyp (npm i -g node-gyp)
  • 安装 Visual Studio Code
  • 安装 Visual Studio Code调试插件C/C++ extension for VS Code

2. 测试代码

目录结构:

code
+--- .vscode
|   +--- launch.json
+--- addon.cpp
+--- binding.gyp
+--- common.h
+--- index.js
+--- thirdlib
|   +--- CMakeLists.txt
|   +--- myadd.cc
|   +--- myadd.h
  • 第三方库代码

myadd.h

// myadd.h

#if defined(_WIN32)
#define DLL_EXPORT __declspec(dllexport) ///< define DLL_EXPORT windows
#else
#define DLL_EXPORT __attribute__((visibility("default"))) ///< define DLL_EXPORT unix 
#endif

int DLL_EXPORT myAdd(int x, int y);

myadd.cc

// myadd.cc

#include "myadd.h"

int myAdd(int x, int y)
{
	return x + y;
}
  • NodeJS C+++插件代码
    common.h
#include <js_native_api.h>

// node-v12.13.0/test/js-native-api/common.h

// Empty value so that macros here are able to return NULL or void
#define NAPI_RETVAL_NOTHING  // Intentionally blank #define

#define GET_AND_THROW_LAST_ERROR(env)                                    \
  do {                                                                   \
    const napi_extended_error_info *error_info;                          \
    napi_get_last_error_info((env), &error_info);                        \
    bool is_pending;                                                     \
    napi_is_exception_pending((env), &is_pending);                       \
    /* If an exception is already pending, don't rethrow it */           \
    if (!is_pending) {                                                   \
      const char* error_message = error_info->error_message != NULL ?    \
        error_info->error_message :                                      \
        "empty error message";                                           \
      napi_throw_error((env), NULL, error_message);                      \
    }                                                                    \
  } while (0)

#define NAPI_ASSERT_BASE(env, assertion, message, ret_val)               \
  do {                                                                   \
    if (!(assertion)) {                                                  \
      napi_throw_error(                                                  \
          (env),                                                         \
        NULL,                                                            \
          "assertion (" #assertion ") failed: " message);                \
      return ret_val;                                                    \
    }                                                                    \
  } while (0)

// Returns NULL on failed assertion.
// This is meant to be used inside napi_callback methods.
#define NAPI_ASSERT(env, assertion, message)                             \
  NAPI_ASSERT_BASE(env, assertion, message, NULL)

// Returns empty on failed assertion.
// This is meant to be used inside functions with void return type.
#define NAPI_ASSERT_RETURN_VOID(env, assertion, message)                 \
  NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING)

#define NAPI_CALL_BASE(env, the_call, ret_val)                           \
  do {                                                                   \
    if ((the_call) != napi_ok) {                                         \
      GET_AND_THROW_LAST_ERROR((env));                                   \
      return ret_val;                                                    \
    }                                                                    \
  } while (0)

// Returns NULL if the_call doesn't return napi_ok.
#define NAPI_CALL(env, the_call)                                         \
  NAPI_CALL_BASE(env, the_call, NULL)

// Returns empty if the_call doesn't return napi_ok.
#define NAPI_CALL_RETURN_VOID(env, the_call)                             \
  NAPI_CALL_BASE(env, the_call, NAPI_RETVAL_NOTHING)

#define DECLARE_NAPI_PROPERTY(name, func)                                \
  { (name), NULL, (func), NULL, NULL, NULL, napi_default, NULL }

#define DECLARE_NAPI_GETTER(name, func)                                  \
  { (name), NULL, NULL, (func), NULL, NULL, napi_default, NULL }

addon.cpp

#include "common.h"
#include <node_api.h>

#include "myadd.h" // function myAdd

static napi_value helloMethod(napi_env env, napi_callback_info info)
{
    napi_value result;

    NAPI_CALL(env, napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, &result));

    return result;
}

static napi_value addMethod(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

    NAPI_ASSERT(env, argc >= 2, "Not enough arguments, expected 2.");

    napi_valuetype valuetype0, valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
    NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number, "Wrong argument type. Numbers expected.");

    int x;
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &x));
    int y;
    NAPI_CALL(env, napi_get_value_int32(env, args[1], &y));
    napi_value sum;
    NAPI_CALL(env, napi_create_double(env, myAdd(x, y), &sum));

    return sum;
}

static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor properties[] = {DECLARE_NAPI_PROPERTY("hello", helloMethod),
                                             DECLARE_NAPI_PROPERTY("add", addMethod)};

    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(*properties), properties));

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
  • node-gyp源码构建配置文件
{
  'targets': [
    {
      'target_name': 'addon',
      'sources': [ 'addon.cpp' ],
	    'include_dirs':[ 'thirdlib' ],
      'libraries':[ '-lmyadd' ],
      # 'library_dirs':[ '<(module_root_dir)/thirdlib/bin_win/Debug' ]
      'conditions': [
          ['OS=="win"',{
              'library_dirs':[ '<(module_root_dir)/thirdlib/bin_win/Debug' ]
          }],
          ['OS=="linux"',{              'library_dirs':[ '<(module_root_dir)/thirdlib/bin_unix' ]
          }],
          ['OS=="mac"',{ # not test              'library_dirs':[ '<(module_root_dir)/thirdlib/bin_unix' ]
          }]
      ]
    }
  ]
}
  • 测试的js代码
    index.js
'use strict';

const addon = require('./build/Debug/addon.node');

console.log('JS output : ',addon.hello()); // hello world

console.log('JS output : ', addon.add(3, 6)); // 3+6=9

3. 编译

3.1 生成第三方库

cd thirdlib
mkdir bin_win
cd bin_win
cmake ..
cmake --build .

3.2 生成debug版本的addon.node

cd code
node-gyp configure build --debug

注意:只有带有调试信息的NodeJS C++插件才能进行源码调试。

4. 配置vscode调试环境

4.1 windows msvc(Visual Studio)

Run(Ctrl + Shift + D)】-> 【create a launch.json file】-> 【C++ (Windows)】
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(Windows) 启动",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "C:/Program Files/nodejs/node.exe",
            "args": ["${workspaceFolder}/index.js"],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "console": "externalTerminal"
        }
    ]
}

4.2 linux gcc/g++

Run(Ctrl + Shift + D)】-> 【create a launch.json file】-> 【C++ (GDB/LLDB)】
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "g++ - 生成和调试活动文件",
            "type": "cppdbg",
            "request": "launch",
            "program": "/usr/local/bin/node",
            "args": ["${workspaceFolder}/index.js"],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}

5. 调试

在vscode中按F5进行调试。

vscode左侧有变量、监视、调用堆栈和断点的信息。
在这里插入图片描述


License

License under CC BY-NC-ND 4.0: 署名-非商业使用-禁止演绎


Reference:

  1. gitee.com/itas109/nod…