『이펙티브 디버깅』 맛보기

복연 이
복연 이골든래빗 편집자 & 공동창업자 en Golden Rabbit

디오미디스 스피넬리스 지음 | 남기혁 옮김 | 한빛미디어 | 24,000원 ★ 소프트웨어의 완성은 디버깅! 이 책은 경험이 풍부한 개발자를 대상으로 소프트웨어를 완성하는 마지막 기술을 가르친다. 저자는 35년 경험에서 깨우친 일반 원칙, 높은 수준의 전략, 구체적인 기술에 관한 조언, 효율 높은 도구, 창의적인 기법, 효과적인 디버깅과 관련된 행동 특성을 제시한다. 저자가 제안하는 66개의 전문 기법을 통해 디버깅 역량을 확장하고, 각 문제 상황에 맞는 최상의 접근법을 선택할 수 있을 것이다. ★ 주요 내용 다양한 소프트웨어 장애를 해결하는 높은 수준의 전략과 방법 프로그래밍, 컴파일, 실행 시 적용할 구체적인 기법 디버거를 최대한 활용하는 방법 믿고 투자해도 좋은 범용 기술과 도구 막다른 길과 복잡한 미궁에서 탈출하는 첨단 아이디어와 기법 디버깅하기 쉬운 프로그램을 만들기 위한 조언 멀티스레딩, 비동기, 임베디드 코드 디버깅에 특화된 접근법 향상된 소프트웨어 설계, 구축, 관리를 통한 버그 회피법

『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
디오미디스 스피넬리스 지음
남기혁 옮김
디버깅 지옥에서 탈출하는 66가지 전략과 기법
이펙티브 디버깅
디버깅 지옥에서 탈출하는 66가지 전략과 기법
초판발행 2017년 5월 20일
지은이 디오미디스 스피넬리스 / 옮긴이 남기혁 / 펴낸이 김태헌
펴낸곳 한빛미디어 (주) / 주소 서울시 마포구 양화로7길 83 한빛미디어(주) IT출판부
전화 02 – 325 – 5544 / 팩스 02 – 336 – 7124
등록 1999년 6월 24일 제10 – 1779호 / ISBN 978-89-6848-778-1 93000
총괄 전태호 / 책임편집 김창수 / 기획·편집 이복연 / 교정 홍성신
디자인 표지 김연정 내지 여동일 조판 이경숙
영업 김형진, 김진불, 조유미 / 마케팅 박상용, 송경석, 변지영 / 제작 박성우, 김정우
이 책에 대한 의견이나 오탈자 및 잘못된 내용에 대한 수정 정보는 한빛미디어(주)의 홈페이지나 아래 이메일로
알려주십시오. 잘못된 책은 구입하신 서점에서 교환해 드립니다. 책값은 뒤표지에 표시되어 있습니다.
한빛미디어 홈페이지 www.hanbit.co.kr / 이메일 ask@hanbit.co.kr
Authorized translation from the English language edition, entitled EFFECTIVE DEBUGGING: 52
SPECIFIC WAYS TO DEBUG SOFTWARE AND SYSTEMS, 1st Edition by SPINELLIS, DIOMIDIS,
published by Pearson Education, Inc, publishing as Addison-Wesley Professional,
Copyright © 2016.
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any
means, electronic or mechanical, including photocopying, recording or by any information storage
retrieval system, without permission from Person Education, Inc. KOREAN language edition
published by Hanbit Media, Inc., Copyright © 2017.
이 책의 한국어판 저작권은 대니홍 에이전시를 통한 저작권사와의 독점 계약으로 한빛미디어(주)에 있습니다.
저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 무단 복제를 금합니다.
지금 하지 않으면 할 수 없는 일이 있습니다.
책으로 펴내고 싶은 아이디어나 원고를 메일 ( writer@hanbit.co.kr ) 로 보내주세요.
한빛미디어(주)는 여러분의 소중한 경험과 지식을 기다리고 있습니다.
과거와 미래의 멘토에게
4
지은이 디오미디스 스피넬리스Diomidis Spinellis
아테네 경제경영 대학교의 경영과학 및 기술학과 교수다. 주요 연구 분야는 소프트웨어 공학,
IT 보안, 클라우드 시스템 공학이다. 2004년과 2007년에 각각 소프트웨어 개발 생산성 어워
드를 수상한 『Code Reading』과 『Code Quality』의 저자이기도 하다. 200편 이상의 논문을
저널과 학회지에 게재했으며 2,500건 이상 인용됐다. 십 년간 「IEEE Software」의 편집 위원
으로 활동하면서 정기 칼럼인 ‘Tools of the Trade’에 글을 기고했다. macOS와 BSD 유닉스
에 그가 작성한 코드가 탑재되었으며, UMLGraph, CScout를 비롯한 다양한 오픈소스 소프
트웨어 패키지, 라이브러리, 도구를 개발했다. 임페리얼 칼리지 런던에서 소프트웨어 공학 석
사와 컴퓨터 과학 박사를 취득했다. ACM과 IEEE의 시니어 멤버다. 2015년 1월부터 현재까
지 「IEEE Software」의 편집장을 맡고 있다.
옮긴이 남기혁 kihyuk.nam@gmail.com
고려대학교 컴퓨터학과에서 학부와 석사를 마친 후 한국전자통신연구원에서 근무하다가, 현
재는 (주)프리스티에서 SDN 및 IoT 관련 기술을 개발하고 있다. 한빛미디어에서 『메이커 매
뉴얼』, 『Make: 센서』를 번역하였으며, 그 외에도 에이콘에서 『도커 컨테이너』, 『현대 네트워크
기초 이론』, 『오픈스택 네트워킹: 뉴트론』, 『실전 IoT 네트워크 프로그래밍』, 『자바 7의 새로운
기능』 등 다수의 도서를 번역했다.
지은이·옮긴이 소개
5
소프트웨어를 개발하거나 소프트웨어 시스템을 관리하다 보면 오류가 발생하기 마련이다. 코
드 작성 과정에서 발생하는 컴파일러 에러처럼 몇 초 만에 해결할 수 있는 것부터 대형 시스템
이 다운되는 일처럼 시간당 수백만 달러의 손실을 일으키는 문제에 이르기까지 다양한 오류가
발생한다. 실력 있는 전문가라면 어떤 오류가 발생하더라도 신속히 문제를 파악해서 근본 원인
을 해결할 줄 알아야 한다. 이러한 능력과 기술이 바로 디버깅의 핵심이며 이 책에서 다루고자
하는 주제다.
이 책은 경험이 풍부한 개발자를 대상으로 집필했다. 여기서 소개하는 내용을 제대로 이해하려
면 다양한 프로그래밍 언어로 작성된 예제 코드를 읽을 줄 알고 고급 GUI 및 명령줄 기반의 프
로그래밍 도구도 사용할 줄 알아야 한다. 그런 점에서 이 책은 입문서는 아니다. 하지만 이 책
에 나온 디버깅 기법만큼은 상세하게 설명한다. 필자의 경험에 따르면 특정한 기법의 전문성을
갖춘 숙련된 개발자도 다른 이들의 도움이 필요한 경우가 많기 때문이다. 소프트웨어에서 발생
한 문제를 수개월에 걸쳐 디버깅한 경험이 있다면 이 책에서 소개하는 몇 가지 고급 기법의 등
장 배경을 쉽게 이해할 수 있을 것이다.
이 책에서 다루는 내용
이 책에서는 최신 컴퓨팅 시스템을 개발하고 운영하는 과정에서 발생하는 문제를 해결하는 데
필요한 전략, 도구, 기법을 비롯한 디버깅에 관련된 전반적인 주제를 다룬다. 예전에는 프로그
램에서 발생한 오류를 찾아서 해결하는 것만 디버깅이라 했다. 하지만 요즘은 단독으로 실행되
는 프로그램은 거의 없으며 아무리 작은 프로그램이라도 최소한 외부 라이브러리와 동적으로
링크한다. 애플리케이션 서버에서 실행되는 웹 서비스는 관계형 데이터베이스나 NoSQL 데이
터베이스를 이용하여 디렉터리 서버로부터 데이터를 가져오고, 외부 프로그램을 실행하고, 다
른 미들웨어를 활용하고, 여러 가지 서드파티 패키지를 통합한다. 개발이 완료된 시스템이나
서비스가 제대로 작동하려면 전 세계에 퍼져 있는 호스트를 통해 실행되는 자체 개발 및 서드
파티 컴포넌트의 기능에 오류가 없어야 한다.
들어가며
6
현실적인 소프트웨어 개발 원칙인 데브옵스DevOps는 개발자와 다른 IT 전문가의 역할을 모두 강
조한다. 오류에 대해서도 이러한 전반적인 관점으로 다뤄야 한다. 난해한 문제들 대부분은 소
프트웨어의 어느 부분이 문제의 근본인지 곧바로 파악하기가 어렵기 때문이다.
이 책은 범용적인 기법부터 시작해서 점차 구체적인 기법을 소개하는 방식으로 설명한다. 먼저
다양한 소프트웨어 및 시스템 오류를 해결하는 데 도움되는 전략(1장), 방법론(2장), 도구와 기
법(3장)부터 설명한다. 그런 다음 디버깅 작업에 실제로 적용할 수 있는 기법들을 단계별로 소
개한다. 4장에서는 디버거를 사용하는 과정에 적용할 수 있는 기법을, 5장에서는 프로그램을
작성하는 과정에 필요한 기법을, 6장에서는 소프트웨어를 컴파일하는 과정에 필요한 기법을,
7장에서는 시스템을 실행하는 과정에 적용할 기법을, 8장에서는 멀티스레드 및 동시성 코드에
관련된 까다로운 버그들을 잡는 데 특화된 도구와 기법을 소개한다.
이 책의 활용 방법
처음부터 끝까지 한 장씩 차례대로 넘기면서 읽으면 된다. 하지만 더 좋은 방법은 필요할 때마
다 해결하려는 문제에 관련한 기법을 찾아보는 것이다. 이 책은 다음과 같이 크게 세 가지 주제
로 묶을 수 있다.
●● 전략과 기법: 장애에 대처하고 해결하기 위해 알아야 할 사항을 설명한다. 이에 대한 전반적인 사항은 ‘1장 고
차원 전략’과 ‘2장 범용적인 디버깅 기법’에서 소개한다. 그리고 ‘5장 프로그래밍 기법’에서 소개하는 여러 가지
기법도 이 범주에 속한다. 여기서 소개하는 아이템들을 잘 읽고 이해한 다음, 몸에 밸 때까지 하나씩 직접 적용
해본다. 디버깅하는 동안 자신이 사용한 기법을 체계적으로 다시 검토한다. 더 이상 해결할 수 없는 상황에 빠
지더라도 그동안 작업한 과정을 잘 파악하고 있다면 얼마든지 새로운 돌파구를 찾아낼 수 있다.
●● 기술과 도구: 디버깅을 위해 투자해야 할 대상을 설명한다. 주로 ‘3장 범용 도구를 활용한 기법’에서 설명하지
만, ‘아이템 36: 디버깅 도구 조율하기’처럼 일상에서 마주치는 문제를 해결할 때 적용할 만한 내용도 소개한
다. 시간을 충분히 투자하여 이러한 아이템에서 소개하는 기술과 도구를 익히고 서서히 실전에 적용한다. 때로
는 기존에 익숙했던 도구를 버리고 새로운 고급 도구를 적용하기 위해 공부를 해야 할 수도 있다. 처음에는 힘
들지만 길게 보면 훨씬 좋은 소프트웨어를 만들 수 있다.
7
●● 디버깅 요령: 상황이 좋지 않을 때 적용할 만한 요령을 다룬다. 여기서 소개하는 요령은 흔히 사용하지 않지만
난해한 문제를 해결하는 데 걸리는 시간을 절약할 수 있다. 가령 작성한 C나 C++ 코드를 컴파일할 수 없는 이
유를 못 찾겠다면, ‘아이템 50: 생성된 코드 확인하기’를 빠르게 훑어본 뒤 작업에 적용할 만한 것들을 골라낸
다. 나중에 이러한 요령을 실제로 적용할 상황에 놓이면 다시 책에 나온 설명을 자세히 읽고 확실히 파악한다.
이 책의 적용 방법
이 책은 장애의 원인을 진단하고 시스템에 존재하는 오류를 디버깅하는 데 필요한 노하우를 중
심으로 설명하고 있지만, 그중 상당수는 버그의 발생 가능성을 최소화하고 갑작스레 나타난 버
그에 손쉽게 대처하는 데 적용할 수도 있다. 엄격한 디버깅 기법과 소프트웨어 개발 방법론은
상호 보완 관계에 있다. 이 책에서 설명한 내용은 현재 또는 미래에 소프트웨어 설계, 구현, 관
리 업무를 수행할 때도 그대로 적용할 수 있다.
소프트웨어의 설계 단계에는 다음 원칙에 따른다.
●● 역할에 맞는 고차원 메커니즘을 적용한다(아이템 47, 66).
●● 디버깅 방식을 마련한다(아이템 6, 40).
●● 시스템의 작동 과정을 모니터링하고 로그를 남기는 메커니즘을 갖춘다(아이템 27, 41, 56).
●● 유닉스 명령줄 도구를 스크립트로 작성하여 활용하는 방법도 준비한다(아이템 22).
●● 내부에서 발생하는 오류를 불안정한 상태로 놔두지 않고 명확히 드러나게 만든다(아이템 55).
●● 오류가 발생한 후의 메모리 덤프를 가져오는 방법을 확보한다(아이템 35, 60).
●● 소프트웨어가 비결정적으로 실행되는 원인과 현상을 최소화한다(아이템 63).
소프트웨어의 구현 단계에는 다음 방법을 적용한다.
●● 동료로부터 피드백을 받는다(아이템 39).
●● 루틴을 작성할 때마다 단위 테스트를 만든다(아이템 42).
●● 어서션으로 가설과 코드의 정확성을 검증한다(아이템 43).
●● 코드를 최대한 유지하기 좋은(가독성, 안정성이 좋고 분석과 수정이 용이한) 형태로 만든다(아이템 46, 48).
●● 빌드 과정에 비결정성이 발생할 근원을 차단한다(아이템 52).
8
마지막으로 소프트웨어 개발과 운영 작업을 관리할 때는 다음 원칙을 따른다.
●● 적절한 시스템을 이용하여 이슈를 기록하고 추적한다(아이템 1).
●● 작업할 이슈를 분류하고 우선순위를 정한다(아이템 8).
●● 제대로 구축된 버전 관리 시스템을 이용하여 소프트웨어의 변경 사항을 적절히 기록한다(아이템 26).
●● 이전 버전과 새 버전을 비교할 수 있도록 소프트웨어를 점진적으로 배치한다(아이템 5).
●● 사용하는 도구와 배치할 환경을 다양하게 구성한다(아이템 7).
●● 도구와 라이브러리를 정기적으로 업데이트한다(아이템 14).
●● 시스템에서 사용하는 서드파티 코드를 구매하거나(아이템 15), 까다로운 오류를 정확히 찾아주는 정교한 도
구를 구매한다(아이템 51, 59, 62, 64, 65).
●● 하드웨어 인터페이스와 임베디드 시스템을 디버깅하기 위한 전용 키트를 마련한다(아이템 16).
●● 소프트웨어를 원격에서 디버깅하는 환경을 꾸민다(아이템 18).
●● 시스템 성능이 높아야 처리할 수 있는 작업을 위해 CPU와 저장 공간을 넉넉히 준비한다(아이템 19).
●● 코드 리뷰와 멘토링을 통해 개발자끼리 협업을 유도한다(아이템 39).
●● 테스트 중심 개발 방법론을 도입한다(아이템 42).
●● 빌드 및 테스트 주기를 짧고 가볍게 유지하는 선에서 소프트웨어 빌드 성능 프로파일링과 정적 분석, 동적 분석
기법을 도입한다(아이템 11, 51, 53, 57, 59).
용어 관련
이 책에서 말하는 ‘오류fault ’란 ISO-24765-2010(Systems and software engineering-
Vocabulary)에 나온 ‘컴퓨터 프로그램에 있는 부정확한 과정, 프로세스, 데이터 정의’를 의미
한다. 흔히 ‘결함defect ’이라고도 표현하고 일상에서는 간단히 ‘버그’라고 부른다. ‘장애failure ’란 표
현도 같은 문서에서 정의한 ‘시스템 또는 시스템 컴포넌트가 주어진 기능을 처리하는 데 일정
한 수준의 성능을 만족하지 않는 현상’이라는 의미를 따른다. 장애가 발생하면 프로그램에 충
돌이 발생하거나 멈춰서 아무런 반응이 없거나 엉뚱한 결과를 낸다. 따라서 ‘장애’는 ‘오류’가 있
을 때 발생한다. 때로는 ‘장애’를 ‘오류’와 ‘결함’으로 부르기도 하는데 ISO 표준에서도 이러한
표현을 인정한다. 기본적으로 앞에 나온 정의를 따르지만 용어의 의미가 문맥에 명확히 드러날
9
때는 용어를 법률 문서처럼 엄격히 구분하지 않았다. 가령 ‘문제problem ’란 표현은 오류(예: 코드
에 존재하는 문제)를 의미하기도 하고 장애(예: 재현할 수 있는 문제)를 의미하기도 한다.
이제는 유닉스 운영체제에서 사용하던 셸, 라이브러리, 도구를 다른 플랫폼에서도 사용할 수
있다. 이 책에서 말하는 유닉스란 애플의 macOS, 여러 가지 GNU/Linux(예: 아치 리눅스,
센트 OS, 데비안, 페도라, 오픈수세, 레드햇 엔터프라이즈 리눅스, 슬랙웨어, 우분투), 유닉스
의 직계 후손들(예: AIX, HP-UX, 솔라리스), 다양한 BSD 변종(예: FreeBSD, OpenBSD,
NetBSD)와 윈도우의 시그윈Cygwin을 비롯한 유닉스의 원칙과 API를 따르는 모든 시스템을 가
리킨다.
마찬가지로 C++, 자바, 파이썬으로 코드를 작성할 때 최신 버전을 사용한다고 가정한다. 예제
를 작성할 때 최신 기능이나 특수한 기능을 사용해야만 돌아갈 수 있는 것들은 최대한 자제했다.
본문에서 ‘자신이 작성한 코드/소프트웨어’란 표현은 디버깅하거나 사용하고 있는 코드나 소프
트웨어를 의미한다. 이렇게 표현하면 간결할 뿐만 아니라 코드의 작성자를 명확히 드러낼 수
있다. 이는 특히 소프트웨어를 개발할 때 중요하다.
루틴routine이란 용어는 코드에서 호출할 수 있는 단위를 의미한다. 예를 들어 멤버 함수, 메서
드, 함수, 프로시저, 서브루틴 등이 있다.
비주얼 스튜디오와 윈도우란 용어는 마이크로소프트 제품을 의미한다.
버전 관리(제어) 시스템은 깃Git과 같은 소프트웨어 설정 관리에 사용하는 도구를 의미한다.
10
먼저 이 책을 진행하는 데 전문적인 조언과 관리를 해준 담당 편집자인 트리나 플레처 맥도날
드와 시리즈 편집자인 스콧 메이어에게 감사의 말을 전하고 싶다. 또한 기술 감수를 해준 디미
트리스 안드레아디스, 케블린 헤니, 존 페고니스, 조지 서루바투칼에게도 감사드린다. 이분들
이 제공해준 뛰어난 아이디어와 지적과 제안 덕분에 이 책의 품질을 크게 향상시킬 수 있었다.
원고를 매의 눈처럼 예리하고 검토하고 깃털처럼 가볍게 다듬어준 교열 편집자인 스테파니 길
스에게도 특별히 감사드린다. 그녀의 뛰어난 일 처리 덕분에 끔찍하게 여기던 교정 작업을 즐
겁게 마무리할 수 있었다. 이 책의 제작 과정을 굉장히 효과적으로 관리해준 멜리사 파나고스
와 총 제작 책임자인 주리 나힐, 이 책의 구성을 담당한 레이텍의 도사인 로리 휴, 편집 관련 조
언을 해준 셰리 레플린, 기술 감수단을 이끌어준 올리비아 바세지오, 멋진 표지를 만들어준 추
티 프라저티스, 마케팅을 담당한 스테파니 나킵에게도 감사드린다. 초반에 이 책의 콘셉트를
잡는 데 조언해준 알프레도 벤조, 조르지오스 고시오스, 파나지오티스 로리다스에게도 감사드
린다.
저자가 「IEEE Software」의 ‘Tools of the Trade’ 칼럼에 기고한 네 편의 글이 이 책에 다음과
같이 반영됐다.
●● 아이템 5: 정상 시스템과 비정상 시스템의 차이점 분석하기 (‘Differential Debugging’, vol. 30, no. 5,
2013, pp. 19-21)
●● 아이템 22: 유닉스 명령줄 도구로 디버깅 데이터 분석하기 (‘Working with Unix Tools’, vol. 22, no. 6,
2005, pp.9-11)
●● 아이템 58: 실행 흐름 추적하기 (‘I Spy’, vol. 24, no. 2, 2007, pp.16-17)
●● 아이템 66: 고수준 추상화를 사용하도록 코드 다시 작성하기 (‘Faking It’, vol. 28, no. 5, 2011, pp. 96,
95)
또한 다른 소스에서 영감을 얻은 아이템들도 있다.
●● ‘아이템 63 : 예측할 수 없는 부분을 분리하거나 제거하기’는 마틴 파울러의 글 ‘Eradicating Non -
Determinism in Tests’(2011년 4월 14일), ‘TestDouble’(2006년 1월 17일)에 나온 아이디어를 토
대로 작성했다.
감사의 글
11
●● ‘아이템 48: 의심스런 코드의 가독성과 구조 향상시키기’에서 소개한 리팩토링 기법은 대부분 마틴 파울러의
저서인 『리팩토링: 코드 품질을 개선하는 객체지향 사고법』(한빛미디어, 2012)에서 인용했다.
●● ‘아이템 60: 사후 디버깅으로 교착 상태 분석하기’는 브라이언 캔트릴과 제프 본윅이 「ACM 큐Queue」에 기
고한 ‘Real-World Concurrency’를 읽고 자극받아 집필했다.
●● ‘아이템 66: 고수준 추상화를 사용하도록 코드 다시 작성하기’에 나온 자바 코드는 타기르 발레예프가 작성한
코드를 바탕으로 만들었다.
저자의 학계 활동에 다방면으로 도와준 아테네 경제경영 대학교의 여러 동료들, 다미아노스 챠
치안토니오우, 조르지오스 도우키디스, 콘스탄틴 카치오스, 조지 기아글리스, 엠마노우일 기아
코우마키스, 디미트리스 그리차일리스, 조지 레카코스, 파나지오티스 로우리다스, 카터리나 파
라마타리, 낸시 포울로우디, 안젤리키 포울리메나코우, 조르지오스 시옴코스, 스파이로스 스파
이로우, 크리스토스 파란틸리스에게도 감사드린다. 이들의 도움으로 책을 마무리할 수 있었다.
디버깅은 실습을 통해 배우는 숙련 기술이다. 따라서 지난 40년 동안 저자로 인해 발생한 버그
를 견디고, 도움되는 이슈 리포트를 제공하고, 저자가 작성한 코드를 검토하고 테스트하고, 버
그를 피하고 추적하고 잡는 방법을 가르쳐준 동료들에게도 감사의 말을 남겨야 마땅하다.
●● 구글 애드 SRE FE 팀원들: 마크 빈, 칼 크로스, 알렉산드루-니콜라이 디미트리우, 페데 하인즈, 렉스 홀트,
토머스 헝거, 토머스 콥, 조나단 랭, 데이비드 리드비터, 앤서니 렌튼, 스벤 마나치, 리노 마스트로도메니코, 트레
버 맷슨-해밀튼, 필립 멀케이, 울프램 파이퍼, 마틴 스티언홈, 스튜어트 태일러, 스티븐 손, 스티븐 서굿, 니콜라
워싱턴
●● CQS 동료들: 테오도로스 이브게니우, 바겔리스 카파르치아니스, 닉 나써피스
●● 아테네 경제경영 대학교의 경영과학 및 기술학과 소속의 전현직 연구원 및 조교: 아킬레아스 아나그노스토폴
로스, 스테파노스 안드로첼리스-테오토키스, 콘스탄티노스 코리아노폴로스, 마리오스 프라콜리스, 바겔리스
기아니카스, 조르지오스 고시오스, 스타브로스 그리고라카키스, 바실리오스 카라코이다스, 마리아 케차기아, 크
리스토스 라자리스, 디미트리스 미트로폴로스, 크리스토스 오이코노모우, 투샤 샤르마, 소포클리스 스토우라이
티스, 콘스탄티노스 스트로길로스, 바소 탄갈라키, 스타브로스 트리히아스, 바실레이오스 플라코스, 기오르고스
조가넬리스
●● 그리스 재무부의 정보 시스템 담당 사무국 직원들: 코스타스 발라토스, 레오니다스 보기아티스, 파라스케비 챠
지미타코우, 크리스토스 코보로조스, 야니스 디마스, 디미트리스 디미트리아디스, 아레티 드라카키, 니콜라우스
12
드로소스, 크리스탈리아 드라이스텔라, 마리아 엘레프테리아도우, 스타마티스 에조발리스, 카테리나 프란체스
카키, 불라 하밀루, 안나 혼드로다키, 야니스 이오아니디스, 크리스토스 K. K. 로버도스, 이피게네이아 칼람포키
도우, 니코스 칼라티스, 라자로스 카플라노그로우, 아겔로스 카보니스, 소피아 카트리, 크리스토스 카시스, 디오
니시스 케팔리노스, 아이작 코키니디스, 조르지오 코사키스, 기오르고스 콘도라키스, 파나기오티스 크라니 디오
티스, 야니스 키리아코폴로스, 오디세아스 키리아코포이로스, 조르기오스, 라스카리디스, 파나기오티스 라자리
디스, 나나 레이소우, 이오안나 리바디오티, 아겔리키 라이코디, 아시미나 만타, 마리아 마라벨라키, 차라 마브
리도우, 소피아 마브로폴로우, 미카일 미칼로폴로스, 판텔리스 나시카스, 토도로스 파그치스, 안젤리키 파나이
오타키, 크리스토스 파파돌리스, 바실리스 파파포티노스, 이오아니스 페라키스, 칸토 페트리, 안드레아스 피피
스, 니코스 프사라키스, 마리안티 프소마, 오디세아스 파이로보라키스, 타소스 사그리스, 아포스톨로스 스키자
스, 소피 세페리데스, 마리노스 시갈라스, 조지 스타몰리스, 안토니스 스트리키스, 안드레아스 스볼로스, 카리스
테오카리스, 아드리아노스 트리가스, 디미트리스 차키리스, 니키 초마, 마리아 차팔리아, 바실리키 초블라, 디미
트리스 바피아디스, 아킬레아스 베모스, 이오아니스 블라코스, 지아니스 저르바스, 타나시스 제르보폴로스
●● FreeBSD 프로젝트 팀원들: 존 볼드윈, 일코 벌트, 마틴 크라코서, 파월 야쿱 다이덱, 세리 데이비스, 브룩스
데이비스, 러슬란 에밀로프, 브루스 에반스, 브라이언 펀다코스키 펠만, 페드로 지푸니, 존-마크 거니, 칼 요한
구스타프슨, 콘래드 잰코스키, 폴헤닝 캄프, 크리스 케나웨이, 기오르고스 케라미다스, 보리스 코바렌코, 맥스
라이어, 네이트 로손, 샘 레플러, 알렉산더 레이딩어, 씬 라이, 스콧 롱, M. 워너 로시, 브루스 A. 마, 데이비드 말
론, 마크 머리, 사이먼 L. 닐슨, 데이비드 오브라이언, 요한 ‘미르크라버크’ 오스카슨, 콜린 퍼시발, 알프레드 펄
스타인, 제스 피터스, 톰 로드, 루이지 리조, 래리 로젠만, 젠스 슈웨이카드, 켄 스미스, 댁얼링 스모르그라프, 머
리 스토켈리, 마리우스 스트로블, 이반 보라스, 로버트 왓슨, 피터 웸, 가렛 울맨
●● LH 소프트웨어 및 SENA 팀원들: 카터리나 아라반티노우, 미칼리스 벨리바나키스, 폴리나 비라키, 디미트리
스 카라미도폴로스, 릴리 카라미도폴로우, 안젤로스 카리시스, 기오르고스 카치미칼리스, 니코스 크리스토폴로
스, 크리스티나 다라, 데잔 디미트리제빅, 파니아 도르코피키, 니코스 도카스, 레프테리스 지오갈라스, 소티리스
지로디아노스, 바실리스 지아나코스, 크리스토스 콜로지아니스, 안티 칼리비오티, 에르시 카라나소우, 안토니스
코노모스, 이시도로스 코우벨라스, 조지 키리아지스, 마리나 리아파티, 스피로스 리비에라토스, 소피아 리비에
라토우, 파나지오티스, 로리다스, 나탈리아 밀리오우, 스피로스 몰페타스, 카터리나 모토지아니, 디미트리스 넬
라스, 지오니스 엔톤토스, 크리스토스 오이코노모우, 니코스 파노시스, 바실리스 파파리조스, 타소스 파파스, 알
렉산드로스 파파스, 칸티아 프린테지, 마리오스 살테리스, 아르지로 스타마티, 타키스 테오파노폴로스, 디미트
리스 톨리스, 프로소 토팔리, 타키스 트라가키스, 사바스 트리안타필로, 페리클리스 차하게아스, 니코스 차그카
리스, 아포스톨리스 치그크로스, 기오르고스 차말리스, 기아니스 블라코기아니스
●● ECRC(European Computer Industry Research Center) 소속 동료들: 미레이 듀카스, 안나-
마리아 엠드, 알렉산더 헤롤드, 폴 마틴, 데이브 모튼
13
●● 임페리얼 칼리지 런던의 컴퓨터과학과 동료들: 바실리스 카포일리아스, 마크 도슨, 소피아 드로소폴로우, 코스
티스 드라일라키스, 데이브 에드몬슨, 수잔 아이젠바흐, 필리포스 프란굴리스, 아나스타시오스 하지코콜리스,
폴 켈리, 스티븐 J. 레이시, 필 매일, 리 M. J. 맥클로클린, 스튜어트 매크로버트, 믹셀리스 멜라크리니디스, 잔-
시몬 팬드리, 마크 테일러, 페리클리스 차하기아스, 던컨 화이트
●● UC 버클리의 CSRG(Computer Science Research Group): 케이스 보스틱
●● 폴리아디스  어소시에이츠 동료들: 알렉시스 아나스타시오우, 콘스탄틴 도콜라스, 노엘 코틀리스, 디미트리오
스 크라소폴로스, 조지 카이리아지스, 기아니스 마라키스, 아타나시오스 폴리아디스
●● 여러 모임을 통해 만난 이들: 이오르고스 아다모폴로스, 디미트리스 안드레아디스, 야니스 코로베시스, 알렉산
더 콜롬비스, 존 이오아니디스, 디미트리오스 칼로게라스, 파나기오티스 카나보스, 테오도로스 카로노스, 파이
돈 리암포티스, 엘리아스 파파바실로폴로스, 바실리스 프레벨라키스, 스텔리오스 카르체타키스, 아킬레스 볼리
오티스, 알렉시오스 자브라스
마지막으로 집에서도 디버깅하고 주말과 휴가 중에도 글을 쓰는 저자를 오랫동안 참고 견디며 지
원을 아끼지 않은 나의 가족에게 감사의 말을 전하고 싶다. 특히 [그림 5.2]를 만든 디오니시스와
이 책의 표지를 선정하는 데 도와준 일라이자와 일레아나에게 특별히 고맙다는 말을 남긴다.
14
CONTENTS
지은이·옮긴이 소개................................................................................................................
4
들어가며...............................................................................................................................
5
감사의 글...........................................................................................................................
10
CHAPTER 1 고차원 전략
아이템 1: 모든 문제를 이슈 추적 시스템으로 관리하기 ..............................................................
19
아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기 .......................................
22
아이템 3: 선행 조건과 후행 조건 만족 여부 확인하기 ................................................................
24
아이템 4: 문제 발생 지점부터 버그를 추적하거나, 프로그램 시작 지점부터 버그를 찾아나가기 ..........
26
아이템 5: 정상 시스템과 비정상 시스템의 차이점 분석하기 ........................................................
28
아이템 6: 소프트웨어에서 제공하는 디버깅 기능 활용하기 .........................................................
32
아이템 7: 빌드 및 실행 환경을 다양하게 구성하기 ....................................................................
36
아이템 8: 가장 중요한 문제에 집중하기 ..................................................................................
40
CHAPTER 2 범용적인 디버깅 기법
아이템 9: 성공적인 디버깅을 위한 마음가짐 ............................................................................
43
아이템 10: 효율적으로 문제 상황 재현하기 .............................................................................
46
아이템 11: 코드 수정 후 결과 확인까지의 시간 최소화하기 ........................................................
49
아이템 12: 복잡한 테스트 시나리오 자동화하기 .......................................................................
51
아이템 13: 디버깅 관련 데이터를 한눈에 볼 수 있는 환경 구축하기 .......................................
53
아이템 14: 소프트웨어 업데이트 고려하기 ..............................................................................
56
아이템 15: 서드파티 소스 코드 분석을 통해 문제 해결하기 ........................................................
57
아이템 16: 전문 모니터링 및 테스팅 장비 활용하기 ..................................................................
59
아이템 17: 오류의 효과 극대화하기 .......................................................................................
62
아이템 18: 원격 디버깅 환경 구축하기 ...................................................................................
65
15
아이템 19: 디버깅 작업 자동화하기 .......................................................................................
67
아이템 20: 디버깅 전과 후에 정리하기 ...................................................................................
69
아이템 21: 비슷한 문제 모두 고치기 ......................................................................................
70
CHAPTER 3 범용 도구를 활용한 기법
아이템 22: 유닉스 명령줄 도구로 디버깅 데이터 분석하기 .........................................................
73
아이템 23: 명령줄 도구 옵션과 관용 표현 활용하기 ..................................................................
80
아이템 24: 코드 편집기로 디버깅 데이터 탐색하기 ...................................................................
83
아이템 25: 작업 환경 최적화하기 ..........................................................................................
85
아이템 26: 버전 관리 시스템으로 버그 원인과 히스토리 추적하기 ...............................................
90
아이템 27: 독립적인 프로세스로 구성된 시스템에서 모니터링 도구 사용하기 ................................
94
CHAPTER 4 디버거 활용법
아이템 28: 디버깅 버전으로 컴파일하기 ...............................................................................
100
아이템 29: 한 단계씩 코드 실행하기 ....................................................................................
104
아이템 30: 코드와 데이터 중단점 활용하기 ...........................................................................
106
아이템 31: 리버스 디버깅 ..................................................................................................
109
아이템 32: 루틴 사이의 호출 흐름 추적하기 ..........................................................................
112
아이템 33: 변수와 표현식의 값을 분석하여 에러 찾기 .............................................................
114
아이템 34: 실행 중인 프로세스에 디버거 연동하기 .................................................................
117
아이템 35: 코어 덤프 다루기 ..............................................................................................
120
아이템 36: 디버깅 도구 조율하기 ........................................................................................
122
아이템 37: 어셈블리 코드와 메모리 값 확인하기 ....................................................................
126
16
CONTENTS
CHAPTER 5 프로그래밍 기법
아이템 38: 의심스런 코드를 검토하고 손으로 실행해보기 ........................................................
131
아이템 39: 동료 검토하기 ..................................................................................................
133
아이템 40: 디버깅 기능 추가하기 ........................................................................................
135
아이템 41: 로그 남기기 .....................................................................................................
138
아이템 42: 단위 테스트 사용하기 ........................................................................................
144
아이템 43: 어서션 사용하기 ...............................................................................................
147
아이템 44: 코드를 바꿔보면서 검증하기 ...............................................................................
151
아이템 45: 정상적인 코드와 문제가 발생한 코드의 차이점 줄이기 .............................................
152
아이템 46: 의심스런 코드 간소화하기 ..................................................................................
153
아이템 47: 의심스런 코드를 다른 언어로 작성해보기 ..............................................................
157
아이템 48: 의심스런 코드의 가독성과 구조 향상시키기 ...........................................................
158
아이템 49: 버그의 증상이 아닌 원인 고치기 ..........................................................................
162
CHAPTER 6 컴파일 시간 기법
아이템 50: 생성된 코드 확인하기 ........................................................................................
165
아이템 51: 정적 분석 도구 활용하기 ....................................................................................
169
아이템 52: 빌드 결과와 실행 동작이 항상 일정하도록 설정하기 ................................................
174
아이템 53: 라이브러리에서 제공하는 디버깅 및 검사 기능 설정하기 ..........................................
177
17
CHAPTER 7 실행 시간 기법
아이템 54: 테스트 케이스를 작성하여 오류 찾기 ....................................................................
185
아이템 55: 오류가 발생한 즉시 프로그램 중단하기 .................................................................
190
아이템 56: 애플리케이션 로그 파일 분석하기 ........................................................................
191
아이템 57: 시스템 및 프로세스 연산에 대한 프로파일 분석하기 ................................................
196
아이템 58: 실행 흐름 추적하기 ...........................................................................................
200
아이템 59: 동적 프로그램 분석 도구 활용하기 .......................................................................
206
CHAPTER 8 멀티스레드 코드 디버깅하기
아이템 60: 사후 디버깅으로 교착 상태 분석하기 ....................................................................
212
아이템 61: 프로그램의 실행 흐름을 기록한 뒤 재생하기 ..........................................................
219
아이템 62: 전문 도구로 교착 상태와 경쟁 상태 찾기 ...............................................................
224
아이템 63: 예측할 수 없는 부분을 분리하거나 제거하기 ..........................................................
231
아이템 64: 자원 경쟁으로 인해 발생한 성능 확장성 문제 분석하기 ............................................
233
아이템 65: 성능 카운터를 이용하여 거짓 공유 발생 지점 찾기 ..................................................
237
아이템 66: 고수준 추상화를 사용하도록 코드 다시 작성하기 ....................................................
241
찾아보기 ........................................................................................................................
251
『이펙티브 디버깅』 맛보기
191장 고차원 전략
고차원 전략
본격적으로 문제 해결에 들어가기 전에 전략을 잘 세워야 한다. 그래야 최소한의 노력으로 문
제를 성공적으로 해결할 수 있다. 선택한 전략이 작업에 적합하지 않다면 즉시 차선책으로 전
환해야 한다.
아이템 1: 모든 문제를 이슈 추적 시스템으로 관리하기
어느 날 동료가 전화를 걸어 여러분이 작성한 애플리케이션이 제대로 작동하지 않는다고 불평
을 늘어놓을 수 있다. 그 사실을 포스트잇에 간단히 기록하고 모니터를 화려하게 장식한 다른
노트 틈에 추가하고선, 나중에 그 동료에게 애플리케이션의 새 버전에 필요한 최신 라이브러리
를 보냈는지 확인할 것을 머릿속으로만 기억해둘 것이다. 하지만 이런 식으로 일을 처리하면
안 된다. 제대로 하려면 다음에서 소개하는 방법에 따라 처리한다.
먼저 이슈 추적 시스템issue-tracking system부터 구축한다. 깃허브GitHub나 깃랩GitLab을 비롯한 여러 소스
코드 저장소는 코드의 버전 관리를 위한 기본적인 기능뿐 아니라 이슈 추적 기능도 함께 제공
하고 있다. 또한 상당수의 조직에서 상용 이슈 추적 시스템인 지라JIRA를 사용하고 있다. 이 시
스템은 라이선스를 구입하여 조직 내부에 직접 구축하거나 서비스 형태로 사용할 수 있다. 지
라 대신 버그질라Bugzilla, 론치패드Launchpad, OTRS, 레드마인Redmine, 트랙Trac 같은 오픈소스 시스
템도 많이 사용한다. 물론 어느 시스템을 사용하는가보다는, 이러한 시스템을 잘 활용하여 개
CHAPTER 1
20 이펙티브 디버깅
발 과정에서 발생한 모든 이슈를 시스템에 잘 기록해두는 것이 무엇보다 중요하다.
이슈 추적 시스템에 기록되지 않은 문제는 처리되지 않는다. 하지만 시스템을 꾸준히 활용하면
다음 효과를 얻을 수 있다.
●● 디버깅 작업 과정을 명확하게 파악할 수 있다.
●● 릴리스 일정을 수립하고 추적할 수 있다.
●● 작업의 우선순위를 정할 수 있다.
●● 자주 발생하는 이슈나 해결책을 문서로 정리할 수 있다.
●● 해결해야 할 문제를 실수로 빼먹지 않을 수 있다.
●● 릴리스 노트를 자동으로 생성할 수 있다.
●● 결함을 측정하고, 이를 되돌아보며 교훈을 얻을 수 있는 저장소로 활용할 수 있다.
직급이 높아서 이슈를 직접 기록할 의무가 없는 이들도 이슈 추적 시스템을 사용하는 것이 좋
다. 자신이 직접 발견한 이슈도 마찬가지다. 조직에 따라 이슈에 직접 관련된 이들만 소스 코드
를 수정하는 권한을 주기도 한다.
이슈를 작성할 때 문제를 재현하는 방법도 정확히 기록한다. 이때 간결하면서도 필요한 내용이 모
두 담긴 예제도 함께 적어주면 좋다. 이러한 예제는 애플리케이션 코드에 그대로 복사한 후 컴
파일하고 실행해서 문제를 직접 확인해볼 수 있을 정도로 정확하게 작성한다(아이템 10: 효율적으
로 문제 상황 재현하기 참조). 잘 작성된 버그 리포트만 이슈 추적 시스템에 등록되게 하려면, 리포
트를 제대로 작성하는 방법을 제시하고 사용자에게 이를 철저히 숙지시킨다(그 방법을 화장실
문에 붙여둔 회사도 있었다).
또한 버그 리포트의 제목은 정확하게 붙이고, 버그의 우선순위와 심각한 정도, 그리고 해당 버그에 관련
된 이들과 버그가 발생한 환경에 대한 상세한 정보도 기록한다. 이러한 항목은 다음 사항을 염두
에 두고 작성해야 한다.
●● 제목을 정확하고 간결하게 작성하면 요약 리포트만 봐도 어떤 버그인지 쉽게 알아볼 수 있다. 가장 나쁜 예는
‘프로그램이 갑자기 뻗었음’과 같이 적는 것이다. ‘저장하는 동안 리프레시 버튼을 누르면 프로그램이 뻗음’과
같이 구체적으로 적는 것이 바람직하다.
●● 심각한 정도를 명시하면 버그의 우선순위를 정하는 데 도움이 된다. 데이터가 손실되는 버그는 당연히 심각한
문제로 봐야 하지만, 미美적인 문제나 대안이 알려진 문제는 이보다 심각한 정도가 낮다. 이처럼 버그의 심각한
정도를 명시하면 이슈 목록을 작성할 때 당장 해결해야 하는 이슈, 나중에 처리해도 되는 이슈, 무시해도 되는
이슈 등과 같이 우선순위에 따라 분류할 수 있다.
211장 고차원 전략
●● 이렇게 정해진 우선순위는 이슈의 우선순위 항목에 기록한다. 그리고 그 순서에 따라 작업의 순서를 정한다(아
이템 8: 가장 중요한 문제에 집중하기 참조). 일반적으로 버그의 우선순위는 개발자나 프로젝트 리더가 정한
다. 최종 사용자들은 한결같이 자신이 제출한 버그가 가장 중요하다고 주장하는 경향이 있기 때문이다. 우선순
위를 제대로 정해서 기록해두면, 모든 문제가 다 중요하거나 자신이 제기한 이슈가 최우선이라고 여기는 관리
자, 고객, 다른 팀 개발자들의 아우성에 대처하는 데 도움된다.
●● 이슈마다 이해 당사자를 명시하면 개발 팀원이 문제를 해결하는 데 좀 더 노력하게 만들 수 있고, 제품 담당자
가 해당 이슈의 우선순위를 결정하는 데 도움된다. 어떤 조직은 이슈에 관련된 이해 당사자 옆에 그 사람이 기
여한 연간 수익을 태깅하기도 한다(예: ‘25만 달러짜리 고객인 애크미가 제출한 이슈임’).
●● 이슈가 발생한 환경에 대한 정보도 기록해두면 쉽게 드러나지 않는 버그를 재현하는 데 도움된다. 환경을 기록
할 때 PC의 일련번호나 BIOS 날짜, 시스템에 설치된 모든 라이브러리의 버전 등과 같이 불필요한 정보까지
너무 자세하게 작성하도록 요구하면 사용자들은 너무 부담스러워서 이러한 항목들을 그냥 건너뛴다. 따라서 가
장 핵심적인 부분만 상세하게 물어본다. 가령 웹 기반 앱이라면 브라우저에 대한 정보가 중요하다. 모바일 앱이
라면 디바이스 제조사와 모델 정보가 필요하다. 기왕이면 이러한 세부 사항을 제출하는 절차를 소프트웨어를
통해 자동화하면 좋다.
이슈 추적 시스템을 사용할 때 현재 진행 상황을 문서로 남긴다는 기분으로 활용하는 것이 좋다. 대
부분의 이슈 추적 시스템에서는 이슈를 기록하는 항목의 끝부분에 자유롭게 댓글을 남길 수 있
는 기능을 제공한다. 이러한 항목을 통해 버그를 분석하고 해결한 방법이나, 해결하다가 막힌
사실 등을 문서화한다. 그러면 조직의 작업 현황을 명확히 들여다볼 수 있다. 프로그램의 동작
을 추적하거나 로그를 남길 때 사용한 명령어도 정확히 기록해둔다. 그러면 다음 날 반복하거
나 일 년 후 자신 또는 동료가 비슷한 버그를 해결할 때 큰 도움이 된다. 또한 나중에 팀원이나
상사에게 그동안 작업한 내용을 설명해야 할 때 일주일 내내 버그를 잡느라 눈이 침침해지고
정신이 몽롱해진 상태에서도 쉽게 기억을 떠올릴 수 있다.
기억할 사항
●● 모든 문제를 이슈 추적 시스템으로 처리한다.
●● 이슈를 작성할 때 문제를 재현하는 방법을 정확하고, 간결하고, 관련된 사항을 모두 담아서, 구체적인 예제
와 함께 기록한다.
●● 이슈의 심각한 정도와 우선순위를 정해서 이에 맞게 작업 일정을 짠다.
●● 이슈 추적 시스템을 통해 작업 현황을 문서화한다. 
22 이펙티브 디버깅
아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기
요즘은 인터넷이 항상 연결된 환경에서 작업하는 경우가 대부분이다. 그러다 갑자기 인터넷을
사용할 수 없게 되면 개발자의 생산성이 크게 떨어진다. 작성하던 코드가 제대로 작동하지 않
으면 대부분 인터넷을 검색하거나 동료 개발자에게 물어보는 방식으로 해결하기 때문이다.
이렇게 해결책을 찾기 위해 웹을 검색할 때 에러 메시지 부분을 큰따옴표로 묶어서 검색 창에
입력하면 훨씬 효과적이다. 검색 엔진은 큰따옴표로 묶인 문구가 그대로 담긴 페이지를 검색
하기 때문에 결과의 정확도가 훨씬 높아진다. 또한 문제와 관련된 라이브러리나 미들웨어 이
름, 클래스나 메서드 이름, 에러 코드 등도 함께 검색어로 입력하면 좋다. 검색하려는 함수의
이름이 특이할수록 찾기 쉽다. 가령 BitBlt로 검색할 때보다 PlgBlt로 검색할 때 더 좋은 결과
를 얻을 수 있다. 또한 동의어도 함께 넣으면 좋다. 가령 아무런 반응 없이 멈춘 현상freeze을 표
현할 때 ‘먹통hang’과 같은 단어도 같이 적어주고, 비활성화disabled된 상태를 표현할 때 ‘회색으로
반전greyed’과 같은 표현도 함께 적어주면 좋다.
API를 호출하는 과정에서 발생하는 문제들은 대부분 다른 사람들이 사용하는 방법을 참고하
는 것만으로도 쉽게 해결할 수 있다. 해당 함수를 사용하는 오픈소스 소프트웨어를 찾아서 전
달된 매개변수가 어떻게 초기화되고, 여기서 반환한 값을 어떻게 해석하는지 살펴본다. 이처
럼 코드를 검색할 때는 구글과 같은 범용 검색 엔진보다는 블랙 덕 오픈 허브 코드 서치Black Duck
Open Hub Code Search처럼 코드 검색에 특화된 검색 엔진을 사용하면 더 좋은 결과를 얻을 수 있다.
예를 들어 블랙 덕에서 mktime을 검색하고, 모든 라이브러리 선언과 정의들을 일일이 살펴보
지 않도록 특정한 프로젝트에 대한 결과만 나오게 필터링하면 다음과 같은 코드를 찾을 수 있
다.*
nowtime = mktime(time-tm_year+1900, time-tm_mon+1,
time-tm_mday, time-tm_hour, time-tm_min,
time-tm_sec);
결과를 보면 localtime과 달리 mktime 함수는 연도를 인수로 받는데, 1900년도부터 지난 연
도의 수가 아니라 연도를 표현하는 네 자리 숫자를 지정해야 하며, 월도 1부터 시작하는 형태
* 옮긴이_ 현재 코드 검색 서비스가 중단됐다. 인터넷에서 ‘코드 검색 엔진’으로 검색해보면 다양한 서비스를 찾을 수 있으며, https://
searchcode.com도 추천한다.
231장 고차원 전략
로 표현한다는 것을 알 수 있다. 이러한 사항은 해당 함수에 대한 문서를 제대로 읽어보지 않으
면 놓치기 쉽다.
검색 결과를 살펴볼 때 해당 페이지를 어느 사이트에서 호스팅하는지도 주의 깊게 본다. 스택
오버플로StackOverflow와 같은 스택익스체인지StackExchange 계열의 커뮤니티에는 개발자들이 관심을
가질만한 노하우가 상당히 많이 축적되어 있기 때문에 원하는 답을 찾을 확률도 높다. 스택오
버플로에서 검색할 때 채택된 답변뿐만 아니라 답변을 계속 찾고 있는 항목도 함께 참고한다.
또한 답변에 달린 댓글도 꼭 읽어본다. 에러를 피하기 위한 다른 기법과 같은 새로운 정보가 이
항목을 통해 업데이트되기 때문이다.
아무리 열심히 검색해봐도 만족할만한 답을 찾지 못했다면 애초에 방향을 잘못 잡았을 수도 있
다. 유명한 라이브러리나 소프트웨어에 관련된 문제라면 누군가 최소한 한 명은 같은 문제를
겪었을 것이다. 따라서 문제에 관련된 내용을 웹에서 찾지 못했다면, 애초에 문제의 원인을 잘
못 진단했을 수도 있다. 가령 버그가 있어서 충돌이 발생했다고 생각했던 API 함수가 사실은
그 함수에 전달한 데이터에 문제가 있었기 때문일 수도 있다.
검색으로 해결책을 찾지 못했다면 스택오버플로에 직접 질문을 올린다. 단, 포럼에 질문을 올
릴 때 다른 이들이 그대로 복사해서 컴파일하기만 해도 문제를 직접 확인할 수 있는 수준의 짤
막한 코드를 제공해야 하는데(SSCCEshort, self-contained, correct (compilable and runnable) example), 이렇게 하
려면 상당한 노력을 기울여야 한다(아이템 10: 효율적으로 문제 상황 재현하기 참조). 사용하는 언
어에 따라 SourceLair나 JSFiddle과 같은 온라인 IDE를 통해 문제를 재현하는 예제를 라이
브 형태로 제공할 수도 있다. 특정한 언어나 기술에 대한 예제를 잘 작성하기 위한 구체적인
방법은 sscce.org를 참고한다. 또한 에릭 레이몬드Eric Raymond가 작성한 「똑똑하게 질문하기」
(How To Ask Questions The Smart Way, http://www.catb.org/~esr/faqs/smart-
questions.html)도 함께 읽어보면 좋다.
경험상 질문을 잘 작성해서 예제와 함께 올리는 것만으로도 많은 문제를 쉽게 해결했다. 물론
이처럼 정성 들여 질문한다고 해서 당장 해답을 구할 수 있는 것은 아니지만, 예제를 잘 작성하
면 실력 있는 개발자의 눈에 잘 띄기 때문에 이들이 직접 실행해보고 해결책을 제시해줄 가능
성이 높아진다.
발생한 문제가 오픈소스 라이브러리나 프로그램 코드에 존재하는 버그 때문이라면, 해당 오픈
소스 프로그램의 개발자에게 직접 연락하거나, 그 프로젝트의 버그 추적 시스템에 이슈를 올린
24 이펙티브 디버깅
다. 이슈를 올리기 전에 비슷한 버그 리포트가 이미 등록되어 있는지부터 확인하고, 없다면 해
당 문제를 재현할 수 있도록 정보를 상세하게 기록하는 것이 좋다. 해당 프로그램에 대한 버그
추적 시스템이 없다면 개발자에게 직접 이메일을 보낸다. 이메일로 문의할 때는 버그 추적 시
스템에 리포팅할 때보다 공손하게 메일을 작성하는 것이 좋다. 대부분의 오픈소스 개발자는 이
러한 지원 활동을 돈을 받고 하는 것이 아니기 때문이다.
기억할 사항
●● 	발생한 에러에 대한 해결책을 웹에서 검색할 때 에러 메시지를 큰따옴표로 묶어서 입력한다.
●● 	스택익스체인지 사이트에 올라온 답변을 참고한다.
●● 위 두 방법으로 해결하지 못했다면 직접 질문을 올리거나 버그 리포팅 시스템에 이슈를 등록한다.
아이템 3: 선행 조건과 후행 조건 만족 여부 확인하기
전자 제품을 수리할 때는 가장 먼저 전원의 연결 상태부터 확인한다. 전원 공급 장치로부터 전
기가 나오는지, 그래서 회로에 잘 전달되는지부터 점검한다. 의외로 이 부분이 문제의 원인인
경우가 많다. 소프트웨어도 마찬가지로 특정 루틴에 진입하기 전의 값(선행 조건precondition, 프
로그램의 상태와 입력값)과 루틴을 실행하고 나온 시점의 값(후행 조건postcondition, 프로그램 상
태와 반환값)을 확인하는 것만으로도 문제가 발생한 부분을 찾아내는 경우가 많다. 선행 조건
이 잘못됐다면 이 값을 설정한 곳에 문제가 있는 것이고, 후행 조건이 잘못됐다면 루틴 내부에
문제가 있는 것이다. 두 값 모두 이상이 없다면 다른 지점에서 원인을 찾아야 한다.
루틴이 시작하는 부분이나 루틴을 호출하는 지점, 또는 핵심 알고리즘이 실행되는 지점에 중단
점breakpoint을 설정한다(아이템 30: 코드와 데이터 중단점 활용하기 참조). 선행 조건을 만족하는지 확
인할 때는 의심스런 코드에서 사용하는 전역변숫값과 매개변숫값, 호출한 메서드가 속한 객체
등과 같이 알고리즘에 영향을 주는 요소들을 주의 깊게 살펴본다. 그중에서도 특히 다음 사항
에 주의한다.
251장 고차원 전략
●● 	값이 null이 아니어야 하는 부분이 null인지 살펴본다.
●● 	수학 함수에 전달한 값이 그 함수에서 다루는 범위 안에 있는지 확인한다. 가령 log 함수에 전달한 값이 0보다
큰지 확인한다.
●● 	루틴에 전달한 객체나 구조체, 배열의 내부를 살펴보고, 그 안에 필요한 내용이 담겨 있는지 확인한다. 이렇게
확인하면 포인터 값이 잘못된 부분도 찾을 수 있다.
●● 	변수의 값이 정상 범위 안인지 확인한다. 간혹 변수를 초기화하지 않으면 6.89851e-308이나 61007410
과 같은 이상한 값이 들어 있는 경우가 있다.
●● 	무작위 추출 검사spot-check를 통해 루틴에 전달한 데이터 구조의 무결성을 검사한다. 예를 들어 맵map에 키와
값이 제대로 들어가 있는지, 이중 링크드 리스트를 정확하게 탐색할 수 있는지 등을 확인한다.
루틴의 끝이나 루틴을 호출한 지점 뒤, 또는 핵심 알고리즘의 실행이 끝나는 지점에도 중단점
을 설정한다. 그러고 나서 루틴의 실행 결과를 확인한다.
●● 	계산된 결과가 적합한지, 예상 범위 안에 있는지 확인한다.
●● 	값이 정상 범위에 있다면 실제로 결과가 정확한지 확인한다. 직접 손으로 계산해보거나(아이템 38: 의심스
런 코드를 검토하고 손으로 실행해보기 참조), 이미 알려진 정상 값과 비교해보거나, 다른 도구로 계산해보면
된다.
●● 	루틴의 부작용side effect이 예상했던 것인지, 의심스런 코드로 인해 데이터가 손상되거나 엉뚱한 값으로 설정되
어 있지 않은지 확인한다. 이러한 확인 작업은 데이터 구조를 탐색하는 동작에 관련된 상태 정보를 해당 알고
리즘의 내부에서 직접 관리할 때 특히 중요하다.
●● 알고리즘에서 사용하는 (파일 핸들이나 락lock과 같은) 자원을 정상적으로 반환했는지 확인한다.
고차원 연산이나 설정 작업을 수행하는 부분도 이와 같은 기법으로 확인할 수 있다. SQL문이
테이블을 제대로 구성하는지 확인할 때도, 연산의 실행 전후에 테이블의 구조와 값을 비교해보
면 된다. 파일을 다루는 코드를 확인할 때는 입력 파일과 출력 파일을 검사한다. 웹 서비스에서
수행하는 연산을 디버깅할 때는 웹 서비스마다 다루는 입력과 출력값을 살펴본다. 데이터센터
전체에 발생한 문제를 해결할 때는 관련된 장비와 네트워킹, DNS, 공유 스토리지, 데이터베이
스, 미들웨어 등을 비롯한 그 장비와 관련된 요소들을 검사한다. 항상 추측하지 말고 직접 확인
한다.
기억할 사항
●● 루틴의 선행 조건과 후행 조건을 주의 깊게 검사한다. 
26 이펙티브 디버깅
아이템 4: 문제 발생 지점부터 버그를 추적하거나,
프로그램 시작 지점부터 버그를 찾아나가기
문제의 원인을 찾는 방법은 크게 두 가지다. 하나는 문제가 발생한 지점에서 출발해서 근본 원
인을 추적하는 것이고, 다른 하나는 애플리케이션이나 시스템이 시작하는 시점부터 문제의 원
인이 나타날 때까지 찾는 것이다. 둘 중 어느 방법이 효과적인지는 발생한 문제의 성격에 따라
다르다. 때로는 한 가지 방식으로 추적하다가 막힐 때 다른 방식으로 시도하면 도움된다.
문제가 명확히 드러났다면 그 문제가 발생한 지점에서 출발하는 것이 좋다. 이 방식은 다음과
같이 세 가지 시나리오로 진행할 수 있다.
첫째, 프로그램이 갑자기 죽는 문제를 해결할 때는 디버거로 그 프로그램을 실행하거나, 프로그램
이 충돌하는 시점에 디버거를 붙이거나, 문제가 발생한 후 출력된 메모리 덤프 값을 읽어보는
방법(아이템 35: 코어 덤프 다루기 참조)으로 원인을 쉽게 찾을 수 있다. 이때 프로그램이 충돌하는
시점의 변숫값을 확인해보고, 문제가 발생하는 이유가 변수의 값이 null이기 때문인지, 잘못
된 값이 들어가서인지, 아니면 변수를 제대로 초기화하지 않았기 때문인지 등을 파악한다. 어
떤 시스템에서는 변수의 기본값으로 (bad food를 의미하는) 0xBAADF00D와 같이 눈에 쉽
게 띄는 값을 사용하기 때문에 변수의 초기화 여부를 쉽게 발견할 수 있다. 이러한 용도로 사용
하는 값에 대한 자세한 내용은 위키백과의 Magic Number 항목*을 참고한다. 변수에 값이 잘
못 들어간 지점을 발견했다면 더 세부적으로 파고들어 그렇게 된 근본 원인을 찾는다. 충돌을
발생시키는 루틴 내부를 들여다볼 수도 있고, 루틴을 호출하는 흐름상에서 잘못된 인수를 전달
하는 지점을 찾는 방식으로 접근할 수도 있다(아이템 3: 선행 조건과 후행 조건 만족 여부 확인하기, 아
이템 32: 루틴 사이의 호출 흐름 추적하기 참조).
둘째, 프로그램이 갑자기 죽지 않고 그냥 멈춘 뒤 아무런 반응이 없다면 문제가 발생한 지점에서 원
인을 추적하는 상향식 방식을 앞에서 소개한 방법과는 조금 다르게 진행한다. 먼저 프로그램을
디버거에서 실행하다가 문제가 발생하는 지점에 멈추거나(아이템 30: 코드와 데이터 중단점 활용하기
참조), 메모리 덤프를 출력하도록 설정한다(아이템 35: 코어 덤프 다루기 참조). 때에 따라 직접 작
성한 코드가 아닌 외부 라이브러리에서 실행하는 코드에서 문제가 발생할 수 있다. 이럴 때는
루틴을 호출하는 흐름을 따라가보면서 프로그램을 멈추게 만드는 루프가 있는지 검사한다. 이
* https://goo.gl/nnnyns
432장 범용적인 디버깅 기법
범용적인 디버깅 기법
디버깅 방법은 시스템의 기반 기술과 개발 플랫폼에 따라 다르다. 그러나 몇 가지 기법은 다양
한 환경에서 공통적으로 사용할 수 있다.
아이템 9: 성공적인 디버깅을 위한 마음가짐
소프트웨어는 굉장히 복잡하게 구성되는 경우가 많다. 기계식 시계의 부품은 100개가 넘고 일
반 가정집의 전기 배선은 이보다 몇 배 많은 부품으로 구성된다. 이에 반해 일상에서 우리가 흔
히 볼 수 있는 소프트웨어 시스템은 수만 개의 명령문이 복잡하게 얽혀 있다. 극단적인 예로
A380 항공기를 구성하는 부품이 약 400만 개인 데 반해, 리눅스 커널 코드는 900만 줄이 넘
는다. 이렇게 복잡한 코드를 다루기 위해서는 마음을 단단히 먹어야 한다.
우선 소프트웨어에서 발생한 문제는 항상 찾아서 고칠 수 있다고 믿는다. 마음가짐에 따라 디버깅 작업의
생산성이 달라진다. 전문가들은 이를 ‘인지된 도전과 인지된 기술 사이의 경기’라고 부른다. 문
제를 해결할 수 있다는 믿음이 없으면 마음이 흔들리거나 포기하기 쉽다. 그래서 문제의 원인
을 해결하기보다는 겉으로 드러난 증상만 임시방편으로 해결하여 상황을 더 악화시킬 수도 있
다. 마음가짐을 바르게 하려면 다음 사항을 명심한다.
발생한 문제를 재현할 수 있다면 (이 책에서 소개하는 기법을 통해) 반드시 해결할 수 있다. 재
현하기 힘든 문제도 해결할 방법은 얼마든지 찾을 수 있다. 디버깅 작업은 주로 다음과 같은 두
CHAPTER 2
44 이펙티브 디버깅
가지 수단을 토대로 진행한다. 하나는 문제 해결에 필요한 데이터를 최대한 확보하는 것이고,
다른 하나는 이러한 데이터를 처리할 수 있는 고성능 컴퓨터를 사용하는 것이다. 데이터를 확
보할 때는 문제의 현상, 로그, 소스 코드를 살펴보고 필요하다면 기계어 명령어까지 조사한다.
또한 소프트웨어 스택의 원하는 지점에 로그를 좀 더 자세히 출력하는 문장(또는 모니터링을
위한 탐침)을 추가한 뒤, 다른 도구나 간단한 스크립트를 통해 대량으로 수집한 데이터를 분석
하여 문제의 원인을 찾는다. 마치 원하는 물고기를 잡기 위해 그물을 던질 때 그동안 축적된 경
험을 반영하여 그물의 폭과 깊이를 적절히 조절하는 것과 같다.
디버깅 작업을 효과적으로 수행하려면 시간도 충분히 확보해야 한다. 디버깅은 프로그래밍보다
훨씬 많은 노력을 기울여야 하는 작업이다. 프로그램의 로직과 이로 인해 발생하는 (저수준low
level의) 현상을 항상 머릿속에 그리고 있어야 하기 때문이다. 또한 문제를 효과적으로 재현하기
위해 필요한 환경과 중단점, 로깅, 창/화면, 테스트 케이스 등을 정확히 설정해야 한다. 그리고
목표로 삼은 버그를 완전히 잡거나 최소한 이를 위해 해야 할 일을 정확히 파악하기도 전에 디
버깅을 중단하여 그동안 투자한 시간을 그냥 날리지 않도록 주의한다.
디버깅 작업은 단순하지 않기 때문에 고도로 집중해야 한다. 두뇌가 어떤 활동에 완전히 몰두하여
빠져든 몰입flow 상태에 들어가려면 어느 정도의 시간이 걸린다. 몰입이라는 개념을 소개한 미
할리 칙센트미하이Mihály Csíkszentmihályi에 따르면 몰입 상태에 들어가면 모든 감정이 현재 수행하
는 작업에 맞춰진다고 한다. 몰입 상태에서 느낀 성취감을 통해 작업에 대한 지속성과 효율성
을 더욱 높일 수 있다. 성취감과 지속성, 효율성은 복잡한 시스템을 디버깅하는 과정에서 겪는
엄청난 어려움을 극복하는 데 핵심적인 요소다. 팝업 메시지가 뜨거나, 전화벨이 울리거나, 메
신저로 대화하거나, SNS에 올라온 새 소식을 읽거나, 회사 동료가 말을 걸면 집중력이 흐트러
져서 몰입의 효과를 누릴 수 없게 된다. 이러한 방해 요인은 모두 제거한다. 필요 없는 애플리
케이션은 모두 종료하고, 전화기는 진동 모드로 설정하고, 모니터나 책상 앞(직책이 높다면 문
앞)에 ‘방해 금지’라는 팻말을 걸어 놓은 뒤에 작업한다.
또 다른 효과적인 기법으로 어려운 문제를 만나면 잠을 자는 방법이 있다. 연구 결과에 따르면 수면
상태에 있는 동안에는 언뜻 보기에는 관련이 없어 보이는 경로를 따라 뉴런이 연결된다고 한
다. 이러한 뉴런의 활동은 디버깅 작업에 큰 도움을 줄 수 있다. 더 이상 해결의 실마리가 보이
지 않는 막다른 상황에서 완전히 새로운 관점으로 디버깅 전략을 수정함으로써 돌파구를 찾을
수 있다. 잠은 이렇게 새로운 관점을 떠올리기 위해 반드시 필요하다. 단, 효과를 제대로 보려
면 적절한 방법에 따라 수면을 취해야 한다. 잠자리에 들기 직전까지 열심히 작업하다가 잠들
452장 범용적인 디버깅 기법
면 문제에 대한 새로운 해결책을 떠올리는 데 필요한 모든 데이터가 의식 속에 그대로 전달된
다. 하지만 작업을 중간에 포기하고 맥주 한 잔 마신 뒤에 잠들면 아무런 효과를 볼 수 없다. 또
한 잠을 충분히 자야 수면 상태에서 두뇌의 잠재의식 영역이 제시한 여러 가지 아이디어를 다
음 날 아침에 일어난 후에 활동할 두뇌의 의식 영역이 제대로 받아들일 수 있다.
디버깅은 결코 쉽지 않다. 디버깅 작업을 효과적으로 수행하기 위해서는 끈기가 필요하다. 컴
퓨터는 근본적으로 모든 동작을 예측할 수 있는(결정적인deterministic) 방식으로 작동한다. 따라서
오류를 찾아낼 때까지 문제를 얼마든지 깊이 파고 들어가 볼 수 있다. 그런데 프로그래밍의 표
현력과 효율성을 높이기 위해 고수준의 관점에서 보면 무작위로 작동하는 것처럼 보이게 하는
비결정성nondeterminism을 도입했다. 이렇게 동작을 예측하기 힘든 비결정적인 에러를 디버깅할
때는 컴퓨터의 빠른 속도와 프로그래밍 능력을 최대한 활용하여 에러를 발견할 때까지 수많은
경우의 수를 하나씩 검사하면 된다. 결국 디버깅 실패의 원인은 대부분 끈기 부족에 있다. 테스
트 케이스를 충분히 만들지 않았거나, 로그 파일을 제대로 보지 않았거나, 다른 접근 방식을 찾
지 않았기 때문이다.
마지막으로 실전에서 효과적으로 디버깅할 수 있도록 디버깅 환경과 도구, 관련 기법을 익히는
데 끊임없이 노력해야 한다. 소프트웨어에 들어가는 기술의 복잡도가 지속적으로 증가하는 상
황에서 경쟁력을 확보하려면 이 방법 외에 다른 방법은 없다. 돌이켜보면, 필자가 디버깅 과정
에서 저지른 실수의 원인 중 대다수는 디버깅 환경을 제대로 갖추는 데 충분한 투자를 하지 않
았기 때문이다. 그래서 다음과 같은 기법을 적용하지 못했다.
●● 	효과적인 테스트 케이스를 충분히 확보한다(아이템 10: 효율적으로 문제 상황 재현하기 참조).
●● 	버그를 재현하는 과정을 자동화한다.
●● 	로그 파일을 분석하기 위한 스크립트를 작성한다.
●● 	API나 언어의 기능이 실제로 작동하는 과정을 확실히 파악한다.
저자의 경험에 따르면 디버깅 작업에 필요한 에너지를 적재적소에 투입하는 것만으로도 디버
깅 생산성을 크게 향상시킬 수 있었다. 일단 이렇게 자세와 환경을 제대로 갖추고 나면 버그를
굉장히 빠르게 해결할 수 있다.
46 이펙티브 디버깅
기억할 사항
●● 모든 문제는 찾아서 고칠 수 있다고 믿는다.
●● 디버깅 작업에 필요한 시간을 충분히 확보한다.
●● 작업에 최대한 집중할 수 있도록 환경을 마련한다.
●● 힘든 문제를 해결할 때는 효과적인 수면을 취한다.
●● 포기하지 않는다.
●● 디버깅 환경, 도구, 기법을 익히는 데 꾸준히 노력한다. 
아이템 10: 효율적으로 문제 상황 재현하기
디버깅을 효과적으로 수행하려면 문제가 발생하는 상황을 쉽고 안정적으로 재현할 수 있어야
한다. 이유는 다음과 같다. 첫째, 클릭 한 번으로 항상 문제를 재현할 수 있다면 오류가 나타나
게 만들기 위해 허둥대느라 시간을 낭비할 필요 없이 곧바로 원인을 해결하는 데 집중할 수 있
다. 둘째, 문제 상황을 쉽게 재현할 수 있는 수단을 마련하면 다른 이에게 도움을 청할 때 문제
상황을 설명하기도 쉽다(아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기 참조). 마지
막 세 번째 이유는 오류를 수정한 뒤에 문제가 발생하는 상황을 여러 번 반복하여 더 이상 문제
가 발생하지 않는다는 것을 확인함으로써 문제를 완벽하게 해결했다는 것을 증명하기도 쉽다.
문제를 재현하는 예제(테스트 케이스)를 간결하게 만들면 작업의 효율성을 크게 높일 수 있다. 기본
적으로 예제는 간단해야 한다. 문제를 재현하는 범위에서 최대한 간결하게 만든다. 더 좋은 방
법은 SSCCE라 부르는 기준(아이템 1: 모든 문제를 이슈 추적 시스템으로 관리하기 참조)에 따라 간결
할 뿐만 아니라, 독립적으로 실행할 수 있고, 컴파일해서 실행할 수 있도록 정확하게 예제를 작
성하는 것이다. 이렇게 예제를 최대한 간결하게 만들면 코드에서 불필요한 부분을 탐색하느
라 낭비하는 시간을 절약할 수 있다. 간결한 예제는 장황하게 작성된 예제보다 실행 속도도 훨
씬 빠르다. 특히 성능 오버헤드가 엄청난 디버깅 모드로 실행할 때 그 차이가 극명하게 드러
난다.
예제를 최대한 간결하게 만들기 위해서는 하향식top-down으로 접근할 수도 있고 상향식bottom-up
으로 접근할 수도 있다(아이템 4: 문제 발생 지점부터 버그를 추적하거나, 프로그램 시작 지점부터 버그를
찾아나가기 참조). 상황에 가장 적합한 방식으로 진행한다. 코드에 의존 관계가 많다면 상향식으
472장 범용적인 디버깅 기법
로 접근하여 처음부터 새로 시작하는 것이 바람직하다. 반면 문제의 원인을 파악하기 힘들 때
는 테스트 케이스를 작성해서 경우의 수를 줄여나가는 하향식 접근 방식이 좋다.
상향식으로 접근할 때는 문제의 원인에 대한 가설을 세운 뒤(예를 들어, 특정한 API를 호출하
는 부분에 문제가 있다고 가정한 뒤) 이를 확인하는 테스트 케이스를 만든다. 구체적인 예를 살
펴보기 위해 저자가 예전에 파일 입력을 처리하는 프로그램을 디버깅할 때 경험한 사례를 소개
한다. 이 프로그램은 27,000줄 이상의 긴 코드를 입력하면 속도가 굉장히 느려지는 문제가 있
었다. 소스 코드에서 시스템 콜을 호출하는 부분을 면밀히 살펴보니, 입력 파일을 읽는 과정에
파일 스트림의 오프셋을 반환하는 함수인 tellg를 호출하는 부분에 뭔가 문제가 있다는 의심이
들었다. 이를 확인하기 위해 테스트 코드를 간략히 작성해서 실행한 결과 이 부분이 원인이라
는 확신이 들었고(아이템 58: 실행 흐름 추적하기 참조), 이때 작성한 코드는 tellg에서 발생하는 오
류를 우회하는 코드(래퍼 클래스)를 테스트할 때도 유용하게 써먹었다.
ifstream in(fname.c_str(), ios::binary);
do {
	 (void)in.tellg();
} while ((val = in.get()) != EOF); 
하향식으로 접근할 때는 문제를 재현하는 시나리오에 따라 진행하면서 더 이상 문제가 발생하
지 않을 때까지 의심스런 부분을 하나씩 제거해나간다. 이때 이진 탐색을 적용하면 상당히 효
과적이다. 예를 들어 특정한 HTML 파일을 읽을 때 브라우저가 이상하게 작동하는 문제가 발
생하는 경우를 생각해보자. 우선 HTML의 헤드(head) 영역을 제거하고 실행한다. 그래도
문제가 사라지지 않으면 이번에는 보디(body) 부분도 삭제한다. 이렇게 할 때 문제가 사라
진다면 다시 보디 부분을 원래대로 집어넣고 보디 영역의 절반만 삭제해서 실행한다. 원인을
확실히 찾을 때까지 이 과정을 반복하면서 문제가 발생하는 영역을 좁혀나간다. 삭제한 부분이
문제와 관련이 없어서 이전 상태로 되돌아갈 때는 코드 편집기의 실행 취소/되돌리기undo 기능
을 활용하면 작업을 효율적으로 수행할 수 있다.
예제를 간결하게 만들면 이를 독립적인 형태로 만들기도 쉬워진다. 독립적인 형태란 다른 라이브
러리나 헤더 파일, CSS 파일, 웹 서비스와 같은 외부 요소에 의존하지 않고 이 예제만으로 다
른 곳에서 문제를 똑같이 재현할 수 있다는 뜻이다. 작성한 테스트 케이스를 실행하기 위해 외
부 요소가 필요하다면 예제와 함께 번들 형태로 묶어둔다. 이때 파일의 위치를 절대 경로로
48 이펙티브 디버깅
표현하거나 IP 주소를 코드에 직접 박아두지 말고 코드의 위치에 최대한 독립적인 방식으로
표현한다. 예를 들어, /home/susan/resources/file.css 대신 ../resources/file.css로 표기
하고, http://193.92.66.100:8081/myService 대신 http://localhost:8081/myService로
표현한다. 이렇게 예제를 독립적인 형태로 작성하면 고객의 환경이나 다른 플랫폼(가령 리눅
스 환경에서 테스트한 것을 윈도우)에서 테스트하기도 쉽고, QA 게시판에 올리기도 편하고
(아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기 참조), 벤더에게 도움을 요청하기
도 좋다.
또한 반복적으로 실행할 수 있는 환경도 마련해야 한다. 현재 작성 중인 코드와 실행 환경에서 원인
을 도저히 찾을 수 없다면 엉뚱한 곳에서 버그를 찾고 있기 때문일 수도 있다. 예를 들어 소프
트웨어 인스톨러를 디버깅하는 경우를 생각해보자. 디버깅 과정에서 인스톨러를 실행할 때마
다 현재 사용하는 운영체제의 설정이 지저분해진다. 특히 디버깅 대상이 인스톨러일 때는 이러
한 상황을 반드시 피해야 한다. 이럴 때는 시스템을 언제든지 완전 초기 상태로 되돌릴 수 있도
록 설치 환경을 가상 머신 이미지로 만들면 좋다. 설치에 실패할 때마다 언제든지 다시 처음 상
태로 돌아가면 된다. 이때 완전한 형태의 가상 머신 대신 도커Docker와 같은 컨테이너 기술 또는
운영체제 수준에서 제공하는 가상화 기법을 활용해도 된다. 여기에 앤서블Ansible, CFEngine,
셰프Chef, 퍼핏Puppet, 솔트Salt와 같은 시스템 설정 관리 도구를 도입하면 더 좋다. 이러한 도구를
활용하면 고수준의 명령으로 원하는 시스템 설정을 안정적으로 생성할 수 있고 배포 환경과 테
스팅 환경, 개발 환경의 호환성을 유지하기도 편하다. 또한 소프트웨어의 진화 과정을 관리할
때와 똑같은 방식으로 이러한 환경의 진화 과정을 관리할 수 있다.
오류가 발생하는 버전에 대한 복사본을 언제든지 만들 수 있는 기능도 필요하다. 이를 위해 가장 먼저 해
야 할 일은 소프트웨어를 깃Git과 같은 버전 관리 도구에 넣는 것이다. 그러고 나서 소프트웨어
를 빌드할 때 소스 코드에 대한 식별자를 포함하도록 설정한다. 예를 들어 다음과 같이 셸 명령
을 실행하면 가장 최근에 발생한 커밋에 대한 짧은 깃 해시 값으로 변수를 초기화하는 문장을
출력한다. 이 문장을 소스 코드에 집어넣으면 된다.
git log -n 1 --format='const string version = %h;'  
앞의 명령을 실행하면 다음과 같이 출력된다.
const string version = 035cd45;  
733장 범용 도구를 활용한 기법
범용 도구를 활용한 기법
특정한 영역에 특화된 디버깅 도구를 사용하는 것이 훨씬 편리하고 효율적이지만 범용 도구를
활용하는 것도 나름대로 장점이 있다. 여러 언어와 플랫폼으로 구성된 시스템에서 문제가 발
생했을 때 당장 활용할 수 있기 때문이다. 이 장에서 소개하는 범용 도구들은 오래 전 유닉스를
주로 사용하던 시절에 나온 것이지만 지금까지도 GNU/리눅스와 윈도우, macOS 등과 같은
다양한 시스템에서 사용할 수 있다. 이러한 도구들이 제공하는 유연성, 효율성, 범용성만 감안
하더라도 사용법을 익히는 데 시간을 투자할 만한 가치가 있다. 이러한 범용 도구의 사용법에
대해 조슈아 레비가 간결하게 정리한 「The Art of Command Line」(https://github.com/
jlevy/the-art-of-command-line)을 추천한다. 이 장에서는 독자들이 유닉스 명령줄과 정
규표현식에 대한 기초는 갖추고 있다고 가정하고, 곧바로 여러 가지 도구를 디버깅 작업에 활
용하는 기법을 소개한다.
아이템 22: 유닉스 명령줄 도구로 디버깅 데이터 분석하기
디버깅을 하다 보면 지금까지 아무도 겪어보지 못한 문제에 맞닥뜨릴 수 있다. 소프트웨어를 작
성할 때 사용하던 IDE에 이러한 문제를 분석하는데 딱 맞는 기능이 없을 수 있다. 이럴 때는 유
닉스 명령줄 도구로 해결하면 된다. 유닉스 명령줄 도구는 범용적인 용도로 제작된 것이기 때문
에 파이프라인을 잘 활용하면 텍스트 형태의 데이터를 손쉽게 분석할 수 있다.
CHAPTER 3
74 이펙티브 디버깅
줄 단위의 텍스트 포맷으로 작성된 데이터 스트림은 다양한 종류의 데이터를 다루는 데 필요
한 최소한의 포맷으로만 구성되어 있다. 따라서 디버깅 과정에서 다루는 프로그램의 소스 코드
나 로그, 버전 관리 히스토리, 파일 목록, 심벌 테이블, 아카이브 내용, 에러 메시지, 테스트 결
과, 프로파일 결과 등을 비롯한 다양한 형태의 데이터를 표현할 수 있다. 디버깅 데이터를 다루
는 작업을 날마다 반복적으로 수행하다 보면 펄, 파이썬, 루비, 혹은 윈도우의 파워셸PowerShell과
같은 스위스 다용도칼처럼 강력한 스크립팅 언어를 사용하고 싶을 수도 있다. 이러한 스크립팅
언어에서 디버깅 데이터를 쉽게 가져올 수 있는 인터페이스를 제공하고, 인터랙티브 방식으로
명령을 작성하는 데 익숙하다면 이러한 스크립팅 언어를 활용하는 것도 좋은 방법이다. 그렇지
않으면 데이터 처리에 필요한 기능을 모두 갖춘 프로그램을 직접 작성해야 한다. 이렇게 작업
하다 보면 굉장히 번거로울 뿐만 아니라 모든 작업을 일일이 처리하느라 디버깅에 관련된 중요
한 통찰을 얻을 기회를 잃어버릴 수도 있다.
이보다 바람직한 방법은 여러 가지 유닉스 도구들을 파이프라인을 이용하여 간결하고 효율적
으로 잘 엮어서 셸 프롬프트에서 실행할 수 있는 형태로 만드는 것이다. 셸 명령줄에서 제공
하는 최신 편집 기능을 활용하면 명령을 하나씩 조합하는 방식으로 원하는 작업을 구성할 수
있다.
이번 아이템에서는 유닉스 명령을 활용하여 디버깅 데이터를 처리하는 방법에 대해 소개한다.
명령줄과 정규표현식의 사용법에 대해 익숙하지 않은 독자는 관련 주제에 대한 온라인 튜토리
얼을 참고하기 바란다. 또한 각각의 명령에서 제공하는 여러 가지 옵션에 대해 자세히 알고 싶
다면 man 명령에 궁금한 옵션의 이름을 인수로 지정하여 실행하면 볼 수 있다.
현재 사용하는 운영체제의 종류에 따라 유닉스 명령줄을 곧바로 사용할 수 있거나, 어렵지 않게
필요한 환경을 구축할 수도 있다. 유닉스나 macOS 시스템이라면 곧바로 터미널 창을 띄우기
만 하면 된다. 윈도우 환경이라면 시그윈Cygwin을 설치하는 것이 가장 무난하다. 시그윈은 윈도
우 환경에서 여러 가지 유닉스 도구와 강력한 패키지 관리 시스템을 쉽게 사용할 수 있도록 윈
도우에 맞게 포팅하여 제공한다. 또한 이 절에서 소개하는 도구 중에서 macOS 환경에서 기본
적으로 제공하지 않는 것들은 홈브루Homebrew 패키지 관리자를 이용하면 손쉽게 설치할 수 있다.
디버깅에 필요한 작업을 여러 가지 유닉스 도구를 조합하여 한 줄짜리 명령으로 만들어 처리하
는 경우가 많은데, 이러한 명령은 대체로 가져오기fetching, 선택하기selecting, 처리하기processing, 정
리하기summarizing와 같은 패턴에 따라 작성한다. 이때 원하는 작업의 성격에 따라 파이프라인 연
753장 범용 도구를 활용한 기법
산(|)을 활용하여 한쪽 작업에서 출력된 결과를 다른 작업의 입력으로 전달하는 방식으로 여러
가지 작업을 하나로 연결한다.
디버깅 과정에서 다루는 데이터는 대부분 텍스트 포맷으로 되어 있기 때문에 사용하는 도구의
표준 입력으로 데이터를 그대로 전달할 수 있다. 이렇게 처리하기 힘든 포맷으로 구성되어 있
다면 그 포맷을 지원하는 도구를 사용해야 한다. 가령 오브젝트 파일을 분석하고 처리할 때는
nm(유닉스), dumpbin(윈도우), javap(자바) 등과 같은 명령을 활용하면 된다. 예를 들어 작
성하던 C 또는 C++ 프로그램이 갑자기 종료했다면 해당 오브젝트 파일을 nm 명령으로 실행
하여 exit 함수가 호출된 지점을 찾을 수 있다.
# 지정한 파일 이름으로 시작하는 모든 오브젝트 파일에 담긴 심벌을 출력한다.
nm -A *.o |
# U exit로 끝나는 라인을 출력한다.
grep 'U exit$' 
그러면 다음과 같은 결과를 얻을 수 있는데, 소스 코드를 일일이 뒤지는 것보다 훨씬 정확하게
찾아낼 수 있다.
cscout.o:		 U exit
error.o:		U exit
idquery.o:		 U exit
md5.o:			 U exit
pdtoken.o:		 U exit 
아카이브로 묶인 파일에 담긴 내용을 보려면 tar, jar, ar와 같은 명령을 사용하면 된다. 검색해
야 할 파일이 너무 많다면 find 명령으로 디버깅 작업에 직접적으로 관련된 파일만 골라낸다.
데이터를 웹에서 가져와야 한다면 curl이나 wget 명령을 활용한다. 또한 dd 명령(과 특수한
형태의 파일인 /dev/zero)을 사용하거나, yes나 jot을 이용하여 데이터를 인위적으로 생성하
여 간단히 벤치마크를 실행할 수도 있다. 마지막으로 컴파일러에서 발생한 여러 가지 에러 메
시지를 처리할 때는 21이나 2파일이름과 같은 구문을 사용하여 컴파일러에서 생성한 표준
에러를 표준 출력이나 다른 파일로 리다이렉션할 수 있다. 예를 들어, 어떤 함수의 인터페이스
를 변경했을 때 그 함수에 관련된 모든 부분에 변경 사항을 반영하기 위해 관련된 파일을 모두
찾으려면, 다음과 같이 여러 가지 명령을 파이프라인으로 조합하는 방법으로 해결할 수 있다.
76 이펙티브 디버깅
# 수정된 함수에 관련된 모든 파일을 빌드한다.
# 이때 빌드 과정에서 발생하는 표준 에러를 표준 출력으로 리다이렉션한다.
make -k 21 |
# 에러가 발생한 파일의 이름을 출력한다.
awk -F: '/no matching function for call to Myclass::myFunc/
{ print $1}' |
# 각 파일이 한 번씩만 출력되도록 정렬한다.
sort -u 
로그 파일을 비롯한 디버깅 과정에서 분석해야 할 파일들은 대체로 불필요한 데이터가 많이 담
겨 있다. 실제로 필요한 부분은 전체 라인 중에서 일부분이거나, 각 라인의 특정한 영역일 수도
있다. 이때 각 항목이 고정된 폭으로 구성되어 있거나, 각 항목이 공백과 같은 구분자로 나눠져
있다면 cut 명령을 활용하여 각 라인에서 원하는 항목만 골라낼 수 있다. 그렇지 않고 데이터
가 필드 단위로 깔끔하게 구분되어 있지 않다면 원하는 부분을 정규표현식으로 표현해서 sed
명령의 인수로 전달하여 실행하는 방식으로 골라내면 된다.
파일에서 원하는 라인만 골라낼 때 가장 흔히 사용하는 명령으로 grep이 있다. 원하는 항목을
담은 행만 정확히 골라내도록 정규표현식을 작성해서 인수로 지정하고, -v 옵션을 설정하여
필요 없는 행을 걸러내는 방식으로 처리하면 된다. 2장의 아이템 21: 비슷한 문제 모두 고치기에서
모든 나눗셈 연산 중에서 분모가 sizeof가 아닌 것만 찾아낼 때 이 방식으로 처리한 바 있다.
grep -r '/' . |
grep -v '/ sizeof' 
찾으려는 항목이 정규표현식이 아닌 일반 문자열이거나, 데이터가 이전 단계의 처리 과정을 통
해 파일에 저장되어 있다면, 고정된 길이의 문자열에 특화된 grep인 fgrep에 -f 옵션을 지정
하여 실행한다. 선택 기준이 조금 복잡하다면 awk 패턴 표현식으로 표현한다. 작업을 하다 보
면 원하는 결과를 얻기 위해 방금 소개한 여러 가지 기법들을 조합해야 하는 경우가 많다. 이를
테면, grep으로 원하는 라인을 찾아서, grep -v로 필요 없는 부분을 제거한 뒤, awk로 각 행
에서 원하는 필드만 골라낸다. 좀 더 구체적인 예를 들면, 시스템 트레이스 출력 결과에서 제대
로 열린 파일의 이름만 화면에 표시하려면 다음과 같은 명령을 구성한다.
773장 범용 도구를 활용한 기법
# open을 호출한 라인을 출력한다.
grep '^open(' trace.out |
# open 함수를 호출한 부분 중에서 실패한(-1을 반환한) 부분은 제거한다.
grep -v '= -1' |
# 인용 부호로 구분된 항목 중에서 두 번째 필드만 출력한다.
awk -F '{print $2}' 
(이 문장을 하나의 awk 명령으로 작성할 수도 있지만, 여기에 나온 것처럼 단계별로 하나씩 조
합해나가는 방식으로 작성하는 것이 훨씬 쉽다.)
데이터를 처리할 때 라인을 특정한 필드를 기준으로 정렬해야 하는 경우가 많다. sort 명령은
이러한 작업을 처리할 수 있도록 다양한 정렬 기준과 그 값의 타입, 출력 순서 등을 지정하는
옵션을 제공한다. 결과를 원하는 형태로 정렬했다면 각 항목의 개수를 효율적으로 알아내야 한
다. 이 작업은 uniq 명령에 -c 옵션을 지정하여 실행하면 된다. 또한 마지막에 sort 명령을 더
추가하여 앞에서 정렬한 결과를 다른 방식으로 정렬해야 하는 경우도 많다. 예를 들어, 뽑아낸
결과 중에서 가장 많이 등장한 항목을 알아내려면 -n 옵션을 지정한 sort 명령을 마지막에 추
가하면 된다. 때로는 동일한 프로그램을 다양한 환경에서 실행한 결과를 서로 비교해야 하는
경우도 있다. 가령 원래는 동작이 서로 같아야 하는 실행 결과들에 차이점이 있는지 확인할 때
는 diff로 명령을 사용하면 된다. 또한 두 개의 정렬된 리스트를 비교할 때는 comm 명령을 활
용할 수 있다. 이때도 awk를 사용하면 좀 더 복잡한 작업을 처리할 수 있다. 예를 들어, 자원
누수 현상을 분석하기 위해 obtainResource을 호출하고 나서 releaseResource를 호출하지
않는 파일을 모두 찾아내는 작업을 다음과 같이 처리할 수 있다.
# 첫 번째 집합에만 나온 레코드 나열하기
comm -23 (
# obtainResource가 포함된 파일 이름 나열하기
grep -rl obtainResource . | sort) (
# releaseResource가 포함된 파일 이름 나열하기
grep -rl releaseResource . | sort) 
(여기서 (...)구문은 배시bash 셸의 확장 기능으로, 명령에서 파일 형태의 인수를 받는 자리에서
파일명 대신에 프로세스를 인수로 지정할 수 있다. 이렇게 괄호 안에 지정한 프로세스는 백그
라운드로 실행된 후에 결과가 파이프로 연결되어 명령의 인수로 전달된다.)
78 이펙티브 디버깅
일반적으로 이렇게 처리한 결과로 나온 데이터는 당장 사용하기에는 너무 방대한 경우가 많다.
가령 로그 파일에서 에러가 발생했다는 것을 표시하는 라인을 일일이 살펴볼 필요 없이 이러한
라인의 전체 개수만 알면 되는 경우도 있다. 놀랍게도 wc 명령에 -l 옵션을 지정하여 결과의
개수만 세는 것만으로도 문제를 해결하는 경우가 많다. 결과 목록에서 상위 또는 하위 10개 항
목만 알아내고 싶다면 head나 tail 명령을 사용하면 된다. 또한 사람들이 가장 많이 건드리는
파일을 알아내고 싶다면 다음과 같이 명령을 실행한다.
# 각 라인에 대한 최종 수정 사항을 나열한다.
git blame --line-porcelain Foo.java |
# 작성자를 알아낸다.
grep '^author' |
# 같은 이름을 한데 묶도록 정렬한다.
sort |
# 각각의 이름이 나타난 횟수를 센다.
uniq -c |
# 나타난 횟수 기준으로 정렬한다.
sort -rn |
# 최상위 결과만 나열한다.
head 
로그 파일을 살펴볼 때는 tail 명령을 활용하면 편하다(아이템 23: 명령줄 도구 옵션과 관용 표현 활용
하기, 아이템 56: 애플리케이션 로그 파일 분석하기 참조). 그리고 방대한 결과를 자세히 들여다볼 때
는 more나 less 명령에 파이프로 연결해서 보면 좀 더 편하다. 두 명령 모드 스크롤 업/다운
기능과 문자열을 검색하는 기능을 제공한다. 이것만으로 부족할 때는 awk 명령을 활용한다.
주로 sum += $3처럼 특정한 필드 값을 모두 더하는 작업을 하나의 명령으로 처리할 때 유용
하다. 좀 더 구체적으로 예를 들면, 웹 서버 로그를 분석하여 요청된 횟수와 각 요청에 대해 전
송된 바이트 수의 평균값을 구하는 작업을 다음과 같이 명령을 작성하여 처리할 수 있다.
awk '
# HTTP 결과 코드가 200(success)이라면
# 10번 필드(전송된 바이트 수)를 합산한다.
$9 == 200 {sum += $10; count++}
# 입력이 끝나면 요청 횟수와 전송된 바이트의 평균값을 출력한다.
END {print count, sum / count}' /var/log/access.log 
유닉스를 구성하는 요소들이 아무리 훌륭하더라도 이들을 하나로 엮어주는 기능이 없다면 아
793장 범용 도구를 활용한 기법
무런 쓸모가 없다. 본Bourne 셸에서 제공하는 기능을 활용하면 여러 요소를 엮을 수 있다. 경우
에 따라 동일한 명령을 인수만 바꿔서 여러 차례 실행해야 할 수도 있다. 이럴 때는 그 명령에
전달할 여러 개의 인수를 xargs의 입력으로 전달한다. 일반적으로 find로 파일의 목록을 구한
다음, 각각에 대해 명령을 실행하는 작업은 xargs로 처리하는 패턴으로 구성한다. 굉장히 흔히
사용되는 패턴이기 때문에 (윈도우의 경우 ‘Program Files’ 폴더처럼) 이름에 공백이 담긴 파
일을 처리할 수 있도록 두 명령 모두 데이터가 공백 대신 널null 문자로 끝나도록 지정하는 옵션
(-print0과 -0)을 제공한다. 구체적인 예를 살펴보기 위해, foo.cpp 파일을 수정한 뒤에 생
성된 로그 파일 중에서 ‘access failure’라는 문자열이 가장 많이 등장하는 파일을 찾는 경우를
살펴보자. 이 작업은 다음과 같이 파이프라인으로 여러 명령을 연결하여 처리할 수 있다.
# /var/log/acme 폴더에 있는 파일 중에서
# foo.cpp를 변경한 후에 수정된 파일을 모두 찾는다.
find /var/log/acme -type f -cnewer ~/src/acme/foo.cpp -print0 |
# fgrep으로 'access failure'가 나타난 횟수를 센다.
xargs -0 fgrep -c 'access failure' |
# 앞에서 처리한 결과를 :를 기준으로 필드를 구분하고
# 두 번째 필드를 기준으로 내림차순으로 정렬한다.
sort -t: -rn -k2 |
# 최상위 결과를 출력한다.
head -1 
데이터를 처리하는 과정이 이보다 복잡하다면 파이프로 인수를 while read 루프에 연결한다
(놀랍게도 본 셸에서는 모든 제어 구조에 파이프를 연결해서 데이터를 전달하거나 가져올 수
있다). 예를 들어, 발생한 문제의 원인이 시스템의 동적 링크 라이브러리(DLL)을 업데이트한
것과 관련이 있다고 의심되어 windows/system32 디렉터리에 있는 모든 DLL 파일의 버전
정보를 나열하고 싶다면 다음과 같이 처리할 수 있다.
# 모든 DLL 파일을 찾는다.
find /cygdrive/c/Windows/system32 -type f -name *.dll |
# 각각의 파일에 대해
while read f ; do
# 윈도우상의 경로를 구하고
# 그중 를 이스케이프(특수 문자로 처리하는) 포맷으로 변환한다.
wname=$(cygpath -w $f | sed 's///g')
# WMIC 질의를 통해 이름과 버전을 구한다.
wmic datafile where Name=$wname get name, version
80 이펙티브 디버깅
done |
# 헤더와 빈 줄을 제거한다.
grep windows 
명령이 제대로 실행되지 않는다면 데이터를 다루는 데 필요한 작업들을 한 단계씩 차근차근 작
성한다.
기억할 사항
●● 	텍스트 형태의 레코드를 가져오고, 선택하고, 처리하고, 정리하는 유닉스 명령을 이용하여 디버깅 데이터를
분석한다.
●● 	여러 가지 유닉스 명령을 파이프라인으로 조합하는 방식으로 복잡한 분석 작업을 간단히 처리할 수 있다. 
아이템 23: 명령줄 도구 옵션과 관용 표현 활용하기
디버깅하는 과정에서 ‘Missing foo’와 같이 이해하기 힘든 에러 메시지가 발생했을 때, 소스 코
드의 어느 부분에서 그 메시지가 발생했는지 찾고 싶다면 다음과 같이 명령을 실행하면 된다.
fgrep -lr 'Missing foo' . 
여기서 하위 디렉터리를 파고 들며 파일을 탐색하도록 -r 옵션을 지정하고, 인수로 지정한 에
러 메시지가 담긴 파일을 나열하도록 -l 옵션을 지정했다. grep의 가장 큰 장점은 모든 텍스트
에 대해 적용할 수 있기 때문에 코드에 사용한 프로그래밍 언어의 종류에 관계 없이 에러 메시
지를 찾아낼 수 있다. 이 기법은 애플리케이션을 여러 가지 언어로 작성했거나 IDE 환경에 올
려서 작업할 여유가 없을 때 특히 유용하다. 참고로 방금 사용한 fgrep의 -r 옵션은 GNU에서
확장한 것으로 원래 형태의 유닉스 명령을 추구하는 이들은 좋아하지 않는 기능이다. 따라서
이 옵션이 제공되지 않는 시스템에서 작업할 때는 다음과 같이 파이프라인을 활용하면 앞에서
실행한 명령과 똑같이 작업할 수 있다.
find . -type f |
xargs fgrep -l 'Missing foo' 
994장 디버거 활용법
디버거 활용법
디버거debugger는 소프트웨어의 실행 과정을 상세하게 분석하기 위한 특수한 용도로 사용하는
도구로, 현존하는 소프트웨어 중에서도 디버거만큼 CPU와 OS로부터 특별한 기능을 지원받으
면서 시스템을 종횡무진하는 것은 없다. 그만큼 디버거는 전문가들이 인정하는 특별한 가치를
갖고 있다. 하지만 효과적으로 활용하는 방법을 모르면 디버거가 제공하는 여러 혜택을 제대로
누릴 수 없다. 따라서 이 장에서 설명하는 기술을 꼼꼼히 읽고 적용하기 바란다. 그중에서 현재
자신이 사용하는 언어와 환경에 맞지 않거나 이미 알고 있는 기술은 지나쳐도 좋다.
디버거는 다양한 형태로 나와 있다. 독립 애플리케이션 형태로 제공되기도 하고, IDE의 부가
기능으로 통합된 형태로 제공되기도 한다. 이처럼 형태는 다양하지만 사용하는 방법이나 기술
은 대부분 비슷하다. 이 장에서 소개하는 디버깅 예제들은 현재 가장 널리 사용되는 세 가지 환
경에 맞춰 구성했다. 자바와 스칼라 코드를 디버깅하는 예제는 이클립스 IDE로, C, C++, 비
주얼 C#, 비주얼 베이직 등으로 작성된 코드를 디버깅하는 예제는 비주얼 스튜디오 IDE로,
그리고 C, C++, D, 고Go, 오브젝티브-C, 포트란, 자바, 오픈CL C, 파스칼, 어셈블리, 모듈
라-2, 에이다Ada 등과 같은 언어로 작성된 프로그램을 디버깅하는 예제는 유닉스 환경의 gdb
를 이용한다. 구글 크롬에서 제공하는 자바스크립트 디버거를 비롯한 여기서 소개하지 않는 디
버거에서 이 장에서 소개한 기법을 적용하고 싶다면 해당 디버거의 매뉴얼을 참조하기 바란다.
현재 사용하는 디버거에서 이 장에서 소개하는 기술을 적용하는 데 필요한 기능을 제공하지 않
는다면 다른 디버거로 교체하는 것도 좋다.
CHAPTER 4
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기
『이펙티브 디버깅』 맛보기

Recomendados

『이펙티브 디버깅』 - 디버깅 지옥에서 탈출하는 66가지 전략과 기법 por
『이펙티브 디버깅』 - 디버깅 지옥에서 탈출하는 66가지 전략과 기법『이펙티브 디버깅』 - 디버깅 지옥에서 탈출하는 66가지 전략과 기법
『이펙티브 디버깅』 - 디버깅 지옥에서 탈출하는 66가지 전략과 기법복연 이
7.8K vistas74 diapositivas
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기 por
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기복연 이
6.1K vistas253 diapositivas
​『함수형 반응형 프로그래밍』 맛보기 por
​『함수형 반응형 프로그래밍』 맛보기​『함수형 반응형 프로그래밍』 맛보기
​『함수형 반응형 프로그래밍』 맛보기복연 이
2.8K vistas151 diapositivas
​『골빈해커의 3분 딥러닝』 맛보기 por
​『골빈해커의 3분 딥러닝』 맛보기​『골빈해커의 3분 딥러닝』 맛보기
​『골빈해커의 3분 딥러닝』 맛보기복연 이
6K vistas81 diapositivas
『파이썬 라이브러리를 활용한 머신러닝』 맛보기 por
『파이썬 라이브러리를 활용한 머신러닝』 맛보기『파이썬 라이브러리를 활용한 머신러닝』 맛보기
『파이썬 라이브러리를 활용한 머신러닝』 맛보기복연 이
2.7K vistas210 diapositivas
​『9가지 사례로 익히는 고급 스파크 분석(2판) 』 맛보기 por
​『9가지 사례로 익히는 고급 스파크 분석(2판) 』 맛보기​『9가지 사례로 익히는 고급 스파크 분석(2판) 』 맛보기
​『9가지 사례로 익히는 고급 스파크 분석(2판) 』 맛보기복연 이
2.1K vistas93 diapositivas

Más contenido relacionado

La actualidad más candente

『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기 por
『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기
『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기복연 이
1.1K vistas97 diapositivas
[NDC2014] 반응적 라이브 개발 por
[NDC2014] 반응적 라이브 개발[NDC2014] 반응적 라이브 개발
[NDC2014] 반응적 라이브 개발ChangKyu Song
6.4K vistas111 diapositivas
버그 트래킹 시스템 Mantis의 사용 그리고 예제 por
버그 트래킹 시스템 Mantis의 사용 그리고 예제버그 트래킹 시스템 Mantis의 사용 그리고 예제
버그 트래킹 시스템 Mantis의 사용 그리고 예제Kiyoung Moon
10.9K vistas190 diapositivas
『Effective Unit Testing』 - 맛보기 por
『Effective Unit Testing』 - 맛보기『Effective Unit Testing』 - 맛보기
『Effective Unit Testing』 - 맛보기복연 이
626 vistas94 diapositivas
소프트웨어 설계 악취: 기술 부채 관리 방법 por
소프트웨어 설계 악취: 기술 부채 관리 방법소프트웨어 설계 악취: 기술 부채 관리 방법
소프트웨어 설계 악취: 기술 부채 관리 방법Jay Park
9.9K vistas39 diapositivas
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기 por
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기Wonha Ryu
1.3K vistas51 diapositivas

La actualidad más candente(16)

『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기 por 복연 이
『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기
『9가지 사례로 익히는 고급 스파크 분석』 - 맛보기
복연 이1.1K vistas
[NDC2014] 반응적 라이브 개발 por ChangKyu Song
[NDC2014] 반응적 라이브 개발[NDC2014] 반응적 라이브 개발
[NDC2014] 반응적 라이브 개발
ChangKyu Song6.4K vistas
버그 트래킹 시스템 Mantis의 사용 그리고 예제 por Kiyoung Moon
버그 트래킹 시스템 Mantis의 사용 그리고 예제버그 트래킹 시스템 Mantis의 사용 그리고 예제
버그 트래킹 시스템 Mantis의 사용 그리고 예제
Kiyoung Moon10.9K vistas
『Effective Unit Testing』 - 맛보기 por 복연 이
『Effective Unit Testing』 - 맛보기『Effective Unit Testing』 - 맛보기
『Effective Unit Testing』 - 맛보기
복연 이626 vistas
소프트웨어 설계 악취: 기술 부채 관리 방법 por Jay Park
소프트웨어 설계 악취: 기술 부채 관리 방법소프트웨어 설계 악취: 기술 부채 관리 방법
소프트웨어 설계 악취: 기술 부채 관리 방법
Jay Park9.9K vistas
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기 por Wonha Ryu
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
리소스 중심의 서든어택2 실시간 메모리 프로파일링 시스템 개발기
Wonha Ryu1.3K vistas
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규 por ChangKyu Song
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
[NDC12] 변화량 분석을 중심으로 한 저비용 고효율의 지속가능한 코드퀄리티 관리법 - 송창규
ChangKyu Song3.2K vistas
게임프로그래머에게 배우는 C#1권(버전1) por Kiyoung Moon
게임프로그래머에게 배우는 C#1권(버전1)게임프로그래머에게 배우는 C#1권(버전1)
게임프로그래머에게 배우는 C#1권(버전1)
Kiyoung Moon2.6K vistas
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅 por DongMin Choi
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
DongMin Choi12.6K vistas
도도와 파이썬: 좋은 선택과 나쁜 선택 por Jc Kim
도도와 파이썬: 좋은 선택과 나쁜 선택도도와 파이썬: 좋은 선택과 나쁜 선택
도도와 파이썬: 좋은 선택과 나쁜 선택
Jc Kim11.8K vistas
나의 오픈소스 사용기 por 주호 강
나의 오픈소스 사용기나의 오픈소스 사용기
나의 오픈소스 사용기
주호 강2.2K vistas
현업 엔지니어의 시각에서 본 알고리즘 공부의 장점과 단점 por Wonha Ryu
현업 엔지니어의 시각에서 본 알고리즘 공부의 장점과 단점현업 엔지니어의 시각에서 본 알고리즘 공부의 장점과 단점
현업 엔지니어의 시각에서 본 알고리즘 공부의 장점과 단점
Wonha Ryu4.5K vistas
NDC2017 언리얼엔진4 디버깅 101 - 게임 기획자, 프로그래머가 버그와 만났을 때 사용할 수 있는 지침들 por 영욱 오
NDC2017 언리얼엔진4 디버깅 101 - 게임 기획자, 프로그래머가 버그와 만났을 때 사용할 수 있는 지침들NDC2017 언리얼엔진4 디버깅 101 - 게임 기획자, 프로그래머가 버그와 만났을 때 사용할 수 있는 지침들
NDC2017 언리얼엔진4 디버깅 101 - 게임 기획자, 프로그래머가 버그와 만났을 때 사용할 수 있는 지침들
영욱 오6.8K vistas
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019 por devCAT Studio, NEXON
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
devCAT Studio, NEXON14.1K vistas
Tdd retro agile_korea_게시용 por Sangcheol Hwang
Tdd retro agile_korea_게시용Tdd retro agile_korea_게시용
Tdd retro agile_korea_게시용
Sangcheol Hwang2.2K vistas
파이썬을 배워야하는 이유 발표자료 - 김연수 por Yeon Soo Kim
파이썬을 배워야하는 이유 발표자료 - 김연수파이썬을 배워야하는 이유 발표자료 - 김연수
파이썬을 배워야하는 이유 발표자료 - 김연수
Yeon Soo Kim25.6K vistas

Similar a 『이펙티브 디버깅』 맛보기

『클라우드 시스템을 관리하는 기술』 - 맛보기 por
『클라우드 시스템을 관리하는 기술』 - 맛보기『클라우드 시스템을 관리하는 기술』 - 맛보기
『클라우드 시스템을 관리하는 기술』 - 맛보기복연 이
1.9K vistas159 diapositivas
커뮤니티와 함께한 예비개발자 성장기- 조성수님 por
커뮤니티와 함께한 예비개발자 성장기- 조성수님커뮤니티와 함께한 예비개발자 성장기- 조성수님
커뮤니티와 함께한 예비개발자 성장기- 조성수님NAVER D2
7.7K vistas84 diapositivas
EMOCON 2015 - 품질과 테스트는 다르다 por
EMOCON 2015 - 품질과 테스트는 다르다EMOCON 2015 - 품질과 테스트는 다르다
EMOCON 2015 - 품질과 테스트는 다르다이상한모임
897 vistas21 diapositivas
Software engineer가 되기 위한 여정 por
Software engineer가 되기 위한 여정Software engineer가 되기 위한 여정
Software engineer가 되기 위한 여정Aree Oh
157 vistas41 diapositivas
애자일의 모든것 por
애자일의 모든것애자일의 모든것
애자일의 모든것KH Park (박경훈)
45.4K vistas175 diapositivas
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th... por
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...Kay Kim
1.1K vistas69 diapositivas

Similar a 『이펙티브 디버깅』 맛보기(20)

『클라우드 시스템을 관리하는 기술』 - 맛보기 por 복연 이
『클라우드 시스템을 관리하는 기술』 - 맛보기『클라우드 시스템을 관리하는 기술』 - 맛보기
『클라우드 시스템을 관리하는 기술』 - 맛보기
복연 이1.9K vistas
커뮤니티와 함께한 예비개발자 성장기- 조성수님 por NAVER D2
커뮤니티와 함께한 예비개발자 성장기- 조성수님커뮤니티와 함께한 예비개발자 성장기- 조성수님
커뮤니티와 함께한 예비개발자 성장기- 조성수님
NAVER D27.7K vistas
EMOCON 2015 - 품질과 테스트는 다르다 por 이상한모임
EMOCON 2015 - 품질과 테스트는 다르다EMOCON 2015 - 품질과 테스트는 다르다
EMOCON 2015 - 품질과 테스트는 다르다
이상한모임897 vistas
Software engineer가 되기 위한 여정 por Aree Oh
Software engineer가 되기 위한 여정Software engineer가 되기 위한 여정
Software engineer가 되기 위한 여정
Aree Oh157 vistas
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th... por Kay Kim
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
Kay Kim1.1K vistas
(독서광) 필독! 개발자 온보딩 가이드 por Jay Park
(독서광) 필독! 개발자 온보딩 가이드(독서광) 필독! 개발자 온보딩 가이드
(독서광) 필독! 개발자 온보딩 가이드
Jay Park1.1K vistas
[HYSS 2016] 쉽고 빠르게 시작하는 Volatility Plugin 개발 por 동현 김
[HYSS 2016] 쉽고 빠르게 시작하는 Volatility Plugin 개발[HYSS 2016] 쉽고 빠르게 시작하는 Volatility Plugin 개발
[HYSS 2016] 쉽고 빠르게 시작하는 Volatility Plugin 개발
동현 김2.2K vistas
[Kerference] 쉽고 빠르게 시작하는 Volatility plugin 개발 - 김동현(BoB) por NAVER D2
[Kerference] 쉽고 빠르게 시작하는 Volatility plugin 개발 - 김동현(BoB)[Kerference] 쉽고 빠르게 시작하는 Volatility plugin 개발 - 김동현(BoB)
[Kerference] 쉽고 빠르게 시작하는 Volatility plugin 개발 - 김동현(BoB)
NAVER D24.5K vistas
C++ 코드 품질 관리 비법 por 선협 이
C++ 코드 품질 관리 비법C++ 코드 품질 관리 비법
C++ 코드 품질 관리 비법
선협 이27.1K vistas
청강대 특강 - 프로젝트 제대로 해보기 por Chris Ohk
청강대 특강 - 프로젝트 제대로 해보기청강대 특강 - 프로젝트 제대로 해보기
청강대 특강 - 프로젝트 제대로 해보기
Chris Ohk3.2K vistas
Agile sw development 101 por Kiwon Kyung
Agile sw development 101Agile sw development 101
Agile sw development 101
Kiwon Kyung174 vistas
소프트웨어설계론 por JeongDong Kim
소프트웨어설계론소프트웨어설계론
소프트웨어설계론
JeongDong Kim1.5K vistas
프로젝트 Xxx에 적용하고 싶은 개발방법 por 도형 임
프로젝트 Xxx에 적용하고 싶은 개발방법프로젝트 Xxx에 적용하고 싶은 개발방법
프로젝트 Xxx에 적용하고 싶은 개발방법
도형 임2.5K vistas
Dev rookie codecomplete-1 por 대영 노
Dev rookie codecomplete-1Dev rookie codecomplete-1
Dev rookie codecomplete-1
대영 노139 vistas
애자일 프랙티스 por 한 경만
애자일 프랙티스애자일 프랙티스
애자일 프랙티스
한 경만71 vistas
초보개발자의 TDD 체험기 por Sehun Kim
초보개발자의 TDD 체험기초보개발자의 TDD 체험기
초보개발자의 TDD 체험기
Sehun Kim3.7K vistas
(독서광) 프로그래머의 뇌 por Jay Park
(독서광) 프로그래머의 뇌(독서광) 프로그래머의 뇌
(독서광) 프로그래머의 뇌
Jay Park1.5K vistas
오픈소스 소프트웨어 개발, 어디서부터 시작하는게 좋을까요? @ CNU(충남대) por Jaewon Choi
오픈소스 소프트웨어 개발, 어디서부터 시작하는게 좋을까요? @ CNU(충남대)오픈소스 소프트웨어 개발, 어디서부터 시작하는게 좋을까요? @ CNU(충남대)
오픈소스 소프트웨어 개발, 어디서부터 시작하는게 좋을까요? @ CNU(충남대)
Jaewon Choi3.3K vistas

Más de 복연 이

『아마존 웹 서비스 인 액션』 맛보기 por
『아마존 웹 서비스 인 액션』 맛보기『아마존 웹 서비스 인 액션』 맛보기
『아마존 웹 서비스 인 액션』 맛보기복연 이
782 vistas109 diapositivas
『밑바닥부터 시작하는 딥러닝』 - 미리보기 por
『밑바닥부터 시작하는 딥러닝』 - 미리보기『밑바닥부터 시작하는 딥러닝』 - 미리보기
『밑바닥부터 시작하는 딥러닝』 - 미리보기복연 이
10.4K vistas87 diapositivas
『오픈스택 인 액션』 - 맛보기 por
『오픈스택 인 액션』 - 맛보기『오픈스택 인 액션』 - 맛보기
『오픈스택 인 액션』 - 맛보기복연 이
955 vistas142 diapositivas
『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기 por
『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기
『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기복연 이
499 vistas28 diapositivas
『고성능 파이썬』 - 맛보기 por
『고성능 파이썬』 - 맛보기『고성능 파이썬』 - 맛보기
『고성능 파이썬』 - 맛보기복연 이
1.4K vistas106 diapositivas
『데이터 분석을 통한 네트워크 보안』 - 맛보기 por
『데이터 분석을 통한 네트워크 보안』 - 맛보기『데이터 분석을 통한 네트워크 보안』 - 맛보기
『데이터 분석을 통한 네트워크 보안』 - 맛보기복연 이
889 vistas66 diapositivas

Más de 복연 이(18)

『아마존 웹 서비스 인 액션』 맛보기 por 복연 이
『아마존 웹 서비스 인 액션』 맛보기『아마존 웹 서비스 인 액션』 맛보기
『아마존 웹 서비스 인 액션』 맛보기
복연 이782 vistas
『밑바닥부터 시작하는 딥러닝』 - 미리보기 por 복연 이
『밑바닥부터 시작하는 딥러닝』 - 미리보기『밑바닥부터 시작하는 딥러닝』 - 미리보기
『밑바닥부터 시작하는 딥러닝』 - 미리보기
복연 이10.4K vistas
『오픈스택 인 액션』 - 맛보기 por 복연 이
『오픈스택 인 액션』 - 맛보기『오픈스택 인 액션』 - 맛보기
『오픈스택 인 액션』 - 맛보기
복연 이955 vistas
『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기 por 복연 이
『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기
『빠르게 훑어보는 구글 클라우드 플랫폼』 - 맛보기
복연 이499 vistas
『고성능 파이썬』 - 맛보기 por 복연 이
『고성능 파이썬』 - 맛보기『고성능 파이썬』 - 맛보기
『고성능 파이썬』 - 맛보기
복연 이1.4K vistas
『데이터 분석을 통한 네트워크 보안』 - 맛보기 por 복연 이
『데이터 분석을 통한 네트워크 보안』 - 맛보기『데이터 분석을 통한 네트워크 보안』 - 맛보기
『데이터 분석을 통한 네트워크 보안』 - 맛보기
복연 이889 vistas
『크리스 크로퍼드의 인터랙티브 스토리텔링』 맛보기 por 복연 이
『크리스 크로퍼드의 인터랙티브 스토리텔링』 맛보기『크리스 크로퍼드의 인터랙티브 스토리텔링』 맛보기
『크리스 크로퍼드의 인터랙티브 스토리텔링』 맛보기
복연 이1.5K vistas
『누워서 읽는 알고리즘』 - 미리보기 por 복연 이
『누워서 읽는 알고리즘』 - 미리보기『누워서 읽는 알고리즘』 - 미리보기
『누워서 읽는 알고리즘』 - 미리보기
복연 이2.2K vistas
『Modern PHP』 - 미리보기 por 복연 이
『Modern PHP』 - 미리보기『Modern PHP』 - 미리보기
『Modern PHP』 - 미리보기
복연 이1.9K vistas
『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 구성 지도 por 복연 이
『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 구성 지도『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 구성 지도
『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 구성 지도
복연 이966 vistas
『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 미리보기 por 복연 이
『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 미리보기『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 미리보기
『프로젝트 성패를 결정짓는 데이터 모델링 이야기』 - 미리보기
복연 이2.5K vistas
어서 와! 번역은 처음이지? por 복연 이
어서 와! 번역은 처음이지?어서 와! 번역은 처음이지?
어서 와! 번역은 처음이지?
복연 이11.5K vistas
『풀스택 개발자를 위한 MEAN 스택 입문』 - 미리보기 por 복연 이
『풀스택 개발자를 위한 MEAN 스택 입문』 - 미리보기『풀스택 개발자를 위한 MEAN 스택 입문』 - 미리보기
『풀스택 개발자를 위한 MEAN 스택 입문』 - 미리보기
복연 이1.9K vistas
『게임 매니악스 액션 게임 알고리즘』 - 미리보기 por 복연 이
『게임 매니악스 액션 게임 알고리즘』 - 미리보기『게임 매니악스 액션 게임 알고리즘』 - 미리보기
『게임 매니악스 액션 게임 알고리즘』 - 미리보기
복연 이2.5K vistas
『안드로이드 시큐리티 인터널』 - 미리보기 por 복연 이
『안드로이드 시큐리티 인터널』 - 미리보기『안드로이드 시큐리티 인터널』 - 미리보기
『안드로이드 시큐리티 인터널』 - 미리보기
복연 이901 vistas
『임베디드 안드로이드』 - 미리보기 por 복연 이
『임베디드 안드로이드』 - 미리보기『임베디드 안드로이드』 - 미리보기
『임베디드 안드로이드』 - 미리보기
복연 이2.1K vistas
『게임을 움직이는 기술과 발상』 - 미리보기 por 복연 이
『게임을 움직이는 기술과 발상』 - 미리보기『게임을 움직이는 기술과 발상』 - 미리보기
『게임을 움직이는 기술과 발상』 - 미리보기
복연 이797 vistas
개앞맵시 '건방진' 사용 설명서 por 복연 이
개앞맵시 '건방진' 사용 설명서개앞맵시 '건방진' 사용 설명서
개앞맵시 '건방진' 사용 설명서
복연 이5.4K vistas

『이펙티브 디버깅』 맛보기

  • 3. 디오미디스 스피넬리스 지음 남기혁 옮김 디버깅 지옥에서 탈출하는 66가지 전략과 기법
  • 4. 이펙티브 디버깅 디버깅 지옥에서 탈출하는 66가지 전략과 기법 초판발행 2017년 5월 20일 지은이 디오미디스 스피넬리스 / 옮긴이 남기혁 / 펴낸이 김태헌 펴낸곳 한빛미디어 (주) / 주소 서울시 마포구 양화로7길 83 한빛미디어(주) IT출판부 전화 02 – 325 – 5544 / 팩스 02 – 336 – 7124 등록 1999년 6월 24일 제10 – 1779호 / ISBN 978-89-6848-778-1 93000 총괄 전태호 / 책임편집 김창수 / 기획·편집 이복연 / 교정 홍성신 디자인 표지 김연정 내지 여동일 조판 이경숙 영업 김형진, 김진불, 조유미 / 마케팅 박상용, 송경석, 변지영 / 제작 박성우, 김정우 이 책에 대한 의견이나 오탈자 및 잘못된 내용에 대한 수정 정보는 한빛미디어(주)의 홈페이지나 아래 이메일로 알려주십시오. 잘못된 책은 구입하신 서점에서 교환해 드립니다. 책값은 뒤표지에 표시되어 있습니다. 한빛미디어 홈페이지 www.hanbit.co.kr / 이메일 ask@hanbit.co.kr Authorized translation from the English language edition, entitled EFFECTIVE DEBUGGING: 52 SPECIFIC WAYS TO DEBUG SOFTWARE AND SYSTEMS, 1st Edition by SPINELLIS, DIOMIDIS, published by Pearson Education, Inc, publishing as Addison-Wesley Professional, Copyright © 2016. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Person Education, Inc. KOREAN language edition published by Hanbit Media, Inc., Copyright © 2017. 이 책의 한국어판 저작권은 대니홍 에이전시를 통한 저작권사와의 독점 계약으로 한빛미디어(주)에 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 무단 복제를 금합니다. 지금 하지 않으면 할 수 없는 일이 있습니다. 책으로 펴내고 싶은 아이디어나 원고를 메일 ( writer@hanbit.co.kr ) 로 보내주세요. 한빛미디어(주)는 여러분의 소중한 경험과 지식을 기다리고 있습니다.
  • 6. 4 지은이 디오미디스 스피넬리스Diomidis Spinellis 아테네 경제경영 대학교의 경영과학 및 기술학과 교수다. 주요 연구 분야는 소프트웨어 공학, IT 보안, 클라우드 시스템 공학이다. 2004년과 2007년에 각각 소프트웨어 개발 생산성 어워 드를 수상한 『Code Reading』과 『Code Quality』의 저자이기도 하다. 200편 이상의 논문을 저널과 학회지에 게재했으며 2,500건 이상 인용됐다. 십 년간 「IEEE Software」의 편집 위원 으로 활동하면서 정기 칼럼인 ‘Tools of the Trade’에 글을 기고했다. macOS와 BSD 유닉스 에 그가 작성한 코드가 탑재되었으며, UMLGraph, CScout를 비롯한 다양한 오픈소스 소프 트웨어 패키지, 라이브러리, 도구를 개발했다. 임페리얼 칼리지 런던에서 소프트웨어 공학 석 사와 컴퓨터 과학 박사를 취득했다. ACM과 IEEE의 시니어 멤버다. 2015년 1월부터 현재까 지 「IEEE Software」의 편집장을 맡고 있다. 옮긴이 남기혁 kihyuk.nam@gmail.com 고려대학교 컴퓨터학과에서 학부와 석사를 마친 후 한국전자통신연구원에서 근무하다가, 현 재는 (주)프리스티에서 SDN 및 IoT 관련 기술을 개발하고 있다. 한빛미디어에서 『메이커 매 뉴얼』, 『Make: 센서』를 번역하였으며, 그 외에도 에이콘에서 『도커 컨테이너』, 『현대 네트워크 기초 이론』, 『오픈스택 네트워킹: 뉴트론』, 『실전 IoT 네트워크 프로그래밍』, 『자바 7의 새로운 기능』 등 다수의 도서를 번역했다. 지은이·옮긴이 소개
  • 7. 5 소프트웨어를 개발하거나 소프트웨어 시스템을 관리하다 보면 오류가 발생하기 마련이다. 코 드 작성 과정에서 발생하는 컴파일러 에러처럼 몇 초 만에 해결할 수 있는 것부터 대형 시스템 이 다운되는 일처럼 시간당 수백만 달러의 손실을 일으키는 문제에 이르기까지 다양한 오류가 발생한다. 실력 있는 전문가라면 어떤 오류가 발생하더라도 신속히 문제를 파악해서 근본 원인 을 해결할 줄 알아야 한다. 이러한 능력과 기술이 바로 디버깅의 핵심이며 이 책에서 다루고자 하는 주제다. 이 책은 경험이 풍부한 개발자를 대상으로 집필했다. 여기서 소개하는 내용을 제대로 이해하려 면 다양한 프로그래밍 언어로 작성된 예제 코드를 읽을 줄 알고 고급 GUI 및 명령줄 기반의 프 로그래밍 도구도 사용할 줄 알아야 한다. 그런 점에서 이 책은 입문서는 아니다. 하지만 이 책 에 나온 디버깅 기법만큼은 상세하게 설명한다. 필자의 경험에 따르면 특정한 기법의 전문성을 갖춘 숙련된 개발자도 다른 이들의 도움이 필요한 경우가 많기 때문이다. 소프트웨어에서 발생 한 문제를 수개월에 걸쳐 디버깅한 경험이 있다면 이 책에서 소개하는 몇 가지 고급 기법의 등 장 배경을 쉽게 이해할 수 있을 것이다. 이 책에서 다루는 내용 이 책에서는 최신 컴퓨팅 시스템을 개발하고 운영하는 과정에서 발생하는 문제를 해결하는 데 필요한 전략, 도구, 기법을 비롯한 디버깅에 관련된 전반적인 주제를 다룬다. 예전에는 프로그 램에서 발생한 오류를 찾아서 해결하는 것만 디버깅이라 했다. 하지만 요즘은 단독으로 실행되 는 프로그램은 거의 없으며 아무리 작은 프로그램이라도 최소한 외부 라이브러리와 동적으로 링크한다. 애플리케이션 서버에서 실행되는 웹 서비스는 관계형 데이터베이스나 NoSQL 데이 터베이스를 이용하여 디렉터리 서버로부터 데이터를 가져오고, 외부 프로그램을 실행하고, 다 른 미들웨어를 활용하고, 여러 가지 서드파티 패키지를 통합한다. 개발이 완료된 시스템이나 서비스가 제대로 작동하려면 전 세계에 퍼져 있는 호스트를 통해 실행되는 자체 개발 및 서드 파티 컴포넌트의 기능에 오류가 없어야 한다. 들어가며
  • 8. 6 현실적인 소프트웨어 개발 원칙인 데브옵스DevOps는 개발자와 다른 IT 전문가의 역할을 모두 강 조한다. 오류에 대해서도 이러한 전반적인 관점으로 다뤄야 한다. 난해한 문제들 대부분은 소 프트웨어의 어느 부분이 문제의 근본인지 곧바로 파악하기가 어렵기 때문이다. 이 책은 범용적인 기법부터 시작해서 점차 구체적인 기법을 소개하는 방식으로 설명한다. 먼저 다양한 소프트웨어 및 시스템 오류를 해결하는 데 도움되는 전략(1장), 방법론(2장), 도구와 기 법(3장)부터 설명한다. 그런 다음 디버깅 작업에 실제로 적용할 수 있는 기법들을 단계별로 소 개한다. 4장에서는 디버거를 사용하는 과정에 적용할 수 있는 기법을, 5장에서는 프로그램을 작성하는 과정에 필요한 기법을, 6장에서는 소프트웨어를 컴파일하는 과정에 필요한 기법을, 7장에서는 시스템을 실행하는 과정에 적용할 기법을, 8장에서는 멀티스레드 및 동시성 코드에 관련된 까다로운 버그들을 잡는 데 특화된 도구와 기법을 소개한다. 이 책의 활용 방법 처음부터 끝까지 한 장씩 차례대로 넘기면서 읽으면 된다. 하지만 더 좋은 방법은 필요할 때마 다 해결하려는 문제에 관련한 기법을 찾아보는 것이다. 이 책은 다음과 같이 크게 세 가지 주제 로 묶을 수 있다. ●● 전략과 기법: 장애에 대처하고 해결하기 위해 알아야 할 사항을 설명한다. 이에 대한 전반적인 사항은 ‘1장 고 차원 전략’과 ‘2장 범용적인 디버깅 기법’에서 소개한다. 그리고 ‘5장 프로그래밍 기법’에서 소개하는 여러 가지 기법도 이 범주에 속한다. 여기서 소개하는 아이템들을 잘 읽고 이해한 다음, 몸에 밸 때까지 하나씩 직접 적용 해본다. 디버깅하는 동안 자신이 사용한 기법을 체계적으로 다시 검토한다. 더 이상 해결할 수 없는 상황에 빠 지더라도 그동안 작업한 과정을 잘 파악하고 있다면 얼마든지 새로운 돌파구를 찾아낼 수 있다. ●● 기술과 도구: 디버깅을 위해 투자해야 할 대상을 설명한다. 주로 ‘3장 범용 도구를 활용한 기법’에서 설명하지 만, ‘아이템 36: 디버깅 도구 조율하기’처럼 일상에서 마주치는 문제를 해결할 때 적용할 만한 내용도 소개한 다. 시간을 충분히 투자하여 이러한 아이템에서 소개하는 기술과 도구를 익히고 서서히 실전에 적용한다. 때로 는 기존에 익숙했던 도구를 버리고 새로운 고급 도구를 적용하기 위해 공부를 해야 할 수도 있다. 처음에는 힘 들지만 길게 보면 훨씬 좋은 소프트웨어를 만들 수 있다.
  • 9. 7 ●● 디버깅 요령: 상황이 좋지 않을 때 적용할 만한 요령을 다룬다. 여기서 소개하는 요령은 흔히 사용하지 않지만 난해한 문제를 해결하는 데 걸리는 시간을 절약할 수 있다. 가령 작성한 C나 C++ 코드를 컴파일할 수 없는 이 유를 못 찾겠다면, ‘아이템 50: 생성된 코드 확인하기’를 빠르게 훑어본 뒤 작업에 적용할 만한 것들을 골라낸 다. 나중에 이러한 요령을 실제로 적용할 상황에 놓이면 다시 책에 나온 설명을 자세히 읽고 확실히 파악한다. 이 책의 적용 방법 이 책은 장애의 원인을 진단하고 시스템에 존재하는 오류를 디버깅하는 데 필요한 노하우를 중 심으로 설명하고 있지만, 그중 상당수는 버그의 발생 가능성을 최소화하고 갑작스레 나타난 버 그에 손쉽게 대처하는 데 적용할 수도 있다. 엄격한 디버깅 기법과 소프트웨어 개발 방법론은 상호 보완 관계에 있다. 이 책에서 설명한 내용은 현재 또는 미래에 소프트웨어 설계, 구현, 관 리 업무를 수행할 때도 그대로 적용할 수 있다. 소프트웨어의 설계 단계에는 다음 원칙에 따른다. ●● 역할에 맞는 고차원 메커니즘을 적용한다(아이템 47, 66). ●● 디버깅 방식을 마련한다(아이템 6, 40). ●● 시스템의 작동 과정을 모니터링하고 로그를 남기는 메커니즘을 갖춘다(아이템 27, 41, 56). ●● 유닉스 명령줄 도구를 스크립트로 작성하여 활용하는 방법도 준비한다(아이템 22). ●● 내부에서 발생하는 오류를 불안정한 상태로 놔두지 않고 명확히 드러나게 만든다(아이템 55). ●● 오류가 발생한 후의 메모리 덤프를 가져오는 방법을 확보한다(아이템 35, 60). ●● 소프트웨어가 비결정적으로 실행되는 원인과 현상을 최소화한다(아이템 63). 소프트웨어의 구현 단계에는 다음 방법을 적용한다. ●● 동료로부터 피드백을 받는다(아이템 39). ●● 루틴을 작성할 때마다 단위 테스트를 만든다(아이템 42). ●● 어서션으로 가설과 코드의 정확성을 검증한다(아이템 43). ●● 코드를 최대한 유지하기 좋은(가독성, 안정성이 좋고 분석과 수정이 용이한) 형태로 만든다(아이템 46, 48). ●● 빌드 과정에 비결정성이 발생할 근원을 차단한다(아이템 52).
  • 10. 8 마지막으로 소프트웨어 개발과 운영 작업을 관리할 때는 다음 원칙을 따른다. ●● 적절한 시스템을 이용하여 이슈를 기록하고 추적한다(아이템 1). ●● 작업할 이슈를 분류하고 우선순위를 정한다(아이템 8). ●● 제대로 구축된 버전 관리 시스템을 이용하여 소프트웨어의 변경 사항을 적절히 기록한다(아이템 26). ●● 이전 버전과 새 버전을 비교할 수 있도록 소프트웨어를 점진적으로 배치한다(아이템 5). ●● 사용하는 도구와 배치할 환경을 다양하게 구성한다(아이템 7). ●● 도구와 라이브러리를 정기적으로 업데이트한다(아이템 14). ●● 시스템에서 사용하는 서드파티 코드를 구매하거나(아이템 15), 까다로운 오류를 정확히 찾아주는 정교한 도 구를 구매한다(아이템 51, 59, 62, 64, 65). ●● 하드웨어 인터페이스와 임베디드 시스템을 디버깅하기 위한 전용 키트를 마련한다(아이템 16). ●● 소프트웨어를 원격에서 디버깅하는 환경을 꾸민다(아이템 18). ●● 시스템 성능이 높아야 처리할 수 있는 작업을 위해 CPU와 저장 공간을 넉넉히 준비한다(아이템 19). ●● 코드 리뷰와 멘토링을 통해 개발자끼리 협업을 유도한다(아이템 39). ●● 테스트 중심 개발 방법론을 도입한다(아이템 42). ●● 빌드 및 테스트 주기를 짧고 가볍게 유지하는 선에서 소프트웨어 빌드 성능 프로파일링과 정적 분석, 동적 분석 기법을 도입한다(아이템 11, 51, 53, 57, 59). 용어 관련 이 책에서 말하는 ‘오류fault ’란 ISO-24765-2010(Systems and software engineering- Vocabulary)에 나온 ‘컴퓨터 프로그램에 있는 부정확한 과정, 프로세스, 데이터 정의’를 의미 한다. 흔히 ‘결함defect ’이라고도 표현하고 일상에서는 간단히 ‘버그’라고 부른다. ‘장애failure ’란 표 현도 같은 문서에서 정의한 ‘시스템 또는 시스템 컴포넌트가 주어진 기능을 처리하는 데 일정 한 수준의 성능을 만족하지 않는 현상’이라는 의미를 따른다. 장애가 발생하면 프로그램에 충 돌이 발생하거나 멈춰서 아무런 반응이 없거나 엉뚱한 결과를 낸다. 따라서 ‘장애’는 ‘오류’가 있 을 때 발생한다. 때로는 ‘장애’를 ‘오류’와 ‘결함’으로 부르기도 하는데 ISO 표준에서도 이러한 표현을 인정한다. 기본적으로 앞에 나온 정의를 따르지만 용어의 의미가 문맥에 명확히 드러날
  • 11. 9 때는 용어를 법률 문서처럼 엄격히 구분하지 않았다. 가령 ‘문제problem ’란 표현은 오류(예: 코드 에 존재하는 문제)를 의미하기도 하고 장애(예: 재현할 수 있는 문제)를 의미하기도 한다. 이제는 유닉스 운영체제에서 사용하던 셸, 라이브러리, 도구를 다른 플랫폼에서도 사용할 수 있다. 이 책에서 말하는 유닉스란 애플의 macOS, 여러 가지 GNU/Linux(예: 아치 리눅스, 센트 OS, 데비안, 페도라, 오픈수세, 레드햇 엔터프라이즈 리눅스, 슬랙웨어, 우분투), 유닉스 의 직계 후손들(예: AIX, HP-UX, 솔라리스), 다양한 BSD 변종(예: FreeBSD, OpenBSD, NetBSD)와 윈도우의 시그윈Cygwin을 비롯한 유닉스의 원칙과 API를 따르는 모든 시스템을 가 리킨다. 마찬가지로 C++, 자바, 파이썬으로 코드를 작성할 때 최신 버전을 사용한다고 가정한다. 예제 를 작성할 때 최신 기능이나 특수한 기능을 사용해야만 돌아갈 수 있는 것들은 최대한 자제했다. 본문에서 ‘자신이 작성한 코드/소프트웨어’란 표현은 디버깅하거나 사용하고 있는 코드나 소프 트웨어를 의미한다. 이렇게 표현하면 간결할 뿐만 아니라 코드의 작성자를 명확히 드러낼 수 있다. 이는 특히 소프트웨어를 개발할 때 중요하다. 루틴routine이란 용어는 코드에서 호출할 수 있는 단위를 의미한다. 예를 들어 멤버 함수, 메서 드, 함수, 프로시저, 서브루틴 등이 있다. 비주얼 스튜디오와 윈도우란 용어는 마이크로소프트 제품을 의미한다. 버전 관리(제어) 시스템은 깃Git과 같은 소프트웨어 설정 관리에 사용하는 도구를 의미한다.
  • 12. 10 먼저 이 책을 진행하는 데 전문적인 조언과 관리를 해준 담당 편집자인 트리나 플레처 맥도날 드와 시리즈 편집자인 스콧 메이어에게 감사의 말을 전하고 싶다. 또한 기술 감수를 해준 디미 트리스 안드레아디스, 케블린 헤니, 존 페고니스, 조지 서루바투칼에게도 감사드린다. 이분들 이 제공해준 뛰어난 아이디어와 지적과 제안 덕분에 이 책의 품질을 크게 향상시킬 수 있었다. 원고를 매의 눈처럼 예리하고 검토하고 깃털처럼 가볍게 다듬어준 교열 편집자인 스테파니 길 스에게도 특별히 감사드린다. 그녀의 뛰어난 일 처리 덕분에 끔찍하게 여기던 교정 작업을 즐 겁게 마무리할 수 있었다. 이 책의 제작 과정을 굉장히 효과적으로 관리해준 멜리사 파나고스 와 총 제작 책임자인 주리 나힐, 이 책의 구성을 담당한 레이텍의 도사인 로리 휴, 편집 관련 조 언을 해준 셰리 레플린, 기술 감수단을 이끌어준 올리비아 바세지오, 멋진 표지를 만들어준 추 티 프라저티스, 마케팅을 담당한 스테파니 나킵에게도 감사드린다. 초반에 이 책의 콘셉트를 잡는 데 조언해준 알프레도 벤조, 조르지오스 고시오스, 파나지오티스 로리다스에게도 감사드 린다. 저자가 「IEEE Software」의 ‘Tools of the Trade’ 칼럼에 기고한 네 편의 글이 이 책에 다음과 같이 반영됐다. ●● 아이템 5: 정상 시스템과 비정상 시스템의 차이점 분석하기 (‘Differential Debugging’, vol. 30, no. 5, 2013, pp. 19-21) ●● 아이템 22: 유닉스 명령줄 도구로 디버깅 데이터 분석하기 (‘Working with Unix Tools’, vol. 22, no. 6, 2005, pp.9-11) ●● 아이템 58: 실행 흐름 추적하기 (‘I Spy’, vol. 24, no. 2, 2007, pp.16-17) ●● 아이템 66: 고수준 추상화를 사용하도록 코드 다시 작성하기 (‘Faking It’, vol. 28, no. 5, 2011, pp. 96, 95) 또한 다른 소스에서 영감을 얻은 아이템들도 있다. ●● ‘아이템 63 : 예측할 수 없는 부분을 분리하거나 제거하기’는 마틴 파울러의 글 ‘Eradicating Non - Determinism in Tests’(2011년 4월 14일), ‘TestDouble’(2006년 1월 17일)에 나온 아이디어를 토 대로 작성했다. 감사의 글
  • 13. 11 ●● ‘아이템 48: 의심스런 코드의 가독성과 구조 향상시키기’에서 소개한 리팩토링 기법은 대부분 마틴 파울러의 저서인 『리팩토링: 코드 품질을 개선하는 객체지향 사고법』(한빛미디어, 2012)에서 인용했다. ●● ‘아이템 60: 사후 디버깅으로 교착 상태 분석하기’는 브라이언 캔트릴과 제프 본윅이 「ACM 큐Queue」에 기 고한 ‘Real-World Concurrency’를 읽고 자극받아 집필했다. ●● ‘아이템 66: 고수준 추상화를 사용하도록 코드 다시 작성하기’에 나온 자바 코드는 타기르 발레예프가 작성한 코드를 바탕으로 만들었다. 저자의 학계 활동에 다방면으로 도와준 아테네 경제경영 대학교의 여러 동료들, 다미아노스 챠 치안토니오우, 조르지오스 도우키디스, 콘스탄틴 카치오스, 조지 기아글리스, 엠마노우일 기아 코우마키스, 디미트리스 그리차일리스, 조지 레카코스, 파나지오티스 로우리다스, 카터리나 파 라마타리, 낸시 포울로우디, 안젤리키 포울리메나코우, 조르지오스 시옴코스, 스파이로스 스파 이로우, 크리스토스 파란틸리스에게도 감사드린다. 이들의 도움으로 책을 마무리할 수 있었다. 디버깅은 실습을 통해 배우는 숙련 기술이다. 따라서 지난 40년 동안 저자로 인해 발생한 버그 를 견디고, 도움되는 이슈 리포트를 제공하고, 저자가 작성한 코드를 검토하고 테스트하고, 버 그를 피하고 추적하고 잡는 방법을 가르쳐준 동료들에게도 감사의 말을 남겨야 마땅하다. ●● 구글 애드 SRE FE 팀원들: 마크 빈, 칼 크로스, 알렉산드루-니콜라이 디미트리우, 페데 하인즈, 렉스 홀트, 토머스 헝거, 토머스 콥, 조나단 랭, 데이비드 리드비터, 앤서니 렌튼, 스벤 마나치, 리노 마스트로도메니코, 트레 버 맷슨-해밀튼, 필립 멀케이, 울프램 파이퍼, 마틴 스티언홈, 스튜어트 태일러, 스티븐 손, 스티븐 서굿, 니콜라 워싱턴 ●● CQS 동료들: 테오도로스 이브게니우, 바겔리스 카파르치아니스, 닉 나써피스 ●● 아테네 경제경영 대학교의 경영과학 및 기술학과 소속의 전현직 연구원 및 조교: 아킬레아스 아나그노스토폴 로스, 스테파노스 안드로첼리스-테오토키스, 콘스탄티노스 코리아노폴로스, 마리오스 프라콜리스, 바겔리스 기아니카스, 조르지오스 고시오스, 스타브로스 그리고라카키스, 바실리오스 카라코이다스, 마리아 케차기아, 크 리스토스 라자리스, 디미트리스 미트로폴로스, 크리스토스 오이코노모우, 투샤 샤르마, 소포클리스 스토우라이 티스, 콘스탄티노스 스트로길로스, 바소 탄갈라키, 스타브로스 트리히아스, 바실레이오스 플라코스, 기오르고스 조가넬리스 ●● 그리스 재무부의 정보 시스템 담당 사무국 직원들: 코스타스 발라토스, 레오니다스 보기아티스, 파라스케비 챠 지미타코우, 크리스토스 코보로조스, 야니스 디마스, 디미트리스 디미트리아디스, 아레티 드라카키, 니콜라우스
  • 14. 12 드로소스, 크리스탈리아 드라이스텔라, 마리아 엘레프테리아도우, 스타마티스 에조발리스, 카테리나 프란체스 카키, 불라 하밀루, 안나 혼드로다키, 야니스 이오아니디스, 크리스토스 K. K. 로버도스, 이피게네이아 칼람포키 도우, 니코스 칼라티스, 라자로스 카플라노그로우, 아겔로스 카보니스, 소피아 카트리, 크리스토스 카시스, 디오 니시스 케팔리노스, 아이작 코키니디스, 조르지오 코사키스, 기오르고스 콘도라키스, 파나기오티스 크라니 디오 티스, 야니스 키리아코폴로스, 오디세아스 키리아코포이로스, 조르기오스, 라스카리디스, 파나기오티스 라자리 디스, 나나 레이소우, 이오안나 리바디오티, 아겔리키 라이코디, 아시미나 만타, 마리아 마라벨라키, 차라 마브 리도우, 소피아 마브로폴로우, 미카일 미칼로폴로스, 판텔리스 나시카스, 토도로스 파그치스, 안젤리키 파나이 오타키, 크리스토스 파파돌리스, 바실리스 파파포티노스, 이오아니스 페라키스, 칸토 페트리, 안드레아스 피피 스, 니코스 프사라키스, 마리안티 프소마, 오디세아스 파이로보라키스, 타소스 사그리스, 아포스톨로스 스키자 스, 소피 세페리데스, 마리노스 시갈라스, 조지 스타몰리스, 안토니스 스트리키스, 안드레아스 스볼로스, 카리스 테오카리스, 아드리아노스 트리가스, 디미트리스 차키리스, 니키 초마, 마리아 차팔리아, 바실리키 초블라, 디미 트리스 바피아디스, 아킬레아스 베모스, 이오아니스 블라코스, 지아니스 저르바스, 타나시스 제르보폴로스 ●● FreeBSD 프로젝트 팀원들: 존 볼드윈, 일코 벌트, 마틴 크라코서, 파월 야쿱 다이덱, 세리 데이비스, 브룩스 데이비스, 러슬란 에밀로프, 브루스 에반스, 브라이언 펀다코스키 펠만, 페드로 지푸니, 존-마크 거니, 칼 요한 구스타프슨, 콘래드 잰코스키, 폴헤닝 캄프, 크리스 케나웨이, 기오르고스 케라미다스, 보리스 코바렌코, 맥스 라이어, 네이트 로손, 샘 레플러, 알렉산더 레이딩어, 씬 라이, 스콧 롱, M. 워너 로시, 브루스 A. 마, 데이비드 말 론, 마크 머리, 사이먼 L. 닐슨, 데이비드 오브라이언, 요한 ‘미르크라버크’ 오스카슨, 콜린 퍼시발, 알프레드 펄 스타인, 제스 피터스, 톰 로드, 루이지 리조, 래리 로젠만, 젠스 슈웨이카드, 켄 스미스, 댁얼링 스모르그라프, 머 리 스토켈리, 마리우스 스트로블, 이반 보라스, 로버트 왓슨, 피터 웸, 가렛 울맨 ●● LH 소프트웨어 및 SENA 팀원들: 카터리나 아라반티노우, 미칼리스 벨리바나키스, 폴리나 비라키, 디미트리 스 카라미도폴로스, 릴리 카라미도폴로우, 안젤로스 카리시스, 기오르고스 카치미칼리스, 니코스 크리스토폴로 스, 크리스티나 다라, 데잔 디미트리제빅, 파니아 도르코피키, 니코스 도카스, 레프테리스 지오갈라스, 소티리스 지로디아노스, 바실리스 지아나코스, 크리스토스 콜로지아니스, 안티 칼리비오티, 에르시 카라나소우, 안토니스 코노모스, 이시도로스 코우벨라스, 조지 키리아지스, 마리나 리아파티, 스피로스 리비에라토스, 소피아 리비에 라토우, 파나지오티스, 로리다스, 나탈리아 밀리오우, 스피로스 몰페타스, 카터리나 모토지아니, 디미트리스 넬 라스, 지오니스 엔톤토스, 크리스토스 오이코노모우, 니코스 파노시스, 바실리스 파파리조스, 타소스 파파스, 알 렉산드로스 파파스, 칸티아 프린테지, 마리오스 살테리스, 아르지로 스타마티, 타키스 테오파노폴로스, 디미트 리스 톨리스, 프로소 토팔리, 타키스 트라가키스, 사바스 트리안타필로, 페리클리스 차하게아스, 니코스 차그카 리스, 아포스톨리스 치그크로스, 기오르고스 차말리스, 기아니스 블라코기아니스 ●● ECRC(European Computer Industry Research Center) 소속 동료들: 미레이 듀카스, 안나- 마리아 엠드, 알렉산더 헤롤드, 폴 마틴, 데이브 모튼
  • 15. 13 ●● 임페리얼 칼리지 런던의 컴퓨터과학과 동료들: 바실리스 카포일리아스, 마크 도슨, 소피아 드로소폴로우, 코스 티스 드라일라키스, 데이브 에드몬슨, 수잔 아이젠바흐, 필리포스 프란굴리스, 아나스타시오스 하지코콜리스, 폴 켈리, 스티븐 J. 레이시, 필 매일, 리 M. J. 맥클로클린, 스튜어트 매크로버트, 믹셀리스 멜라크리니디스, 잔- 시몬 팬드리, 마크 테일러, 페리클리스 차하기아스, 던컨 화이트 ●● UC 버클리의 CSRG(Computer Science Research Group): 케이스 보스틱 ●● 폴리아디스 어소시에이츠 동료들: 알렉시스 아나스타시오우, 콘스탄틴 도콜라스, 노엘 코틀리스, 디미트리오 스 크라소폴로스, 조지 카이리아지스, 기아니스 마라키스, 아타나시오스 폴리아디스 ●● 여러 모임을 통해 만난 이들: 이오르고스 아다모폴로스, 디미트리스 안드레아디스, 야니스 코로베시스, 알렉산 더 콜롬비스, 존 이오아니디스, 디미트리오스 칼로게라스, 파나기오티스 카나보스, 테오도로스 카로노스, 파이 돈 리암포티스, 엘리아스 파파바실로폴로스, 바실리스 프레벨라키스, 스텔리오스 카르체타키스, 아킬레스 볼리 오티스, 알렉시오스 자브라스 마지막으로 집에서도 디버깅하고 주말과 휴가 중에도 글을 쓰는 저자를 오랫동안 참고 견디며 지 원을 아끼지 않은 나의 가족에게 감사의 말을 전하고 싶다. 특히 [그림 5.2]를 만든 디오니시스와 이 책의 표지를 선정하는 데 도와준 일라이자와 일레아나에게 특별히 고맙다는 말을 남긴다.
  • 16. 14 CONTENTS 지은이·옮긴이 소개................................................................................................................ 4 들어가며............................................................................................................................... 5 감사의 글........................................................................................................................... 10 CHAPTER 1 고차원 전략 아이템 1: 모든 문제를 이슈 추적 시스템으로 관리하기 .............................................................. 19 아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기 ....................................... 22 아이템 3: 선행 조건과 후행 조건 만족 여부 확인하기 ................................................................ 24 아이템 4: 문제 발생 지점부터 버그를 추적하거나, 프로그램 시작 지점부터 버그를 찾아나가기 .......... 26 아이템 5: 정상 시스템과 비정상 시스템의 차이점 분석하기 ........................................................ 28 아이템 6: 소프트웨어에서 제공하는 디버깅 기능 활용하기 ......................................................... 32 아이템 7: 빌드 및 실행 환경을 다양하게 구성하기 .................................................................... 36 아이템 8: 가장 중요한 문제에 집중하기 .................................................................................. 40 CHAPTER 2 범용적인 디버깅 기법 아이템 9: 성공적인 디버깅을 위한 마음가짐 ............................................................................ 43 아이템 10: 효율적으로 문제 상황 재현하기 ............................................................................. 46 아이템 11: 코드 수정 후 결과 확인까지의 시간 최소화하기 ........................................................ 49 아이템 12: 복잡한 테스트 시나리오 자동화하기 ....................................................................... 51 아이템 13: 디버깅 관련 데이터를 한눈에 볼 수 있는 환경 구축하기 ....................................... 53 아이템 14: 소프트웨어 업데이트 고려하기 .............................................................................. 56 아이템 15: 서드파티 소스 코드 분석을 통해 문제 해결하기 ........................................................ 57 아이템 16: 전문 모니터링 및 테스팅 장비 활용하기 .................................................................. 59 아이템 17: 오류의 효과 극대화하기 ....................................................................................... 62 아이템 18: 원격 디버깅 환경 구축하기 ................................................................................... 65
  • 17. 15 아이템 19: 디버깅 작업 자동화하기 ....................................................................................... 67 아이템 20: 디버깅 전과 후에 정리하기 ................................................................................... 69 아이템 21: 비슷한 문제 모두 고치기 ...................................................................................... 70 CHAPTER 3 범용 도구를 활용한 기법 아이템 22: 유닉스 명령줄 도구로 디버깅 데이터 분석하기 ......................................................... 73 아이템 23: 명령줄 도구 옵션과 관용 표현 활용하기 .................................................................. 80 아이템 24: 코드 편집기로 디버깅 데이터 탐색하기 ................................................................... 83 아이템 25: 작업 환경 최적화하기 .......................................................................................... 85 아이템 26: 버전 관리 시스템으로 버그 원인과 히스토리 추적하기 ............................................... 90 아이템 27: 독립적인 프로세스로 구성된 시스템에서 모니터링 도구 사용하기 ................................ 94 CHAPTER 4 디버거 활용법 아이템 28: 디버깅 버전으로 컴파일하기 ............................................................................... 100 아이템 29: 한 단계씩 코드 실행하기 .................................................................................... 104 아이템 30: 코드와 데이터 중단점 활용하기 ........................................................................... 106 아이템 31: 리버스 디버깅 .................................................................................................. 109 아이템 32: 루틴 사이의 호출 흐름 추적하기 .......................................................................... 112 아이템 33: 변수와 표현식의 값을 분석하여 에러 찾기 ............................................................. 114 아이템 34: 실행 중인 프로세스에 디버거 연동하기 ................................................................. 117 아이템 35: 코어 덤프 다루기 .............................................................................................. 120 아이템 36: 디버깅 도구 조율하기 ........................................................................................ 122 아이템 37: 어셈블리 코드와 메모리 값 확인하기 .................................................................... 126
  • 18. 16 CONTENTS CHAPTER 5 프로그래밍 기법 아이템 38: 의심스런 코드를 검토하고 손으로 실행해보기 ........................................................ 131 아이템 39: 동료 검토하기 .................................................................................................. 133 아이템 40: 디버깅 기능 추가하기 ........................................................................................ 135 아이템 41: 로그 남기기 ..................................................................................................... 138 아이템 42: 단위 테스트 사용하기 ........................................................................................ 144 아이템 43: 어서션 사용하기 ............................................................................................... 147 아이템 44: 코드를 바꿔보면서 검증하기 ............................................................................... 151 아이템 45: 정상적인 코드와 문제가 발생한 코드의 차이점 줄이기 ............................................. 152 아이템 46: 의심스런 코드 간소화하기 .................................................................................. 153 아이템 47: 의심스런 코드를 다른 언어로 작성해보기 .............................................................. 157 아이템 48: 의심스런 코드의 가독성과 구조 향상시키기 ........................................................... 158 아이템 49: 버그의 증상이 아닌 원인 고치기 .......................................................................... 162 CHAPTER 6 컴파일 시간 기법 아이템 50: 생성된 코드 확인하기 ........................................................................................ 165 아이템 51: 정적 분석 도구 활용하기 .................................................................................... 169 아이템 52: 빌드 결과와 실행 동작이 항상 일정하도록 설정하기 ................................................ 174 아이템 53: 라이브러리에서 제공하는 디버깅 및 검사 기능 설정하기 .......................................... 177
  • 19. 17 CHAPTER 7 실행 시간 기법 아이템 54: 테스트 케이스를 작성하여 오류 찾기 .................................................................... 185 아이템 55: 오류가 발생한 즉시 프로그램 중단하기 ................................................................. 190 아이템 56: 애플리케이션 로그 파일 분석하기 ........................................................................ 191 아이템 57: 시스템 및 프로세스 연산에 대한 프로파일 분석하기 ................................................ 196 아이템 58: 실행 흐름 추적하기 ........................................................................................... 200 아이템 59: 동적 프로그램 분석 도구 활용하기 ....................................................................... 206 CHAPTER 8 멀티스레드 코드 디버깅하기 아이템 60: 사후 디버깅으로 교착 상태 분석하기 .................................................................... 212 아이템 61: 프로그램의 실행 흐름을 기록한 뒤 재생하기 .......................................................... 219 아이템 62: 전문 도구로 교착 상태와 경쟁 상태 찾기 ............................................................... 224 아이템 63: 예측할 수 없는 부분을 분리하거나 제거하기 .......................................................... 231 아이템 64: 자원 경쟁으로 인해 발생한 성능 확장성 문제 분석하기 ............................................ 233 아이템 65: 성능 카운터를 이용하여 거짓 공유 발생 지점 찾기 .................................................. 237 아이템 66: 고수준 추상화를 사용하도록 코드 다시 작성하기 .................................................... 241 찾아보기 ........................................................................................................................ 251
  • 21. 191장 고차원 전략 고차원 전략 본격적으로 문제 해결에 들어가기 전에 전략을 잘 세워야 한다. 그래야 최소한의 노력으로 문 제를 성공적으로 해결할 수 있다. 선택한 전략이 작업에 적합하지 않다면 즉시 차선책으로 전 환해야 한다. 아이템 1: 모든 문제를 이슈 추적 시스템으로 관리하기 어느 날 동료가 전화를 걸어 여러분이 작성한 애플리케이션이 제대로 작동하지 않는다고 불평 을 늘어놓을 수 있다. 그 사실을 포스트잇에 간단히 기록하고 모니터를 화려하게 장식한 다른 노트 틈에 추가하고선, 나중에 그 동료에게 애플리케이션의 새 버전에 필요한 최신 라이브러리 를 보냈는지 확인할 것을 머릿속으로만 기억해둘 것이다. 하지만 이런 식으로 일을 처리하면 안 된다. 제대로 하려면 다음에서 소개하는 방법에 따라 처리한다. 먼저 이슈 추적 시스템issue-tracking system부터 구축한다. 깃허브GitHub나 깃랩GitLab을 비롯한 여러 소스 코드 저장소는 코드의 버전 관리를 위한 기본적인 기능뿐 아니라 이슈 추적 기능도 함께 제공 하고 있다. 또한 상당수의 조직에서 상용 이슈 추적 시스템인 지라JIRA를 사용하고 있다. 이 시 스템은 라이선스를 구입하여 조직 내부에 직접 구축하거나 서비스 형태로 사용할 수 있다. 지 라 대신 버그질라Bugzilla, 론치패드Launchpad, OTRS, 레드마인Redmine, 트랙Trac 같은 오픈소스 시스 템도 많이 사용한다. 물론 어느 시스템을 사용하는가보다는, 이러한 시스템을 잘 활용하여 개 CHAPTER 1
  • 22. 20 이펙티브 디버깅 발 과정에서 발생한 모든 이슈를 시스템에 잘 기록해두는 것이 무엇보다 중요하다. 이슈 추적 시스템에 기록되지 않은 문제는 처리되지 않는다. 하지만 시스템을 꾸준히 활용하면 다음 효과를 얻을 수 있다. ●● 디버깅 작업 과정을 명확하게 파악할 수 있다. ●● 릴리스 일정을 수립하고 추적할 수 있다. ●● 작업의 우선순위를 정할 수 있다. ●● 자주 발생하는 이슈나 해결책을 문서로 정리할 수 있다. ●● 해결해야 할 문제를 실수로 빼먹지 않을 수 있다. ●● 릴리스 노트를 자동으로 생성할 수 있다. ●● 결함을 측정하고, 이를 되돌아보며 교훈을 얻을 수 있는 저장소로 활용할 수 있다. 직급이 높아서 이슈를 직접 기록할 의무가 없는 이들도 이슈 추적 시스템을 사용하는 것이 좋 다. 자신이 직접 발견한 이슈도 마찬가지다. 조직에 따라 이슈에 직접 관련된 이들만 소스 코드 를 수정하는 권한을 주기도 한다. 이슈를 작성할 때 문제를 재현하는 방법도 정확히 기록한다. 이때 간결하면서도 필요한 내용이 모 두 담긴 예제도 함께 적어주면 좋다. 이러한 예제는 애플리케이션 코드에 그대로 복사한 후 컴 파일하고 실행해서 문제를 직접 확인해볼 수 있을 정도로 정확하게 작성한다(아이템 10: 효율적으 로 문제 상황 재현하기 참조). 잘 작성된 버그 리포트만 이슈 추적 시스템에 등록되게 하려면, 리포 트를 제대로 작성하는 방법을 제시하고 사용자에게 이를 철저히 숙지시킨다(그 방법을 화장실 문에 붙여둔 회사도 있었다). 또한 버그 리포트의 제목은 정확하게 붙이고, 버그의 우선순위와 심각한 정도, 그리고 해당 버그에 관련 된 이들과 버그가 발생한 환경에 대한 상세한 정보도 기록한다. 이러한 항목은 다음 사항을 염두 에 두고 작성해야 한다. ●● 제목을 정확하고 간결하게 작성하면 요약 리포트만 봐도 어떤 버그인지 쉽게 알아볼 수 있다. 가장 나쁜 예는 ‘프로그램이 갑자기 뻗었음’과 같이 적는 것이다. ‘저장하는 동안 리프레시 버튼을 누르면 프로그램이 뻗음’과 같이 구체적으로 적는 것이 바람직하다. ●● 심각한 정도를 명시하면 버그의 우선순위를 정하는 데 도움이 된다. 데이터가 손실되는 버그는 당연히 심각한 문제로 봐야 하지만, 미美적인 문제나 대안이 알려진 문제는 이보다 심각한 정도가 낮다. 이처럼 버그의 심각한 정도를 명시하면 이슈 목록을 작성할 때 당장 해결해야 하는 이슈, 나중에 처리해도 되는 이슈, 무시해도 되는 이슈 등과 같이 우선순위에 따라 분류할 수 있다.
  • 23. 211장 고차원 전략 ●● 이렇게 정해진 우선순위는 이슈의 우선순위 항목에 기록한다. 그리고 그 순서에 따라 작업의 순서를 정한다(아 이템 8: 가장 중요한 문제에 집중하기 참조). 일반적으로 버그의 우선순위는 개발자나 프로젝트 리더가 정한 다. 최종 사용자들은 한결같이 자신이 제출한 버그가 가장 중요하다고 주장하는 경향이 있기 때문이다. 우선순 위를 제대로 정해서 기록해두면, 모든 문제가 다 중요하거나 자신이 제기한 이슈가 최우선이라고 여기는 관리 자, 고객, 다른 팀 개발자들의 아우성에 대처하는 데 도움된다. ●● 이슈마다 이해 당사자를 명시하면 개발 팀원이 문제를 해결하는 데 좀 더 노력하게 만들 수 있고, 제품 담당자 가 해당 이슈의 우선순위를 결정하는 데 도움된다. 어떤 조직은 이슈에 관련된 이해 당사자 옆에 그 사람이 기 여한 연간 수익을 태깅하기도 한다(예: ‘25만 달러짜리 고객인 애크미가 제출한 이슈임’). ●● 이슈가 발생한 환경에 대한 정보도 기록해두면 쉽게 드러나지 않는 버그를 재현하는 데 도움된다. 환경을 기록 할 때 PC의 일련번호나 BIOS 날짜, 시스템에 설치된 모든 라이브러리의 버전 등과 같이 불필요한 정보까지 너무 자세하게 작성하도록 요구하면 사용자들은 너무 부담스러워서 이러한 항목들을 그냥 건너뛴다. 따라서 가 장 핵심적인 부분만 상세하게 물어본다. 가령 웹 기반 앱이라면 브라우저에 대한 정보가 중요하다. 모바일 앱이 라면 디바이스 제조사와 모델 정보가 필요하다. 기왕이면 이러한 세부 사항을 제출하는 절차를 소프트웨어를 통해 자동화하면 좋다. 이슈 추적 시스템을 사용할 때 현재 진행 상황을 문서로 남긴다는 기분으로 활용하는 것이 좋다. 대 부분의 이슈 추적 시스템에서는 이슈를 기록하는 항목의 끝부분에 자유롭게 댓글을 남길 수 있 는 기능을 제공한다. 이러한 항목을 통해 버그를 분석하고 해결한 방법이나, 해결하다가 막힌 사실 등을 문서화한다. 그러면 조직의 작업 현황을 명확히 들여다볼 수 있다. 프로그램의 동작 을 추적하거나 로그를 남길 때 사용한 명령어도 정확히 기록해둔다. 그러면 다음 날 반복하거 나 일 년 후 자신 또는 동료가 비슷한 버그를 해결할 때 큰 도움이 된다. 또한 나중에 팀원이나 상사에게 그동안 작업한 내용을 설명해야 할 때 일주일 내내 버그를 잡느라 눈이 침침해지고 정신이 몽롱해진 상태에서도 쉽게 기억을 떠올릴 수 있다. 기억할 사항 ●● 모든 문제를 이슈 추적 시스템으로 처리한다. ●● 이슈를 작성할 때 문제를 재현하는 방법을 정확하고, 간결하고, 관련된 사항을 모두 담아서, 구체적인 예제 와 함께 기록한다. ●● 이슈의 심각한 정도와 우선순위를 정해서 이에 맞게 작업 일정을 짠다. ●● 이슈 추적 시스템을 통해 작업 현황을 문서화한다. 
  • 24. 22 이펙티브 디버깅 아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기 요즘은 인터넷이 항상 연결된 환경에서 작업하는 경우가 대부분이다. 그러다 갑자기 인터넷을 사용할 수 없게 되면 개발자의 생산성이 크게 떨어진다. 작성하던 코드가 제대로 작동하지 않 으면 대부분 인터넷을 검색하거나 동료 개발자에게 물어보는 방식으로 해결하기 때문이다. 이렇게 해결책을 찾기 위해 웹을 검색할 때 에러 메시지 부분을 큰따옴표로 묶어서 검색 창에 입력하면 훨씬 효과적이다. 검색 엔진은 큰따옴표로 묶인 문구가 그대로 담긴 페이지를 검색 하기 때문에 결과의 정확도가 훨씬 높아진다. 또한 문제와 관련된 라이브러리나 미들웨어 이 름, 클래스나 메서드 이름, 에러 코드 등도 함께 검색어로 입력하면 좋다. 검색하려는 함수의 이름이 특이할수록 찾기 쉽다. 가령 BitBlt로 검색할 때보다 PlgBlt로 검색할 때 더 좋은 결과 를 얻을 수 있다. 또한 동의어도 함께 넣으면 좋다. 가령 아무런 반응 없이 멈춘 현상freeze을 표 현할 때 ‘먹통hang’과 같은 단어도 같이 적어주고, 비활성화disabled된 상태를 표현할 때 ‘회색으로 반전greyed’과 같은 표현도 함께 적어주면 좋다. API를 호출하는 과정에서 발생하는 문제들은 대부분 다른 사람들이 사용하는 방법을 참고하 는 것만으로도 쉽게 해결할 수 있다. 해당 함수를 사용하는 오픈소스 소프트웨어를 찾아서 전 달된 매개변수가 어떻게 초기화되고, 여기서 반환한 값을 어떻게 해석하는지 살펴본다. 이처 럼 코드를 검색할 때는 구글과 같은 범용 검색 엔진보다는 블랙 덕 오픈 허브 코드 서치Black Duck Open Hub Code Search처럼 코드 검색에 특화된 검색 엔진을 사용하면 더 좋은 결과를 얻을 수 있다. 예를 들어 블랙 덕에서 mktime을 검색하고, 모든 라이브러리 선언과 정의들을 일일이 살펴보 지 않도록 특정한 프로젝트에 대한 결과만 나오게 필터링하면 다음과 같은 코드를 찾을 수 있 다.* nowtime = mktime(time-tm_year+1900, time-tm_mon+1, time-tm_mday, time-tm_hour, time-tm_min, time-tm_sec); 결과를 보면 localtime과 달리 mktime 함수는 연도를 인수로 받는데, 1900년도부터 지난 연 도의 수가 아니라 연도를 표현하는 네 자리 숫자를 지정해야 하며, 월도 1부터 시작하는 형태 * 옮긴이_ 현재 코드 검색 서비스가 중단됐다. 인터넷에서 ‘코드 검색 엔진’으로 검색해보면 다양한 서비스를 찾을 수 있으며, https:// searchcode.com도 추천한다.
  • 25. 231장 고차원 전략 로 표현한다는 것을 알 수 있다. 이러한 사항은 해당 함수에 대한 문서를 제대로 읽어보지 않으 면 놓치기 쉽다. 검색 결과를 살펴볼 때 해당 페이지를 어느 사이트에서 호스팅하는지도 주의 깊게 본다. 스택 오버플로StackOverflow와 같은 스택익스체인지StackExchange 계열의 커뮤니티에는 개발자들이 관심을 가질만한 노하우가 상당히 많이 축적되어 있기 때문에 원하는 답을 찾을 확률도 높다. 스택오 버플로에서 검색할 때 채택된 답변뿐만 아니라 답변을 계속 찾고 있는 항목도 함께 참고한다. 또한 답변에 달린 댓글도 꼭 읽어본다. 에러를 피하기 위한 다른 기법과 같은 새로운 정보가 이 항목을 통해 업데이트되기 때문이다. 아무리 열심히 검색해봐도 만족할만한 답을 찾지 못했다면 애초에 방향을 잘못 잡았을 수도 있 다. 유명한 라이브러리나 소프트웨어에 관련된 문제라면 누군가 최소한 한 명은 같은 문제를 겪었을 것이다. 따라서 문제에 관련된 내용을 웹에서 찾지 못했다면, 애초에 문제의 원인을 잘 못 진단했을 수도 있다. 가령 버그가 있어서 충돌이 발생했다고 생각했던 API 함수가 사실은 그 함수에 전달한 데이터에 문제가 있었기 때문일 수도 있다. 검색으로 해결책을 찾지 못했다면 스택오버플로에 직접 질문을 올린다. 단, 포럼에 질문을 올 릴 때 다른 이들이 그대로 복사해서 컴파일하기만 해도 문제를 직접 확인할 수 있는 수준의 짤 막한 코드를 제공해야 하는데(SSCCEshort, self-contained, correct (compilable and runnable) example), 이렇게 하 려면 상당한 노력을 기울여야 한다(아이템 10: 효율적으로 문제 상황 재현하기 참조). 사용하는 언 어에 따라 SourceLair나 JSFiddle과 같은 온라인 IDE를 통해 문제를 재현하는 예제를 라이 브 형태로 제공할 수도 있다. 특정한 언어나 기술에 대한 예제를 잘 작성하기 위한 구체적인 방법은 sscce.org를 참고한다. 또한 에릭 레이몬드Eric Raymond가 작성한 「똑똑하게 질문하기」 (How To Ask Questions The Smart Way, http://www.catb.org/~esr/faqs/smart- questions.html)도 함께 읽어보면 좋다. 경험상 질문을 잘 작성해서 예제와 함께 올리는 것만으로도 많은 문제를 쉽게 해결했다. 물론 이처럼 정성 들여 질문한다고 해서 당장 해답을 구할 수 있는 것은 아니지만, 예제를 잘 작성하 면 실력 있는 개발자의 눈에 잘 띄기 때문에 이들이 직접 실행해보고 해결책을 제시해줄 가능 성이 높아진다. 발생한 문제가 오픈소스 라이브러리나 프로그램 코드에 존재하는 버그 때문이라면, 해당 오픈 소스 프로그램의 개발자에게 직접 연락하거나, 그 프로젝트의 버그 추적 시스템에 이슈를 올린
  • 26. 24 이펙티브 디버깅 다. 이슈를 올리기 전에 비슷한 버그 리포트가 이미 등록되어 있는지부터 확인하고, 없다면 해 당 문제를 재현할 수 있도록 정보를 상세하게 기록하는 것이 좋다. 해당 프로그램에 대한 버그 추적 시스템이 없다면 개발자에게 직접 이메일을 보낸다. 이메일로 문의할 때는 버그 추적 시 스템에 리포팅할 때보다 공손하게 메일을 작성하는 것이 좋다. 대부분의 오픈소스 개발자는 이 러한 지원 활동을 돈을 받고 하는 것이 아니기 때문이다. 기억할 사항 ●● 발생한 에러에 대한 해결책을 웹에서 검색할 때 에러 메시지를 큰따옴표로 묶어서 입력한다. ●● 스택익스체인지 사이트에 올라온 답변을 참고한다. ●● 위 두 방법으로 해결하지 못했다면 직접 질문을 올리거나 버그 리포팅 시스템에 이슈를 등록한다. 아이템 3: 선행 조건과 후행 조건 만족 여부 확인하기 전자 제품을 수리할 때는 가장 먼저 전원의 연결 상태부터 확인한다. 전원 공급 장치로부터 전 기가 나오는지, 그래서 회로에 잘 전달되는지부터 점검한다. 의외로 이 부분이 문제의 원인인 경우가 많다. 소프트웨어도 마찬가지로 특정 루틴에 진입하기 전의 값(선행 조건precondition, 프 로그램의 상태와 입력값)과 루틴을 실행하고 나온 시점의 값(후행 조건postcondition, 프로그램 상 태와 반환값)을 확인하는 것만으로도 문제가 발생한 부분을 찾아내는 경우가 많다. 선행 조건 이 잘못됐다면 이 값을 설정한 곳에 문제가 있는 것이고, 후행 조건이 잘못됐다면 루틴 내부에 문제가 있는 것이다. 두 값 모두 이상이 없다면 다른 지점에서 원인을 찾아야 한다. 루틴이 시작하는 부분이나 루틴을 호출하는 지점, 또는 핵심 알고리즘이 실행되는 지점에 중단 점breakpoint을 설정한다(아이템 30: 코드와 데이터 중단점 활용하기 참조). 선행 조건을 만족하는지 확 인할 때는 의심스런 코드에서 사용하는 전역변숫값과 매개변숫값, 호출한 메서드가 속한 객체 등과 같이 알고리즘에 영향을 주는 요소들을 주의 깊게 살펴본다. 그중에서도 특히 다음 사항 에 주의한다.
  • 27. 251장 고차원 전략 ●● 값이 null이 아니어야 하는 부분이 null인지 살펴본다. ●● 수학 함수에 전달한 값이 그 함수에서 다루는 범위 안에 있는지 확인한다. 가령 log 함수에 전달한 값이 0보다 큰지 확인한다. ●● 루틴에 전달한 객체나 구조체, 배열의 내부를 살펴보고, 그 안에 필요한 내용이 담겨 있는지 확인한다. 이렇게 확인하면 포인터 값이 잘못된 부분도 찾을 수 있다. ●● 변수의 값이 정상 범위 안인지 확인한다. 간혹 변수를 초기화하지 않으면 6.89851e-308이나 61007410 과 같은 이상한 값이 들어 있는 경우가 있다. ●● 무작위 추출 검사spot-check를 통해 루틴에 전달한 데이터 구조의 무결성을 검사한다. 예를 들어 맵map에 키와 값이 제대로 들어가 있는지, 이중 링크드 리스트를 정확하게 탐색할 수 있는지 등을 확인한다. 루틴의 끝이나 루틴을 호출한 지점 뒤, 또는 핵심 알고리즘의 실행이 끝나는 지점에도 중단점 을 설정한다. 그러고 나서 루틴의 실행 결과를 확인한다. ●● 계산된 결과가 적합한지, 예상 범위 안에 있는지 확인한다. ●● 값이 정상 범위에 있다면 실제로 결과가 정확한지 확인한다. 직접 손으로 계산해보거나(아이템 38: 의심스 런 코드를 검토하고 손으로 실행해보기 참조), 이미 알려진 정상 값과 비교해보거나, 다른 도구로 계산해보면 된다. ●● 루틴의 부작용side effect이 예상했던 것인지, 의심스런 코드로 인해 데이터가 손상되거나 엉뚱한 값으로 설정되 어 있지 않은지 확인한다. 이러한 확인 작업은 데이터 구조를 탐색하는 동작에 관련된 상태 정보를 해당 알고 리즘의 내부에서 직접 관리할 때 특히 중요하다. ●● 알고리즘에서 사용하는 (파일 핸들이나 락lock과 같은) 자원을 정상적으로 반환했는지 확인한다. 고차원 연산이나 설정 작업을 수행하는 부분도 이와 같은 기법으로 확인할 수 있다. SQL문이 테이블을 제대로 구성하는지 확인할 때도, 연산의 실행 전후에 테이블의 구조와 값을 비교해보 면 된다. 파일을 다루는 코드를 확인할 때는 입력 파일과 출력 파일을 검사한다. 웹 서비스에서 수행하는 연산을 디버깅할 때는 웹 서비스마다 다루는 입력과 출력값을 살펴본다. 데이터센터 전체에 발생한 문제를 해결할 때는 관련된 장비와 네트워킹, DNS, 공유 스토리지, 데이터베이 스, 미들웨어 등을 비롯한 그 장비와 관련된 요소들을 검사한다. 항상 추측하지 말고 직접 확인 한다. 기억할 사항 ●● 루틴의 선행 조건과 후행 조건을 주의 깊게 검사한다. 
  • 28. 26 이펙티브 디버깅 아이템 4: 문제 발생 지점부터 버그를 추적하거나, 프로그램 시작 지점부터 버그를 찾아나가기 문제의 원인을 찾는 방법은 크게 두 가지다. 하나는 문제가 발생한 지점에서 출발해서 근본 원 인을 추적하는 것이고, 다른 하나는 애플리케이션이나 시스템이 시작하는 시점부터 문제의 원 인이 나타날 때까지 찾는 것이다. 둘 중 어느 방법이 효과적인지는 발생한 문제의 성격에 따라 다르다. 때로는 한 가지 방식으로 추적하다가 막힐 때 다른 방식으로 시도하면 도움된다. 문제가 명확히 드러났다면 그 문제가 발생한 지점에서 출발하는 것이 좋다. 이 방식은 다음과 같이 세 가지 시나리오로 진행할 수 있다. 첫째, 프로그램이 갑자기 죽는 문제를 해결할 때는 디버거로 그 프로그램을 실행하거나, 프로그램 이 충돌하는 시점에 디버거를 붙이거나, 문제가 발생한 후 출력된 메모리 덤프 값을 읽어보는 방법(아이템 35: 코어 덤프 다루기 참조)으로 원인을 쉽게 찾을 수 있다. 이때 프로그램이 충돌하는 시점의 변숫값을 확인해보고, 문제가 발생하는 이유가 변수의 값이 null이기 때문인지, 잘못 된 값이 들어가서인지, 아니면 변수를 제대로 초기화하지 않았기 때문인지 등을 파악한다. 어 떤 시스템에서는 변수의 기본값으로 (bad food를 의미하는) 0xBAADF00D와 같이 눈에 쉽 게 띄는 값을 사용하기 때문에 변수의 초기화 여부를 쉽게 발견할 수 있다. 이러한 용도로 사용 하는 값에 대한 자세한 내용은 위키백과의 Magic Number 항목*을 참고한다. 변수에 값이 잘 못 들어간 지점을 발견했다면 더 세부적으로 파고들어 그렇게 된 근본 원인을 찾는다. 충돌을 발생시키는 루틴 내부를 들여다볼 수도 있고, 루틴을 호출하는 흐름상에서 잘못된 인수를 전달 하는 지점을 찾는 방식으로 접근할 수도 있다(아이템 3: 선행 조건과 후행 조건 만족 여부 확인하기, 아 이템 32: 루틴 사이의 호출 흐름 추적하기 참조). 둘째, 프로그램이 갑자기 죽지 않고 그냥 멈춘 뒤 아무런 반응이 없다면 문제가 발생한 지점에서 원 인을 추적하는 상향식 방식을 앞에서 소개한 방법과는 조금 다르게 진행한다. 먼저 프로그램을 디버거에서 실행하다가 문제가 발생하는 지점에 멈추거나(아이템 30: 코드와 데이터 중단점 활용하기 참조), 메모리 덤프를 출력하도록 설정한다(아이템 35: 코어 덤프 다루기 참조). 때에 따라 직접 작 성한 코드가 아닌 외부 라이브러리에서 실행하는 코드에서 문제가 발생할 수 있다. 이럴 때는 루틴을 호출하는 흐름을 따라가보면서 프로그램을 멈추게 만드는 루프가 있는지 검사한다. 이 * https://goo.gl/nnnyns
  • 29. 432장 범용적인 디버깅 기법 범용적인 디버깅 기법 디버깅 방법은 시스템의 기반 기술과 개발 플랫폼에 따라 다르다. 그러나 몇 가지 기법은 다양 한 환경에서 공통적으로 사용할 수 있다. 아이템 9: 성공적인 디버깅을 위한 마음가짐 소프트웨어는 굉장히 복잡하게 구성되는 경우가 많다. 기계식 시계의 부품은 100개가 넘고 일 반 가정집의 전기 배선은 이보다 몇 배 많은 부품으로 구성된다. 이에 반해 일상에서 우리가 흔 히 볼 수 있는 소프트웨어 시스템은 수만 개의 명령문이 복잡하게 얽혀 있다. 극단적인 예로 A380 항공기를 구성하는 부품이 약 400만 개인 데 반해, 리눅스 커널 코드는 900만 줄이 넘 는다. 이렇게 복잡한 코드를 다루기 위해서는 마음을 단단히 먹어야 한다. 우선 소프트웨어에서 발생한 문제는 항상 찾아서 고칠 수 있다고 믿는다. 마음가짐에 따라 디버깅 작업의 생산성이 달라진다. 전문가들은 이를 ‘인지된 도전과 인지된 기술 사이의 경기’라고 부른다. 문 제를 해결할 수 있다는 믿음이 없으면 마음이 흔들리거나 포기하기 쉽다. 그래서 문제의 원인 을 해결하기보다는 겉으로 드러난 증상만 임시방편으로 해결하여 상황을 더 악화시킬 수도 있 다. 마음가짐을 바르게 하려면 다음 사항을 명심한다. 발생한 문제를 재현할 수 있다면 (이 책에서 소개하는 기법을 통해) 반드시 해결할 수 있다. 재 현하기 힘든 문제도 해결할 방법은 얼마든지 찾을 수 있다. 디버깅 작업은 주로 다음과 같은 두 CHAPTER 2
  • 30. 44 이펙티브 디버깅 가지 수단을 토대로 진행한다. 하나는 문제 해결에 필요한 데이터를 최대한 확보하는 것이고, 다른 하나는 이러한 데이터를 처리할 수 있는 고성능 컴퓨터를 사용하는 것이다. 데이터를 확 보할 때는 문제의 현상, 로그, 소스 코드를 살펴보고 필요하다면 기계어 명령어까지 조사한다. 또한 소프트웨어 스택의 원하는 지점에 로그를 좀 더 자세히 출력하는 문장(또는 모니터링을 위한 탐침)을 추가한 뒤, 다른 도구나 간단한 스크립트를 통해 대량으로 수집한 데이터를 분석 하여 문제의 원인을 찾는다. 마치 원하는 물고기를 잡기 위해 그물을 던질 때 그동안 축적된 경 험을 반영하여 그물의 폭과 깊이를 적절히 조절하는 것과 같다. 디버깅 작업을 효과적으로 수행하려면 시간도 충분히 확보해야 한다. 디버깅은 프로그래밍보다 훨씬 많은 노력을 기울여야 하는 작업이다. 프로그램의 로직과 이로 인해 발생하는 (저수준low level의) 현상을 항상 머릿속에 그리고 있어야 하기 때문이다. 또한 문제를 효과적으로 재현하기 위해 필요한 환경과 중단점, 로깅, 창/화면, 테스트 케이스 등을 정확히 설정해야 한다. 그리고 목표로 삼은 버그를 완전히 잡거나 최소한 이를 위해 해야 할 일을 정확히 파악하기도 전에 디 버깅을 중단하여 그동안 투자한 시간을 그냥 날리지 않도록 주의한다. 디버깅 작업은 단순하지 않기 때문에 고도로 집중해야 한다. 두뇌가 어떤 활동에 완전히 몰두하여 빠져든 몰입flow 상태에 들어가려면 어느 정도의 시간이 걸린다. 몰입이라는 개념을 소개한 미 할리 칙센트미하이Mihály Csíkszentmihályi에 따르면 몰입 상태에 들어가면 모든 감정이 현재 수행하 는 작업에 맞춰진다고 한다. 몰입 상태에서 느낀 성취감을 통해 작업에 대한 지속성과 효율성 을 더욱 높일 수 있다. 성취감과 지속성, 효율성은 복잡한 시스템을 디버깅하는 과정에서 겪는 엄청난 어려움을 극복하는 데 핵심적인 요소다. 팝업 메시지가 뜨거나, 전화벨이 울리거나, 메 신저로 대화하거나, SNS에 올라온 새 소식을 읽거나, 회사 동료가 말을 걸면 집중력이 흐트러 져서 몰입의 효과를 누릴 수 없게 된다. 이러한 방해 요인은 모두 제거한다. 필요 없는 애플리 케이션은 모두 종료하고, 전화기는 진동 모드로 설정하고, 모니터나 책상 앞(직책이 높다면 문 앞)에 ‘방해 금지’라는 팻말을 걸어 놓은 뒤에 작업한다. 또 다른 효과적인 기법으로 어려운 문제를 만나면 잠을 자는 방법이 있다. 연구 결과에 따르면 수면 상태에 있는 동안에는 언뜻 보기에는 관련이 없어 보이는 경로를 따라 뉴런이 연결된다고 한 다. 이러한 뉴런의 활동은 디버깅 작업에 큰 도움을 줄 수 있다. 더 이상 해결의 실마리가 보이 지 않는 막다른 상황에서 완전히 새로운 관점으로 디버깅 전략을 수정함으로써 돌파구를 찾을 수 있다. 잠은 이렇게 새로운 관점을 떠올리기 위해 반드시 필요하다. 단, 효과를 제대로 보려 면 적절한 방법에 따라 수면을 취해야 한다. 잠자리에 들기 직전까지 열심히 작업하다가 잠들
  • 31. 452장 범용적인 디버깅 기법 면 문제에 대한 새로운 해결책을 떠올리는 데 필요한 모든 데이터가 의식 속에 그대로 전달된 다. 하지만 작업을 중간에 포기하고 맥주 한 잔 마신 뒤에 잠들면 아무런 효과를 볼 수 없다. 또 한 잠을 충분히 자야 수면 상태에서 두뇌의 잠재의식 영역이 제시한 여러 가지 아이디어를 다 음 날 아침에 일어난 후에 활동할 두뇌의 의식 영역이 제대로 받아들일 수 있다. 디버깅은 결코 쉽지 않다. 디버깅 작업을 효과적으로 수행하기 위해서는 끈기가 필요하다. 컴 퓨터는 근본적으로 모든 동작을 예측할 수 있는(결정적인deterministic) 방식으로 작동한다. 따라서 오류를 찾아낼 때까지 문제를 얼마든지 깊이 파고 들어가 볼 수 있다. 그런데 프로그래밍의 표 현력과 효율성을 높이기 위해 고수준의 관점에서 보면 무작위로 작동하는 것처럼 보이게 하는 비결정성nondeterminism을 도입했다. 이렇게 동작을 예측하기 힘든 비결정적인 에러를 디버깅할 때는 컴퓨터의 빠른 속도와 프로그래밍 능력을 최대한 활용하여 에러를 발견할 때까지 수많은 경우의 수를 하나씩 검사하면 된다. 결국 디버깅 실패의 원인은 대부분 끈기 부족에 있다. 테스 트 케이스를 충분히 만들지 않았거나, 로그 파일을 제대로 보지 않았거나, 다른 접근 방식을 찾 지 않았기 때문이다. 마지막으로 실전에서 효과적으로 디버깅할 수 있도록 디버깅 환경과 도구, 관련 기법을 익히는 데 끊임없이 노력해야 한다. 소프트웨어에 들어가는 기술의 복잡도가 지속적으로 증가하는 상 황에서 경쟁력을 확보하려면 이 방법 외에 다른 방법은 없다. 돌이켜보면, 필자가 디버깅 과정 에서 저지른 실수의 원인 중 대다수는 디버깅 환경을 제대로 갖추는 데 충분한 투자를 하지 않 았기 때문이다. 그래서 다음과 같은 기법을 적용하지 못했다. ●● 효과적인 테스트 케이스를 충분히 확보한다(아이템 10: 효율적으로 문제 상황 재현하기 참조). ●● 버그를 재현하는 과정을 자동화한다. ●● 로그 파일을 분석하기 위한 스크립트를 작성한다. ●● API나 언어의 기능이 실제로 작동하는 과정을 확실히 파악한다. 저자의 경험에 따르면 디버깅 작업에 필요한 에너지를 적재적소에 투입하는 것만으로도 디버 깅 생산성을 크게 향상시킬 수 있었다. 일단 이렇게 자세와 환경을 제대로 갖추고 나면 버그를 굉장히 빠르게 해결할 수 있다.
  • 32. 46 이펙티브 디버깅 기억할 사항 ●● 모든 문제는 찾아서 고칠 수 있다고 믿는다. ●● 디버깅 작업에 필요한 시간을 충분히 확보한다. ●● 작업에 최대한 집중할 수 있도록 환경을 마련한다. ●● 힘든 문제를 해결할 때는 효과적인 수면을 취한다. ●● 포기하지 않는다. ●● 디버깅 환경, 도구, 기법을 익히는 데 꾸준히 노력한다.  아이템 10: 효율적으로 문제 상황 재현하기 디버깅을 효과적으로 수행하려면 문제가 발생하는 상황을 쉽고 안정적으로 재현할 수 있어야 한다. 이유는 다음과 같다. 첫째, 클릭 한 번으로 항상 문제를 재현할 수 있다면 오류가 나타나 게 만들기 위해 허둥대느라 시간을 낭비할 필요 없이 곧바로 원인을 해결하는 데 집중할 수 있 다. 둘째, 문제 상황을 쉽게 재현할 수 있는 수단을 마련하면 다른 이에게 도움을 청할 때 문제 상황을 설명하기도 쉽다(아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기 참조). 마지 막 세 번째 이유는 오류를 수정한 뒤에 문제가 발생하는 상황을 여러 번 반복하여 더 이상 문제 가 발생하지 않는다는 것을 확인함으로써 문제를 완벽하게 해결했다는 것을 증명하기도 쉽다. 문제를 재현하는 예제(테스트 케이스)를 간결하게 만들면 작업의 효율성을 크게 높일 수 있다. 기본 적으로 예제는 간단해야 한다. 문제를 재현하는 범위에서 최대한 간결하게 만든다. 더 좋은 방 법은 SSCCE라 부르는 기준(아이템 1: 모든 문제를 이슈 추적 시스템으로 관리하기 참조)에 따라 간결 할 뿐만 아니라, 독립적으로 실행할 수 있고, 컴파일해서 실행할 수 있도록 정확하게 예제를 작 성하는 것이다. 이렇게 예제를 최대한 간결하게 만들면 코드에서 불필요한 부분을 탐색하느 라 낭비하는 시간을 절약할 수 있다. 간결한 예제는 장황하게 작성된 예제보다 실행 속도도 훨 씬 빠르다. 특히 성능 오버헤드가 엄청난 디버깅 모드로 실행할 때 그 차이가 극명하게 드러 난다. 예제를 최대한 간결하게 만들기 위해서는 하향식top-down으로 접근할 수도 있고 상향식bottom-up 으로 접근할 수도 있다(아이템 4: 문제 발생 지점부터 버그를 추적하거나, 프로그램 시작 지점부터 버그를 찾아나가기 참조). 상황에 가장 적합한 방식으로 진행한다. 코드에 의존 관계가 많다면 상향식으
  • 33. 472장 범용적인 디버깅 기법 로 접근하여 처음부터 새로 시작하는 것이 바람직하다. 반면 문제의 원인을 파악하기 힘들 때 는 테스트 케이스를 작성해서 경우의 수를 줄여나가는 하향식 접근 방식이 좋다. 상향식으로 접근할 때는 문제의 원인에 대한 가설을 세운 뒤(예를 들어, 특정한 API를 호출하 는 부분에 문제가 있다고 가정한 뒤) 이를 확인하는 테스트 케이스를 만든다. 구체적인 예를 살 펴보기 위해 저자가 예전에 파일 입력을 처리하는 프로그램을 디버깅할 때 경험한 사례를 소개 한다. 이 프로그램은 27,000줄 이상의 긴 코드를 입력하면 속도가 굉장히 느려지는 문제가 있 었다. 소스 코드에서 시스템 콜을 호출하는 부분을 면밀히 살펴보니, 입력 파일을 읽는 과정에 파일 스트림의 오프셋을 반환하는 함수인 tellg를 호출하는 부분에 뭔가 문제가 있다는 의심이 들었다. 이를 확인하기 위해 테스트 코드를 간략히 작성해서 실행한 결과 이 부분이 원인이라 는 확신이 들었고(아이템 58: 실행 흐름 추적하기 참조), 이때 작성한 코드는 tellg에서 발생하는 오 류를 우회하는 코드(래퍼 클래스)를 테스트할 때도 유용하게 써먹었다. ifstream in(fname.c_str(), ios::binary); do { (void)in.tellg(); } while ((val = in.get()) != EOF);  하향식으로 접근할 때는 문제를 재현하는 시나리오에 따라 진행하면서 더 이상 문제가 발생하 지 않을 때까지 의심스런 부분을 하나씩 제거해나간다. 이때 이진 탐색을 적용하면 상당히 효 과적이다. 예를 들어 특정한 HTML 파일을 읽을 때 브라우저가 이상하게 작동하는 문제가 발 생하는 경우를 생각해보자. 우선 HTML의 헤드(head) 영역을 제거하고 실행한다. 그래도 문제가 사라지지 않으면 이번에는 보디(body) 부분도 삭제한다. 이렇게 할 때 문제가 사라 진다면 다시 보디 부분을 원래대로 집어넣고 보디 영역의 절반만 삭제해서 실행한다. 원인을 확실히 찾을 때까지 이 과정을 반복하면서 문제가 발생하는 영역을 좁혀나간다. 삭제한 부분이 문제와 관련이 없어서 이전 상태로 되돌아갈 때는 코드 편집기의 실행 취소/되돌리기undo 기능 을 활용하면 작업을 효율적으로 수행할 수 있다. 예제를 간결하게 만들면 이를 독립적인 형태로 만들기도 쉬워진다. 독립적인 형태란 다른 라이브 러리나 헤더 파일, CSS 파일, 웹 서비스와 같은 외부 요소에 의존하지 않고 이 예제만으로 다 른 곳에서 문제를 똑같이 재현할 수 있다는 뜻이다. 작성한 테스트 케이스를 실행하기 위해 외 부 요소가 필요하다면 예제와 함께 번들 형태로 묶어둔다. 이때 파일의 위치를 절대 경로로
  • 34. 48 이펙티브 디버깅 표현하거나 IP 주소를 코드에 직접 박아두지 말고 코드의 위치에 최대한 독립적인 방식으로 표현한다. 예를 들어, /home/susan/resources/file.css 대신 ../resources/file.css로 표기 하고, http://193.92.66.100:8081/myService 대신 http://localhost:8081/myService로 표현한다. 이렇게 예제를 독립적인 형태로 작성하면 고객의 환경이나 다른 플랫폼(가령 리눅 스 환경에서 테스트한 것을 윈도우)에서 테스트하기도 쉽고, QA 게시판에 올리기도 편하고 (아이템 2: 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기 참조), 벤더에게 도움을 요청하기 도 좋다. 또한 반복적으로 실행할 수 있는 환경도 마련해야 한다. 현재 작성 중인 코드와 실행 환경에서 원인 을 도저히 찾을 수 없다면 엉뚱한 곳에서 버그를 찾고 있기 때문일 수도 있다. 예를 들어 소프 트웨어 인스톨러를 디버깅하는 경우를 생각해보자. 디버깅 과정에서 인스톨러를 실행할 때마 다 현재 사용하는 운영체제의 설정이 지저분해진다. 특히 디버깅 대상이 인스톨러일 때는 이러 한 상황을 반드시 피해야 한다. 이럴 때는 시스템을 언제든지 완전 초기 상태로 되돌릴 수 있도 록 설치 환경을 가상 머신 이미지로 만들면 좋다. 설치에 실패할 때마다 언제든지 다시 처음 상 태로 돌아가면 된다. 이때 완전한 형태의 가상 머신 대신 도커Docker와 같은 컨테이너 기술 또는 운영체제 수준에서 제공하는 가상화 기법을 활용해도 된다. 여기에 앤서블Ansible, CFEngine, 셰프Chef, 퍼핏Puppet, 솔트Salt와 같은 시스템 설정 관리 도구를 도입하면 더 좋다. 이러한 도구를 활용하면 고수준의 명령으로 원하는 시스템 설정을 안정적으로 생성할 수 있고 배포 환경과 테 스팅 환경, 개발 환경의 호환성을 유지하기도 편하다. 또한 소프트웨어의 진화 과정을 관리할 때와 똑같은 방식으로 이러한 환경의 진화 과정을 관리할 수 있다. 오류가 발생하는 버전에 대한 복사본을 언제든지 만들 수 있는 기능도 필요하다. 이를 위해 가장 먼저 해 야 할 일은 소프트웨어를 깃Git과 같은 버전 관리 도구에 넣는 것이다. 그러고 나서 소프트웨어 를 빌드할 때 소스 코드에 대한 식별자를 포함하도록 설정한다. 예를 들어 다음과 같이 셸 명령 을 실행하면 가장 최근에 발생한 커밋에 대한 짧은 깃 해시 값으로 변수를 초기화하는 문장을 출력한다. 이 문장을 소스 코드에 집어넣으면 된다. git log -n 1 --format='const string version = %h;'   앞의 명령을 실행하면 다음과 같이 출력된다. const string version = 035cd45;  
  • 35. 733장 범용 도구를 활용한 기법 범용 도구를 활용한 기법 특정한 영역에 특화된 디버깅 도구를 사용하는 것이 훨씬 편리하고 효율적이지만 범용 도구를 활용하는 것도 나름대로 장점이 있다. 여러 언어와 플랫폼으로 구성된 시스템에서 문제가 발 생했을 때 당장 활용할 수 있기 때문이다. 이 장에서 소개하는 범용 도구들은 오래 전 유닉스를 주로 사용하던 시절에 나온 것이지만 지금까지도 GNU/리눅스와 윈도우, macOS 등과 같은 다양한 시스템에서 사용할 수 있다. 이러한 도구들이 제공하는 유연성, 효율성, 범용성만 감안 하더라도 사용법을 익히는 데 시간을 투자할 만한 가치가 있다. 이러한 범용 도구의 사용법에 대해 조슈아 레비가 간결하게 정리한 「The Art of Command Line」(https://github.com/ jlevy/the-art-of-command-line)을 추천한다. 이 장에서는 독자들이 유닉스 명령줄과 정 규표현식에 대한 기초는 갖추고 있다고 가정하고, 곧바로 여러 가지 도구를 디버깅 작업에 활 용하는 기법을 소개한다. 아이템 22: 유닉스 명령줄 도구로 디버깅 데이터 분석하기 디버깅을 하다 보면 지금까지 아무도 겪어보지 못한 문제에 맞닥뜨릴 수 있다. 소프트웨어를 작 성할 때 사용하던 IDE에 이러한 문제를 분석하는데 딱 맞는 기능이 없을 수 있다. 이럴 때는 유 닉스 명령줄 도구로 해결하면 된다. 유닉스 명령줄 도구는 범용적인 용도로 제작된 것이기 때문 에 파이프라인을 잘 활용하면 텍스트 형태의 데이터를 손쉽게 분석할 수 있다. CHAPTER 3
  • 36. 74 이펙티브 디버깅 줄 단위의 텍스트 포맷으로 작성된 데이터 스트림은 다양한 종류의 데이터를 다루는 데 필요 한 최소한의 포맷으로만 구성되어 있다. 따라서 디버깅 과정에서 다루는 프로그램의 소스 코드 나 로그, 버전 관리 히스토리, 파일 목록, 심벌 테이블, 아카이브 내용, 에러 메시지, 테스트 결 과, 프로파일 결과 등을 비롯한 다양한 형태의 데이터를 표현할 수 있다. 디버깅 데이터를 다루 는 작업을 날마다 반복적으로 수행하다 보면 펄, 파이썬, 루비, 혹은 윈도우의 파워셸PowerShell과 같은 스위스 다용도칼처럼 강력한 스크립팅 언어를 사용하고 싶을 수도 있다. 이러한 스크립팅 언어에서 디버깅 데이터를 쉽게 가져올 수 있는 인터페이스를 제공하고, 인터랙티브 방식으로 명령을 작성하는 데 익숙하다면 이러한 스크립팅 언어를 활용하는 것도 좋은 방법이다. 그렇지 않으면 데이터 처리에 필요한 기능을 모두 갖춘 프로그램을 직접 작성해야 한다. 이렇게 작업 하다 보면 굉장히 번거로울 뿐만 아니라 모든 작업을 일일이 처리하느라 디버깅에 관련된 중요 한 통찰을 얻을 기회를 잃어버릴 수도 있다. 이보다 바람직한 방법은 여러 가지 유닉스 도구들을 파이프라인을 이용하여 간결하고 효율적 으로 잘 엮어서 셸 프롬프트에서 실행할 수 있는 형태로 만드는 것이다. 셸 명령줄에서 제공 하는 최신 편집 기능을 활용하면 명령을 하나씩 조합하는 방식으로 원하는 작업을 구성할 수 있다. 이번 아이템에서는 유닉스 명령을 활용하여 디버깅 데이터를 처리하는 방법에 대해 소개한다. 명령줄과 정규표현식의 사용법에 대해 익숙하지 않은 독자는 관련 주제에 대한 온라인 튜토리 얼을 참고하기 바란다. 또한 각각의 명령에서 제공하는 여러 가지 옵션에 대해 자세히 알고 싶 다면 man 명령에 궁금한 옵션의 이름을 인수로 지정하여 실행하면 볼 수 있다. 현재 사용하는 운영체제의 종류에 따라 유닉스 명령줄을 곧바로 사용할 수 있거나, 어렵지 않게 필요한 환경을 구축할 수도 있다. 유닉스나 macOS 시스템이라면 곧바로 터미널 창을 띄우기 만 하면 된다. 윈도우 환경이라면 시그윈Cygwin을 설치하는 것이 가장 무난하다. 시그윈은 윈도 우 환경에서 여러 가지 유닉스 도구와 강력한 패키지 관리 시스템을 쉽게 사용할 수 있도록 윈 도우에 맞게 포팅하여 제공한다. 또한 이 절에서 소개하는 도구 중에서 macOS 환경에서 기본 적으로 제공하지 않는 것들은 홈브루Homebrew 패키지 관리자를 이용하면 손쉽게 설치할 수 있다. 디버깅에 필요한 작업을 여러 가지 유닉스 도구를 조합하여 한 줄짜리 명령으로 만들어 처리하 는 경우가 많은데, 이러한 명령은 대체로 가져오기fetching, 선택하기selecting, 처리하기processing, 정 리하기summarizing와 같은 패턴에 따라 작성한다. 이때 원하는 작업의 성격에 따라 파이프라인 연
  • 37. 753장 범용 도구를 활용한 기법 산(|)을 활용하여 한쪽 작업에서 출력된 결과를 다른 작업의 입력으로 전달하는 방식으로 여러 가지 작업을 하나로 연결한다. 디버깅 과정에서 다루는 데이터는 대부분 텍스트 포맷으로 되어 있기 때문에 사용하는 도구의 표준 입력으로 데이터를 그대로 전달할 수 있다. 이렇게 처리하기 힘든 포맷으로 구성되어 있 다면 그 포맷을 지원하는 도구를 사용해야 한다. 가령 오브젝트 파일을 분석하고 처리할 때는 nm(유닉스), dumpbin(윈도우), javap(자바) 등과 같은 명령을 활용하면 된다. 예를 들어 작 성하던 C 또는 C++ 프로그램이 갑자기 종료했다면 해당 오브젝트 파일을 nm 명령으로 실행 하여 exit 함수가 호출된 지점을 찾을 수 있다. # 지정한 파일 이름으로 시작하는 모든 오브젝트 파일에 담긴 심벌을 출력한다. nm -A *.o | # U exit로 끝나는 라인을 출력한다. grep 'U exit$'  그러면 다음과 같은 결과를 얻을 수 있는데, 소스 코드를 일일이 뒤지는 것보다 훨씬 정확하게 찾아낼 수 있다. cscout.o: U exit error.o: U exit idquery.o: U exit md5.o: U exit pdtoken.o: U exit  아카이브로 묶인 파일에 담긴 내용을 보려면 tar, jar, ar와 같은 명령을 사용하면 된다. 검색해 야 할 파일이 너무 많다면 find 명령으로 디버깅 작업에 직접적으로 관련된 파일만 골라낸다. 데이터를 웹에서 가져와야 한다면 curl이나 wget 명령을 활용한다. 또한 dd 명령(과 특수한 형태의 파일인 /dev/zero)을 사용하거나, yes나 jot을 이용하여 데이터를 인위적으로 생성하 여 간단히 벤치마크를 실행할 수도 있다. 마지막으로 컴파일러에서 발생한 여러 가지 에러 메 시지를 처리할 때는 21이나 2파일이름과 같은 구문을 사용하여 컴파일러에서 생성한 표준 에러를 표준 출력이나 다른 파일로 리다이렉션할 수 있다. 예를 들어, 어떤 함수의 인터페이스 를 변경했을 때 그 함수에 관련된 모든 부분에 변경 사항을 반영하기 위해 관련된 파일을 모두 찾으려면, 다음과 같이 여러 가지 명령을 파이프라인으로 조합하는 방법으로 해결할 수 있다.
  • 38. 76 이펙티브 디버깅 # 수정된 함수에 관련된 모든 파일을 빌드한다. # 이때 빌드 과정에서 발생하는 표준 에러를 표준 출력으로 리다이렉션한다. make -k 21 | # 에러가 발생한 파일의 이름을 출력한다. awk -F: '/no matching function for call to Myclass::myFunc/ { print $1}' | # 각 파일이 한 번씩만 출력되도록 정렬한다. sort -u  로그 파일을 비롯한 디버깅 과정에서 분석해야 할 파일들은 대체로 불필요한 데이터가 많이 담 겨 있다. 실제로 필요한 부분은 전체 라인 중에서 일부분이거나, 각 라인의 특정한 영역일 수도 있다. 이때 각 항목이 고정된 폭으로 구성되어 있거나, 각 항목이 공백과 같은 구분자로 나눠져 있다면 cut 명령을 활용하여 각 라인에서 원하는 항목만 골라낼 수 있다. 그렇지 않고 데이터 가 필드 단위로 깔끔하게 구분되어 있지 않다면 원하는 부분을 정규표현식으로 표현해서 sed 명령의 인수로 전달하여 실행하는 방식으로 골라내면 된다. 파일에서 원하는 라인만 골라낼 때 가장 흔히 사용하는 명령으로 grep이 있다. 원하는 항목을 담은 행만 정확히 골라내도록 정규표현식을 작성해서 인수로 지정하고, -v 옵션을 설정하여 필요 없는 행을 걸러내는 방식으로 처리하면 된다. 2장의 아이템 21: 비슷한 문제 모두 고치기에서 모든 나눗셈 연산 중에서 분모가 sizeof가 아닌 것만 찾아낼 때 이 방식으로 처리한 바 있다. grep -r '/' . | grep -v '/ sizeof'  찾으려는 항목이 정규표현식이 아닌 일반 문자열이거나, 데이터가 이전 단계의 처리 과정을 통 해 파일에 저장되어 있다면, 고정된 길이의 문자열에 특화된 grep인 fgrep에 -f 옵션을 지정 하여 실행한다. 선택 기준이 조금 복잡하다면 awk 패턴 표현식으로 표현한다. 작업을 하다 보 면 원하는 결과를 얻기 위해 방금 소개한 여러 가지 기법들을 조합해야 하는 경우가 많다. 이를 테면, grep으로 원하는 라인을 찾아서, grep -v로 필요 없는 부분을 제거한 뒤, awk로 각 행 에서 원하는 필드만 골라낸다. 좀 더 구체적인 예를 들면, 시스템 트레이스 출력 결과에서 제대 로 열린 파일의 이름만 화면에 표시하려면 다음과 같은 명령을 구성한다.
  • 39. 773장 범용 도구를 활용한 기법 # open을 호출한 라인을 출력한다. grep '^open(' trace.out | # open 함수를 호출한 부분 중에서 실패한(-1을 반환한) 부분은 제거한다. grep -v '= -1' | # 인용 부호로 구분된 항목 중에서 두 번째 필드만 출력한다. awk -F '{print $2}'  (이 문장을 하나의 awk 명령으로 작성할 수도 있지만, 여기에 나온 것처럼 단계별로 하나씩 조 합해나가는 방식으로 작성하는 것이 훨씬 쉽다.) 데이터를 처리할 때 라인을 특정한 필드를 기준으로 정렬해야 하는 경우가 많다. sort 명령은 이러한 작업을 처리할 수 있도록 다양한 정렬 기준과 그 값의 타입, 출력 순서 등을 지정하는 옵션을 제공한다. 결과를 원하는 형태로 정렬했다면 각 항목의 개수를 효율적으로 알아내야 한 다. 이 작업은 uniq 명령에 -c 옵션을 지정하여 실행하면 된다. 또한 마지막에 sort 명령을 더 추가하여 앞에서 정렬한 결과를 다른 방식으로 정렬해야 하는 경우도 많다. 예를 들어, 뽑아낸 결과 중에서 가장 많이 등장한 항목을 알아내려면 -n 옵션을 지정한 sort 명령을 마지막에 추 가하면 된다. 때로는 동일한 프로그램을 다양한 환경에서 실행한 결과를 서로 비교해야 하는 경우도 있다. 가령 원래는 동작이 서로 같아야 하는 실행 결과들에 차이점이 있는지 확인할 때 는 diff로 명령을 사용하면 된다. 또한 두 개의 정렬된 리스트를 비교할 때는 comm 명령을 활 용할 수 있다. 이때도 awk를 사용하면 좀 더 복잡한 작업을 처리할 수 있다. 예를 들어, 자원 누수 현상을 분석하기 위해 obtainResource을 호출하고 나서 releaseResource를 호출하지 않는 파일을 모두 찾아내는 작업을 다음과 같이 처리할 수 있다. # 첫 번째 집합에만 나온 레코드 나열하기 comm -23 ( # obtainResource가 포함된 파일 이름 나열하기 grep -rl obtainResource . | sort) ( # releaseResource가 포함된 파일 이름 나열하기 grep -rl releaseResource . | sort)  (여기서 (...)구문은 배시bash 셸의 확장 기능으로, 명령에서 파일 형태의 인수를 받는 자리에서 파일명 대신에 프로세스를 인수로 지정할 수 있다. 이렇게 괄호 안에 지정한 프로세스는 백그 라운드로 실행된 후에 결과가 파이프로 연결되어 명령의 인수로 전달된다.)
  • 40. 78 이펙티브 디버깅 일반적으로 이렇게 처리한 결과로 나온 데이터는 당장 사용하기에는 너무 방대한 경우가 많다. 가령 로그 파일에서 에러가 발생했다는 것을 표시하는 라인을 일일이 살펴볼 필요 없이 이러한 라인의 전체 개수만 알면 되는 경우도 있다. 놀랍게도 wc 명령에 -l 옵션을 지정하여 결과의 개수만 세는 것만으로도 문제를 해결하는 경우가 많다. 결과 목록에서 상위 또는 하위 10개 항 목만 알아내고 싶다면 head나 tail 명령을 사용하면 된다. 또한 사람들이 가장 많이 건드리는 파일을 알아내고 싶다면 다음과 같이 명령을 실행한다. # 각 라인에 대한 최종 수정 사항을 나열한다. git blame --line-porcelain Foo.java | # 작성자를 알아낸다. grep '^author' | # 같은 이름을 한데 묶도록 정렬한다. sort | # 각각의 이름이 나타난 횟수를 센다. uniq -c | # 나타난 횟수 기준으로 정렬한다. sort -rn | # 최상위 결과만 나열한다. head  로그 파일을 살펴볼 때는 tail 명령을 활용하면 편하다(아이템 23: 명령줄 도구 옵션과 관용 표현 활용 하기, 아이템 56: 애플리케이션 로그 파일 분석하기 참조). 그리고 방대한 결과를 자세히 들여다볼 때 는 more나 less 명령에 파이프로 연결해서 보면 좀 더 편하다. 두 명령 모드 스크롤 업/다운 기능과 문자열을 검색하는 기능을 제공한다. 이것만으로 부족할 때는 awk 명령을 활용한다. 주로 sum += $3처럼 특정한 필드 값을 모두 더하는 작업을 하나의 명령으로 처리할 때 유용 하다. 좀 더 구체적으로 예를 들면, 웹 서버 로그를 분석하여 요청된 횟수와 각 요청에 대해 전 송된 바이트 수의 평균값을 구하는 작업을 다음과 같이 명령을 작성하여 처리할 수 있다. awk ' # HTTP 결과 코드가 200(success)이라면 # 10번 필드(전송된 바이트 수)를 합산한다. $9 == 200 {sum += $10; count++} # 입력이 끝나면 요청 횟수와 전송된 바이트의 평균값을 출력한다. END {print count, sum / count}' /var/log/access.log  유닉스를 구성하는 요소들이 아무리 훌륭하더라도 이들을 하나로 엮어주는 기능이 없다면 아
  • 41. 793장 범용 도구를 활용한 기법 무런 쓸모가 없다. 본Bourne 셸에서 제공하는 기능을 활용하면 여러 요소를 엮을 수 있다. 경우 에 따라 동일한 명령을 인수만 바꿔서 여러 차례 실행해야 할 수도 있다. 이럴 때는 그 명령에 전달할 여러 개의 인수를 xargs의 입력으로 전달한다. 일반적으로 find로 파일의 목록을 구한 다음, 각각에 대해 명령을 실행하는 작업은 xargs로 처리하는 패턴으로 구성한다. 굉장히 흔히 사용되는 패턴이기 때문에 (윈도우의 경우 ‘Program Files’ 폴더처럼) 이름에 공백이 담긴 파 일을 처리할 수 있도록 두 명령 모두 데이터가 공백 대신 널null 문자로 끝나도록 지정하는 옵션 (-print0과 -0)을 제공한다. 구체적인 예를 살펴보기 위해, foo.cpp 파일을 수정한 뒤에 생 성된 로그 파일 중에서 ‘access failure’라는 문자열이 가장 많이 등장하는 파일을 찾는 경우를 살펴보자. 이 작업은 다음과 같이 파이프라인으로 여러 명령을 연결하여 처리할 수 있다. # /var/log/acme 폴더에 있는 파일 중에서 # foo.cpp를 변경한 후에 수정된 파일을 모두 찾는다. find /var/log/acme -type f -cnewer ~/src/acme/foo.cpp -print0 | # fgrep으로 'access failure'가 나타난 횟수를 센다. xargs -0 fgrep -c 'access failure' | # 앞에서 처리한 결과를 :를 기준으로 필드를 구분하고 # 두 번째 필드를 기준으로 내림차순으로 정렬한다. sort -t: -rn -k2 | # 최상위 결과를 출력한다. head -1  데이터를 처리하는 과정이 이보다 복잡하다면 파이프로 인수를 while read 루프에 연결한다 (놀랍게도 본 셸에서는 모든 제어 구조에 파이프를 연결해서 데이터를 전달하거나 가져올 수 있다). 예를 들어, 발생한 문제의 원인이 시스템의 동적 링크 라이브러리(DLL)을 업데이트한 것과 관련이 있다고 의심되어 windows/system32 디렉터리에 있는 모든 DLL 파일의 버전 정보를 나열하고 싶다면 다음과 같이 처리할 수 있다. # 모든 DLL 파일을 찾는다. find /cygdrive/c/Windows/system32 -type f -name *.dll | # 각각의 파일에 대해 while read f ; do # 윈도우상의 경로를 구하고 # 그중 를 이스케이프(특수 문자로 처리하는) 포맷으로 변환한다. wname=$(cygpath -w $f | sed 's///g') # WMIC 질의를 통해 이름과 버전을 구한다. wmic datafile where Name=$wname get name, version
  • 42. 80 이펙티브 디버깅 done | # 헤더와 빈 줄을 제거한다. grep windows  명령이 제대로 실행되지 않는다면 데이터를 다루는 데 필요한 작업들을 한 단계씩 차근차근 작 성한다. 기억할 사항 ●● 텍스트 형태의 레코드를 가져오고, 선택하고, 처리하고, 정리하는 유닉스 명령을 이용하여 디버깅 데이터를 분석한다. ●● 여러 가지 유닉스 명령을 파이프라인으로 조합하는 방식으로 복잡한 분석 작업을 간단히 처리할 수 있다.  아이템 23: 명령줄 도구 옵션과 관용 표현 활용하기 디버깅하는 과정에서 ‘Missing foo’와 같이 이해하기 힘든 에러 메시지가 발생했을 때, 소스 코 드의 어느 부분에서 그 메시지가 발생했는지 찾고 싶다면 다음과 같이 명령을 실행하면 된다. fgrep -lr 'Missing foo' .  여기서 하위 디렉터리를 파고 들며 파일을 탐색하도록 -r 옵션을 지정하고, 인수로 지정한 에 러 메시지가 담긴 파일을 나열하도록 -l 옵션을 지정했다. grep의 가장 큰 장점은 모든 텍스트 에 대해 적용할 수 있기 때문에 코드에 사용한 프로그래밍 언어의 종류에 관계 없이 에러 메시 지를 찾아낼 수 있다. 이 기법은 애플리케이션을 여러 가지 언어로 작성했거나 IDE 환경에 올 려서 작업할 여유가 없을 때 특히 유용하다. 참고로 방금 사용한 fgrep의 -r 옵션은 GNU에서 확장한 것으로 원래 형태의 유닉스 명령을 추구하는 이들은 좋아하지 않는 기능이다. 따라서 이 옵션이 제공되지 않는 시스템에서 작업할 때는 다음과 같이 파이프라인을 활용하면 앞에서 실행한 명령과 똑같이 작업할 수 있다. find . -type f | xargs fgrep -l 'Missing foo' 
  • 43. 994장 디버거 활용법 디버거 활용법 디버거debugger는 소프트웨어의 실행 과정을 상세하게 분석하기 위한 특수한 용도로 사용하는 도구로, 현존하는 소프트웨어 중에서도 디버거만큼 CPU와 OS로부터 특별한 기능을 지원받으 면서 시스템을 종횡무진하는 것은 없다. 그만큼 디버거는 전문가들이 인정하는 특별한 가치를 갖고 있다. 하지만 효과적으로 활용하는 방법을 모르면 디버거가 제공하는 여러 혜택을 제대로 누릴 수 없다. 따라서 이 장에서 설명하는 기술을 꼼꼼히 읽고 적용하기 바란다. 그중에서 현재 자신이 사용하는 언어와 환경에 맞지 않거나 이미 알고 있는 기술은 지나쳐도 좋다. 디버거는 다양한 형태로 나와 있다. 독립 애플리케이션 형태로 제공되기도 하고, IDE의 부가 기능으로 통합된 형태로 제공되기도 한다. 이처럼 형태는 다양하지만 사용하는 방법이나 기술 은 대부분 비슷하다. 이 장에서 소개하는 디버깅 예제들은 현재 가장 널리 사용되는 세 가지 환 경에 맞춰 구성했다. 자바와 스칼라 코드를 디버깅하는 예제는 이클립스 IDE로, C, C++, 비 주얼 C#, 비주얼 베이직 등으로 작성된 코드를 디버깅하는 예제는 비주얼 스튜디오 IDE로, 그리고 C, C++, D, 고Go, 오브젝티브-C, 포트란, 자바, 오픈CL C, 파스칼, 어셈블리, 모듈 라-2, 에이다Ada 등과 같은 언어로 작성된 프로그램을 디버깅하는 예제는 유닉스 환경의 gdb 를 이용한다. 구글 크롬에서 제공하는 자바스크립트 디버거를 비롯한 여기서 소개하지 않는 디 버거에서 이 장에서 소개한 기법을 적용하고 싶다면 해당 디버거의 매뉴얼을 참조하기 바란다. 현재 사용하는 디버거에서 이 장에서 소개하는 기술을 적용하는 데 필요한 기능을 제공하지 않 는다면 다른 디버거로 교체하는 것도 좋다. CHAPTER 4