C++ 모듈을 만드는 법은 알았는데 디버깅 하는 법을 알 수가 없었다.
내가 사용하고 있는 툴은 Visual Studio Code이다.
이 툴을 쓰게된 이유는 미국에 사는 친구가 요즘 Node.js가 미국에서 대세고 사용하는 툴은 주로 위에 것을 쓴다고 해서, 나도 그냥 썻다.
아톰이란 것도 있길래 살짝 고민했는데, 써보니 확실히 가볍고 좋은 것 같다.
저 툴을 사용하면 - Node.js설치하고 path설정후, 재부팅해주면 - 자동으로 .js 파일들은 디버깅이 가능하다.
근데 C++로 만든 .node 모듈 파일들은 디버깅을 할 수가 없었다.
솔직히 그냥 printf가 콘솔로 출력 되면 그걸로 디버깅 하려 했지만... 되지 않았다.
그래서 디버깅을 하기 위한 여행을 떠났다.
최초로 참고한 자료는 아래의 싸이트 이다.
https://computer-vision-talks.com/how-to-debug-nodejs-addons-in-visual-studio/
How to debug node.js addons in Visual Studio
While working on CloudCV I encountered problems in node.js addon written in native code. For CloudCV I use node.js with C++ Addon to separate high-performance algorithms (C++) from high-level networking API which node provides. In this tutorial I’m going to reveal best practices on debugging C
computer-vision-talks.com
동영상도 있고, 어떻게 디버깅을 할수 있는지 자세히 적혀 있다.
반나절 넘게 끙끙거리면서 결국 성공했다.
하지만 문제점들이 있다.
1. 내가 사용 하는 Visual Studio Code가 아닌 Visual Studio 2013을 기반으로 설명하고 있다. 그래서 개발 환경을 바꿔야 했다.
- 사실 이건 그렇게 큰 문제는 아니였다.
2. 기존에는 커맨드 창에서 node-gyp를 이용해서 컴파일을 했는데, 이 컴파일 과정에서 비쥬얼 스튜디오 2013을 동작시키고 있으면, 파일 엑세스 에러가 걸려서 컴파일이 안된다.
- 어떤 파일인지는 정확히 기억이 안나지만, 파일 하나를 비쥬얼 스튜디오 2013이 점유 하고 있는데 이놈때문에 컴파일 중에 에러가 난다. 그래서 매번 컴파일할 때 마다, 비쥬얼 스튜디오를 끄고 켜고를 반복해야 했다. 이러면 개발이 불가능은 아니지만, 열받아서 능률이 떠러진다.
3. 이 문제가 가장큰 문제인데, node-gyp를 통해 컴파일을 하고 나면 프로젝트 설정이 초기화 됐다. 굉장히 중요한 문제이니 잘읽어 보자.
- 우선 node-gyp가 사용 하는 컴파일러는 비쥬얼 스튜디오에서 사용하는 컴파일러와 동일하다.
- 어디에 확실히 그렇다고 쓰여있는 것을 보지는 않았는데.. node-gyp환경 구성을 보면 파이썬과, 비쥬얼 스튜디오 컴파일 환경이 필요 한데. 그곳에서 유추 할 수 있다.
- 또한 디버깅용으로 컴파일하고 나면 binding.sln 파일이 생기는데, 이것은 비쥬얼 스튜디오 프로젝트를 생성하면 나오는 파일과 동일 하다.
- 위에 참고한 사이트를 보면, binding.sln 파일을 비쥬얼 스튜디오 2013을 통해 열어서, 이런 저런 설정(좀있다 자세히 이야기 하겠다.)을 하는데 그 설정을 해야만 디버깅이 가능하다.
-근데 문제는 다시 node-gyp를 통해 컴파일을 하고 나면, 해놨던 설정들이 전부 원래대로 돌아간다.
-즉 매 컴파일 마다 설정을 다시해줘야 한다.
결론은 위 방법을 이용하면, 디버깅은 가능하다. 하지만 개발용 디버깅을 사실상 불가능 하다.
그래서 나는 Visual Studio Code를 통해서 컴파일도 하고, 디버깅을 할 수 있도록 도전해 봤다. 사실 printf만 콘솔창에 나왔어도 나는 안했을 것이다. 내가 만들려는 모듈은 아주 작은 모듈인데.. 배보다 배꼽이 큰 상황이다.
위 과정을 통해 알게된 사실들을 나열한다.
1. node-gyp에서 사용하는 컴파일러는 비쥬얼 스튜디오 환경에서 사용하는 C++ 컴파일러와 동일하다.
2. .node파일(node.js addon 파일)은 동적 라이브러리 파일 .dll과 동일하다. 때문에 C++ 디버거로 디버깅이 가능하다.
3.디버깅을 할때 사용하는 디버거는 (로컬 Windows 디버거)이다.
4. 디버깅을 실행 할때, '디버거를 설정 하는 부분'과, '디버깅을 하기위해 프로그램 실행을 어떻게 해야하는지' 두가지를 설정 해야 한다.
5. 디버깅을 하기위해 프로그램 실행하는 방법은 아래 그림과 같이.
- 명령어 : 프로그램을 실행시킬 프로그램 (C:\경로\node.exe)
- 명령인수(인자) : 처음으로 시작하는 js 파일 (app.js)
- 작업 디렉터리 : sln파일이 있는 곳에서 한 경로 위쪽 (..)
등등을 설정 해야 하는구나.
디버깅을 하기위해 프로그램 실행을 어떻게 해야하는지 설정
위에 나열한 내용을 토대로 문제를 풀어 갔다.
자 같이 문제를 풀어 보자.
우선 아까 이야기한 사이트
https://computer-vision-talks.com/how-to-debug-nodejs-addons-in-visual-studio/
의 내용을 토대로 진행했으니, 이곳에서 하라는 순서를 기반으로 이야기하겠다.
시작전
1. node-gyp를 설치하세요
npm install -g node-gyp
- 위에 명령어 하나로 해결되면 좋은데.. 나는 그리 안됐다.
- https://oj-tube.tistory.com/entry/Nodejs-C-%EB%AA%A8%EB%93%88-%EC%A0%9C%EC%9E%91-%ED%95%98%EA%B8%B0 를 참고하면 해결 가능하다.
Node.js C++ 모듈 제작 하기
C++ 언어로 모듈을 제작해서 간단한 출력을 하는 것 까지의 내용이다. 그 이후에 진행 되는 내용들이 있지...
blog.naver.com
2. nan을 설치하세요
npm install -g nan
nan은 모듈 개발 API인데 내가 진행할 예제는 필요없다.
하지만 위에 사이트에서 사용한 예제는 nan을 사용했다.
이놈을 이용하면 좀 더 간편하게 모듈을 제작할수 있고, 이러 저러한 단점을 보안해놨다. 하지만 결론적으로 v8에 의존적이라 v8 버전이 바뀌면 다시 컴파일 해야한다는 문제는 해결하지 못했고, 그 것을 해결하기 위해 n-api라는 프로젝트가 현재 진행 중이다. 하지만 n-api를 사용해서 컴파일하면, 하직 시험적 단계라는 경고문이 뜬다. 아무튼 지금 신경쓸 단계는 아니다. 우리는 디버깅이 목적이다.
3. node.js 소스를 받아 debug 모드로 빌드 하세요.
이 과정이 필요한 이유는 디버깅하면서 내가 만든 모듈 외에 node.js 관련 소스 부분도 디버깅이 가능하기 때문이다. 근데 내가 해보니 꼭 이거 안해도, 내가 만든 모듈은 그냥 릴리즈용 node.exe에서도 가능했다.
소스를 받았던 사이트는..
https://nodejs.org/ko/download/
다운로드 | Node.js
최신 LTS 버전: 8.11.1 (includes npm 5.6.0) 플랫폼에 맞게 미리 빌드된 Node.js 인스톨러나 소스코드를 다운받아서 바로 개발을 시작하세요. LTS 대다수 사용자에게 추천 현재 버전 최신 기능 Windows Installer node-v8.11.1-x86.msi macOS Installer node-v8.11.1.pkg Source Code node-v8.11.1.tar.gz Windows Installer (.msi) 32-bit 64-bit Windows Binary (.zip) 32-bit 64-bi
nodejs.org
이고, 버전은 8.11을 받았다.
참고로 컴파일은 겁나게 쉽다.
진짜 내 컴파일 역사상 가장 쉬웠다.
windows에서 진행했는데.
그냥 tar.gz파일 받아서, 압축 풀고, vcbuild.bat 파일만 더블 클릭하면 컴파일이 된다.
단, 그냥 더블 클릭하면 릴리즈 모드로 컴파일 되니, 디버그 모드로 컴파일하고 싶으면 커맨드 창 하나 실행하고, 해당 디렉토리로가서 'vcbuild.bat debug' 라고 실행 하면 된다.
근데, 참고 사이트에서는 옵션 두개를 더 붙여서..
'vcbuild.bat debug nosign x64 ' 라고 실행 하라고한다.
옵션이 무엇을 의미하는지 읽어 봤는데. 잘 기억이 안난다. x64는 64bit용이고 nosign이 무엇이엇는지 기억이 잘 안난다.
그래서 다시 한번 읽어 봤다 -_-;
서명을 안한다는 건데.. 기본이 vcbuild는 nosign이라고 한다.
결론은 옵션 2개가 전부 그냥 디폴드 값이니깐, 안써도 무방하다.
'vcbuild.bat debug' 만 하면된다.
그렇게 실행하고 나면 10~30분정도 후에 '설치경로\Debug\node.exe' 파일이 생긴다.
실험 삼아 이놈을 실행하고, console.log("hello~~"); 해보니 잘된다.
릴리즈 node.exe가 아닌 디버깅 정보가 담긴 node.exe가 만들어 진것이다. 이제 이놈을 사용해서 디버깅을 해보자.
위에도 말했지만 릴리즈 node.exe를 사용해서 모듈 디버깅 잘된다.
- 자 이제 부터는 응용 파트이다. 위에서 얻어낸 모든 정보를 동원해 원하는것을 얻어 보자.
4. Visual Studio Code와 C/C++ extension 을 설치하자.
우선 Visual Studio code는 설치됐다고 믿는다.
https://code.visualstudio.com/ 여기서 다운 받자.
Visual Studio Code - Code Editing. Redefined
Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications. Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.
code.visualstudio.com
그리고 C/C++ extension을 설치하자.
https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools 이놈을 설치해야 하는데.
Visual Studio Code를 실행후 ctrl + shift + x 누르고, 검색에 C++ 하면
가장 위에 뜨는 놈이 우리가 설치해야할 C/C++ extension이다.
MS에서 제공하는 것이고 생긴 모양은 아래와 같다.
C/C++ Extension 생긴 모양
5. 우리가 컴파일할 코드를 구성하자.
전에 쓴 글 https://blog.naver.com/ojwojwoj/221266536930에 있는 예제에 변수 a선언 하고, printf로 찍어 보는 것만 추가 했다.
파일1 - app.js
const hangle = require('./build/Debug/hangleModule.node');
console.log(hangle.hello());
파일2 - hangleModule.cc
#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
char test[] = "test etasdklfnaskldfnklasdnfklansdfklnasklfnasklndflasdkf";
args.GetReturnValue().Set(String::NewFromUtf8(isolate,test ));
int a;
a=2;
printf("hello ??? %d\n",a);
}
void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
파일3 - binding.gyp
{
"targets": [
{
"target_name": "hangleModule",
"sources": [
"hangleModule.cc"
],
"include_dirs": ["<!(node -e \"require('nan')\")"] #nan api사용시 추가. 지금은 사용안함
}
]
}
6. launch.json 파일을 구성하자.
이 파일은 위에서 언급한 '디버깅을 하기위해 프로그램 실행을 어떻게 해야하는지'를 설정하는 부분과 비슷하다.
디버깅을 하기위해 프로그램 실행을 어떻게 해야하는지 설정
이 정보를 통해 디버깅이 시작된다.
어떤 디버거를 사용할지 등등을 설정하는 것이다.
이파일은 작업디렉토리에 .vscode 디렉토리 밑에 있어야한다.
이 파일과 .vscode디렉토리는 cc파일에서 f5를 눌러 디버깅을 할려고 하면, 자동으로 생성되고, 반드시 작성해야한다고 알려준다.
개 노가다 끝에 완성된 launch.json 파일은 이와 같다.
{
"version": "0.2.0", //버전은 본인이 알아서 설정.
"configurations": [
{
"name": "C++ Launch (Windows)", //그냥 이름이다.
"type": "cppvsdbg", //이거 정말 중요하다. 정말 정말
"request": "launch", //-_-; 실행하라는 거지뭐
"program": "C:\\ProjectWork\\NodeJsD\\Debug\\node.exe", //디버깅할 대상을 실행 시킬 프로그램
// "program": "C:\\ProjectWork\\NodeJs\\node.exe", //릴리즈 버전 node.exe
"args": [
"${workspaceRoot}\\app.js" //node.exe app.js 이렇게 실행 시키기 위함.
],
"symbolSearchPath": "D:\\프로젝트\\18-스마트미러\\hangle\\build\\Debug", //디버깅 정보가 담긴 파일(pdb) 위치
"externalConsole": false, //외부 콘솔을 사용할것인지?
"logging": { //로그 정보
"moduleLoad": true,
"trace": true
},
"cwd": "${workspaceRoot}" //작업 드랙토리
}
]
}
위에 주석으로 대충 설명해놨는데 중요한것을 집고 넘어 가겠다.
program : 디버깅 시작시 어떤 프로그램으로 디버깅할 프로그램을 실행 시킬지 적는 부분이다. node.exe를 통해서 시작하면 된다. 우리가 위에서 진행한 디버그 버전 node.exe를 통해서 하면된다. 릴리즈용도 된다.
args : node.exe에 넘겨줄 인자다. app.js를 넘겨주면 되는데. 앞에 ${workspaceRoot} 라고 적은 이유는 처음에 내가 잘몰라서 cwd를 설정 안하고 컴파일을 했었는데.. app.js를 찾지 못하는 에러가 발생해서, 임시 방책으로 적어논것이다. 하지만 이제는 밑에 cwd를 설정 했기 떄문에 그냥 지우고 app.js만 써도 된다. ${workspaceRoot}는 현재 작업 디렉토리를 의미한다.
symbolSearchPath : 위에 적어 논대로 디버깅 정보가 담긴 파일(pdb)위치를 적으면 된다.
externalConsol : 디폴트 값이 true인데, 그렇게 되면 외부 콘솔창이 뜨고 거기서 node.exe가 실행된다. 그럼 종료 되면 획 사라지고, 또 환경 변수도 꼬이고 이런 저런 문제가 있길래 그냥 false로 했다, 그럼 visual studio code 디버그 콘솔창에서 콘솔 정보들을 볼수 있다. 그리고 printf 결과도 볼 수 있다.
cwd : 위에 언급한 그대로이다. 현재 작업 디렉토리 설정이다.
type: 우선 결론은 cppvsdbg라고 입력해야 하고, 이렇게 입력해야 디버거를 visual studio 2013에서 사용했던 디버거와 동일한 것을 사용한다.
저것을 몰라서 고생했는데. 그 이유는 다음과 같다.
1. 그냥 f5를 눌려서 생성되는 aunch.json 파일 기본들은 아래와 같다.
- 몇몇 세부 내용은 내가 채워 넣은것인데..
- type을 보면 기본으로 cppdbg로 설정되어있다.
{
// IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.
// 기존 특성에 대한 설명을 보려면 가리킵니다.
// 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요.
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "C:\\ProjectWork\\NodeJsD\\Debug\\node.exe",
"args": [
"app.js"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"miDebuggerPath": "/path/to/gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
type은 두가지가 가능한데..
cppdbg와 cppvsdbg이다.
cppvsdbg는 자동으로 비쥬얼 스튜디오 디버거를 찾아서 디버깅을 해주고,cppdbg 는 miDebuggerPath 설정을 통해 따로 디버거를 설정해줘야 한다.
내가 참고한 자료들은 전부 cppdbg type을 사용했고, 디버거로는 mingw64를 설치해서 gdb를 이용했다.
나도 그렇게 진행했는데. 디버깅 실행까지는 됐는데..
디버깅 정보를 볼수는 없었다.
사실 당연한 것이었다.
- 디버깅 정보를 남긴 컴파일러는 비쥬얼 스튜디오에서 만든 것이고,
- 디버깅 정보를 통해 디버깅을 할려는 놈은 엉뚱한 gdb이니..
- 디버깅 정보가 gdb에서 사용하는 포맷과 다르니 ..
볼 수 없는 것이 당연한 것이다.
이 사실은 전에 같은 arm칩을 쓰더라도, armcc랑 gnu에서 만든 arm용 gcc랑 생성된 내용이 다르다는 것을 보고 알고 있었다.
만약에 .node를 만들때, gcc를 이용해서 만들었다면 gdb로 디버깅이 가능했을 것이다.
하지만 비쥬얼 스튜디오에서 제공하는 컴파일러를 이용했으니, 비쥬얼 스튜디오에서 제공하는 디버거를 이용해야만 했다.
난 그방법을 miDebuggerPath 에 로컬 윈도우 디버거 실행 파일을 지정하는 방식으로 해결하려 했다. 하지만 아무리 찾아도 윈도우 디버거 실행 파일을 찾지 못했고 포기하려고 했는데.
https://code.visualstudio.com/docs/languages/cpp 싸이트를 꼼꼼히 읽다보니 해결방법을 찾은 것이다.
type 부분이 미묘하게 달랐던 것..
아무튼 이래저래 그디어 디버깅에 성공 했다.
하지만, 컴파일을 매번 수동으로 하기 싫었는데.
ctrl + shift + b 를 누르면 자동으로 컴파일 되는 방법이 있었다.
7. tasks.json 파일을 구성하자.
컴파일시 참조 하는 json 파일이고 이놈도 .vscode 디렉토리 밑에 있어야 한다.
이런 저런 방법이 있을텐데..
나는 아래와 같이 작성 해서 성공 했다.
{
"version": "2.0.0",
"tasks": [
{
"label": "build hello world",
"type": "shell",
"windows": {
"command": "./build.bat"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
원래는 windows 안에 command가 아니고
그냥 밖에 command가 있고,
args에 인자를 넣는 형태인데.
그냥 build.bat 파일을 하나 만들고 필요한 내용을 다 때려 박고,
그 파일을 실행하는 형태로 바꿨다.
원래는 이런 형태다. (형태만 참조 할것)
{
"version": "2.0.0",
"tasks": [
{
"label": "build hello world",
"type": "shell",
"command": "g++", //컴파일러
"args": [ // 넘길 인자들
"-g",
"helloworld.cpp"
]
}
]
}
아무튼 build.bat 파일엔 다음과 같이 썼다.
set npm_config_node_gyp=C:\ProjectWork\NodeJs\node_modules\npm\node_modules\node-gyp\bin\node-gyp.js
set PYTHON=C:\Users\user\.windows-build-tools\python27\python.exe
node-gyp configure build --nodedir="C:\ProjectWork\NodeJsD" --debug
npm_config_node_gyp과 PYTHON 변수 설정은 실행 하는 커맨드 창이 비쥬얼 스튜디오 code여서 적용이 안되있길래, 다시 해준것이고 (안하면 컴파일 안됨)
node-gyp configure build --nodedir="C:\ProjectWork\NodeJsD" --debug
가 컴파일 하는 명령어이다.
아마도, pm_config_node_gyp과 PYTHON 변수 설정은 환경 변수 설정에서 해주면 전체 적용 되기 때문에 따로 안해도 될것 같고..
node-gyp configure build --nodedir="C:\ProjectWork\NodeJsD" --debug 는
위에
"command": "g++", //컴파일러
"args": [ // 넘길 인자들
"-g",
를 잘 이용하면 별도의 bat 파일은 안 만들어도 될것 같다.
우선 나는 이것도 충분히 괜찮아서 더이상 진행은 안했는데.
bat 파일 만들기 싫으신 분은 끙끙해 보고 성공 여부를 알려주면 좋겠다.
node-gyp configure build --nodedir="C:\ProjectWork\NodeJsD" --debug에 대해서 조금 설명하자면
--debug를 붙여야 디버깅 정보를 남긴 node 파일이 만들어지고,
--nodedir를 설정해주면 node.exe에 관련된 정보를 디버깅 할때, 소스 까지 디버깅이 가능해진다.
처음 언급한 사이트에서는
What is really important here, is --nodedir="c:\Develop\node-v0.12.0" flag, which indicates to link against node in specified folder rather than system wide available.
라고 말한다.
아무튼 위 bat 파일을 만들고, 바로 실행 할수 있게 작업 디렉토리에 넣어 둔다.
그리고 ctrl + shift + b 를 누르면 bat파일이 실행 되면서 컴파일이 된다.
Visual Studio Code에서 gyp-node 컴파일 하는 장면
8. 디버깅을 해보자.
f5를 누르면 디버깅이 가능하다.
디버깅 장면
브레이크 포인트도 잘 동작하고, 변수 내용도 잘 보인고.. 호출 스택도 보인다.
이제 원하는 모든 것을 이뤄냈다!
이제 개발만 하면된다.
- 한계점
js 파일들은.. 디버깅이 안된다. ㅠ.ㅠ
js파일일땐 node.js용 디버거가! cc파일일때 비쥬얼 스튜디어 디버거가! 동작하면 좋겠는데.. 그건 안된다.
따로 따로 디버깅을 해야한다는 단점이 있다.
- 맺으며
내가 잘 못찾아서 그런지.. 어디에도 visual studio code를 이용한 node.js C++ 모듈 디버깅하는 법이 없어서, 글을 작성했다.
사실 누가 보겠냐만은.. 내가 본다.
기록하지 않은 내용은 몇달있으면 암것도 기억이 안난다.
옜날에 면접 볼때 면접관이 한말이 기억난다.
면접관 : 이건 어떻게 만드셨나요?
나 : 대충 이렇게 이렇게 만들었는데.. 정확히 기억이 안나네요. 제 프로젝트 노트에 적어 놨는데...
면접관 : 그럼 이건요?
나 : 이렇게 저렁게 이렇게요.. 근데 디테일한건 기억이 기억이 잘.. 제가 만든건 맞는데.. 그것도 적어 놧어요..
면접관 : 아니 전부 까먹나요?
나 : ...그러게요 근데 제가 만든거 맞아요. 그때 엄청 고민해서 만들었어요. 지금 3년째 문제 없이 잘 돌아가고 있어요.
면접관 : ㅡㅡ+ (눈초리)
좀 억울했다. 누가 그걸 다 기억한단 말인가 ㅠ.ㅠ
그런데 있나 보다 ㅠ.ㅠ
나는 못하니 적자.
'개발 > Node.JS' 카테고리의 다른 글
비슷한 문자 찾아내는 Node.js C++ 모듈 (0) | 2020.02.07 |
---|---|
Node.js C++ 모듈 제작 하기 (0) | 2020.02.07 |