산발적으로 발생하는 버그를 어떻게 재현합니까?
우리는 응용 프로그램에 매번 발생하지 않는 버그가 있으므로 "논리"를 모릅니다. 오늘은 100 번도 재현하지 못합니다.
면책 조항 :이 버그는 존재하며 본 적이 있습니다. 그것은 pebkac 이나 비슷한 것이 아닙니다 .
이런 종류의 버그를 재현하기위한 일반적인 힌트는 무엇입니까?
쌍으로 문제를 분석하고 코드를 쌍으로 읽으십시오. 사실이라고 알고있는 문제 를 기록하고이 문제가 발생하기 위해 어떤 논리적 전제 조건 이 참 이어야 하는지 주장하십시오 . CSI처럼 증거를 따르십시오.
대부분의 사람들은 본능적으로 "더 많은 로깅 추가"라고 말하며 이것이 해결책이 될 수 있습니다. 그러나 많은 문제의 경우 로깅은 문제를 더 자주 또는 덜 빈번하게 만들기 위해 타이밍 종속성을 충분히 변경할 수 있기 때문에 상황을 악화시킬 수 있습니다. 빈도를 1000 분의 1에서 1,000,000 분의 1로 변경해도 문제의 실제 원인에 더 가까워지지는 않습니다.
따라서 논리적 추론으로 문제가 해결되지 않으면 코드에서 로깅 또는 어설 션으로 조사 할 수있는 몇 가지 세부 사항을 제공 할 수 있습니다.
일종의 로깅 또는 추적을 추가하십시오. 예를 들어 버그를 발생시키기 전에 사용자가 마지막으로 수행 한 X 작업을 기록합니다 (버그와 일치하도록 조건을 설정할 수있는 경우에만).
질문에 대한 일반적인 좋은 대답은 없지만 여기에 내가 찾은 내용이 있습니다.
이런 일에는 재능이 필요합니다. 모든 개발자가 다른 분야의 슈퍼 스타 인 경우에도 가장 적합한 것은 아닙니다. 그러니 재능이있는 팀을 알고 있고, 그들이 자신의 영역이 아니더라도 그들이 당신을 돕는 것에 대해 흥분하도록 충분한 사탕을 줄 수 있기를 바랍니다.
거꾸로 작업하고 과학적 조사처럼 취급하십시오. 버그부터 시작하면 잘못된 것입니다. 원인이 무엇인지에 대한 가설을 개발하십시오 (이는 창의적 / 상상적인 부분, 모든 사람이 재능을 가지고 있지 않은 예술)-코드가 어떻게 작동하는지 아는 데 많은 도움이됩니다. 각 가설에 대해 (가급적 가능성이 가장 높다고 생각하는 것에 따라 정렬 됨-다시 여기에서 순수한 직감으로 분류 됨) 원인으로 제거하려는 테스트를 개발하고 가설을 테스트하십시오. 예측을 충족하지 못한다고해서 가설이 틀렸다는 의미는 아닙니다. 가설이 틀렸다는 것이 확인 될 때까지 테스트합니다 (비록 다른 가설로 넘어 가고 싶을 가능성이 적어 지지만 확실한 실패가있을 때까지이 가설을 무시하지 마십시오).
이 과정에서 가능한 한 많은 데이터를 수집하십시오. 광범위한 로깅 및 기타 적용 가능한 항목. 데이터가 부족하다고 가설을 무시하지 말고 데이터 부족을 해결하십시오. 종종 올바른 가설에 대한 영감은 데이터 검토에서 비롯됩니다. 스택 추적에서 무언가 알아 차리기, 로그의 이상한 문제, 데이터베이스에 있어야하는 무언가 누락 등.
모든 가정을 다시 확인하십시오. 몇 가지 일반적인 메서드 호출이 더 이상 조사되지 않았기 때문에 문제가 빠르게 해결되지 않는 문제를 여러 번 보았으므로 문제가 적용되지 않는 것으로 간주되었습니다. "아, 그건 간단해야합니다." (포인트 1 참조).
가설이 부족한 경우 이는 일반적으로 시스템에 대한 지식이 부족하여 발생하며 (모든 코드 줄을 직접 작성한 경우에도 마찬가지 임) 코드를 실행하고 검토하고 시스템에 대한 추가 통찰력을 얻어야합니다. 새로운 아이디어로
물론 위의 어느 것도 보장하는 것은 없지만 그것이 내가 찾은 접근 방식이 일관되게 결과를 얻습니다.
프로그래머가 사용자 경험 충돌을 반복 할 수없는 것은 단순히 버그를 분명히 처리하는 응용 프로그램을 사용하는 특정 워크 플로와 습관을 개발했기 때문입니다.
1/100의이 빈도에서 가장 먼저해야 할 일은 예외를 처리하고 어디서든 기록하는 것입니다. 그렇지 않으면이 버그를 사냥하는 데 일주일을 더 소비 할 수 있습니다. 또한 프로젝트에서 잠재적으로 민감한 관절과 기능의 우선 순위 목록을 만드십시오. 예를 들면 다음과 같습니다. 1-멀티 스레딩 2-와일드 포인터 / 느슨한 배열 3-입력 장치에 대한 의존성 등. 이는 다른 포스터에서 제안한대로 다시 무차별 대입 할 수있는 영역을 분할하는 데 도움이됩니다.
이것은 언어에 구애받지 않기 때문에 디버깅의 몇 가지 공리를 언급하겠습니다.
컴퓨터가하는 일은 무작위가 아닙니다. '무작위 발생'은 아직 발견되지 않은 패턴을 나타냅니다. 디버깅은 패턴을 분리하는 것으로 시작됩니다. 개별 요소를 변경하고 버그 동작을 변경하는 요소를 평가합니다.
다른 사용자, 같은 컴퓨터? 같은 사용자, 다른 컴퓨터? 발생이 매우 주기적입니까? 재부팅하면주기가 변경됩니까?
참고로 한 번은 한 사람이 경험 한 버그를 본 적이 있습니다. 말 그대로 사용자 계정이 아니라 사람을 의미합니다. 사용자 A는 자신의 시스템에서 문제를 볼 수 없으며 사용자 B는 해당 워크 스테이션에 앉아 사용자 A로 로그인 한 후 즉시 버그를 재현 할 수 있습니다. 앱이 의자에있는 신체의 차이를 알 수있는 방법이 없어야합니다. 하나-
사용자는 다양한 방식으로 앱을 사용했습니다. 사용자 A는 습관적으로 핫키를 사용하여 작업을 호출하고 사용자 B는 온 스크린 컨트롤을 사용했습니다. 사용자 행동의 차이는 나중에 몇 가지 작업을 수행하면 눈에 띄는 오류로 이어집니다.
이치에 맞지 않더라도 버그의 동작에 영향을 미치는 모든 차이점을 조사해야합니다.
응용 프로그램이 MTWIDNTBMT (다중 스레드가 필요하지 않은 경우 다중 스레드)이거나 단순히 다중 스레드 (정중하게) 일 가능성이 높습니다. 다중 스레드 응용 프로그램에서 산발적 인 오류를 재현하는 좋은 방법은 다음과 같은 코드를 사용하는 것입니다 (C #).
Random rnd = new Random();
System.Threading.Thread.Sleep(rnd.Next(2000));
및 / 또는이 :
for (int i = 0; i < 4000000000; i++)
{
// tight loop
}
스레드가 평소와 다른 시간에 작업을 완료하거나 프로세서를 장시간 묶는 것을 시뮬레이션합니다.
나는 수년에 걸쳐 버그가 많은 다중 스레드 앱을 상속했으며 위의 예제와 같은 코드는 일반적으로 산발적 오류가 훨씬 더 자주 발생합니다.
자세한 로깅을 추가합니다. 시나리오를 이해하기에 충분한 로깅을 추가하려면 여러 번 (때로는 수십 번) 반복이 필요합니다. 이제 문제는 문제가 경쟁 조건 인 경우 안정적으로 재현되지 않을 가능성이 높으므로 로깅이 타이밍을 변경할 수 있고 문제가 발생하지 않을 수 있다는 것입니다. 이 경우 파일에 기록하지 않고 로그의 회전 버퍼를 메모리에 유지하고 문제가 발생했음을 감지 할 때만 디스크에 덤프합니다.
편집 : 조금 더 생각 : 이것이 GUI 애플리케이션이라면 매크로를 재생할 수있는 qa 자동화 도구로 테스트를 실행하십시오. 이것이 서비스 유형의 앱인 경우, 무슨 일이 일어나고 있는지 최소한 추측 한 다음 의심되는 코드를 실행하는 '괴상한'사용 패턴을 프로그래밍 방식으로 생성하십시오. 평소보다 더 높은 부하 등을 생성합니다.
어떤 개발 환경? C ++의 경우 가장 좋은 방법은 VMWare Workstation 기록 / 재생 일 수 있습니다. http://stackframe.blogspot.com/2007/04/workstation-60-and-death-of.html을 참조하십시오.
다른 제안으로는 스택 추적 검사와 신중한 코드 개요가 있습니다.
버그가 발생하면 자동으로 추적하기 위해 앱에 코드를 추가하십시오 (또는 메일 / SMS를 통해 알림).
가능한 모든 것을 기록하여 발생하면 올바른 시스템 상태를 파악할 수 있습니다.
또 다른 방법은 인간 기반 테스트보다 더 많은 영역을 포괄 할 수있는 자동화 된 테스트를 형식적으로 적용하는 것입니다. 긴 샷이지만 일반적으로 좋은 방법입니다.
위의 모든 것, 거기에 반 무작위 인 무차별 대입 소프트 로봇을 던지고 코드를 통해 많은 assert / verify (C / C ++, 아마도 다른 언어와 유사)를 스 캐터링합니다.
수많은 로깅과 신중한 코드 검토가 유일한 옵션입니다.
앱이 배포되고 로깅을 조정할 수없는 경우 특히 고통 스러울 수 있습니다. 그 시점에서, 당신의 유일한 선택은 미세한 빗으로 코드를 살펴보고 프로그램이 어떻게 나쁜 상태에 들어갈 수 있는지에 대해 추론하는 것입니다 (구출을위한 과학적 방법!)
종종 이러한 종류의 버그는 손상된 메모리와 관련되어 있으므로 자주 나타나지 않을 수 있습니다. 어떤 종류의 메모리 프로파일 러 (예 : valgrind)로 소프트웨어를 실행하여 문제가 있는지 확인해야합니다.
프로덕션 애플리케이션으로 시작한다고 가정 해 보겠습니다.
일반적으로 버그가 발생한다고 생각되는 영역 주변에 디버그 로깅을 추가합니다. 응용 프로그램 상태에 대한 통찰력을 제공하기 위해 로깅 문을 설정했습니다. 그런 다음 디버그 로그 수준을 켜고 사용자 / 운영자에게 다음 버그 발생 시간을 알려달라고 요청합니다. 그런 다음 로그를 분석하여 응용 프로그램 상태에 대한 힌트를 확인하고 이것이 잘못 될 수있는 사항을 더 잘 이해하도록합니다.
디버거에서 코드 디버깅을 시작할 수있는 위치를 알 때까지 1 단계를 반복합니다.
실행중인 코드의 반복 횟수가 중요 할 때도 있지만 외부 시스템 (데이터베이스, 특정 사용자 시스템, 운영 체제 등)과 구성 요소의 상호 작용이 중요한 경우도 있습니다. 프로덕션 환경에 최대한 가깝게 일치하는 디버그 환경을 설정하는 데 시간을 할애하십시오. VM 기술은이 문제를 해결하기위한 좋은 도구입니다.
다음으로 디버거를 통해 진행합니다. 여기에는 코드 / 구성 요소를 로그에서 관찰 한 상태로 만드는 일종의 테스트 도구를 만드는 것이 포함될 수 있습니다. 조건부 중단 점을 설정하는 방법을 알면 많은 시간을 절약 할 수 있으므로 디버거 내의 다른 기능에 익숙해 지십시오.
디버그, 디버그, 디버그. 몇 시간 후에도 아무데도 가지 않을 경우 잠시 휴식을 취하고 관련없는 작업을 수행하십시오. 신선한 마음과 관점으로 돌아 오십시오.
지금까지 아무것도 얻지 못했다면 1 단계로 돌아가서 다시 반복하십시오.
정말 어려운 문제의 경우 버그가 발생하는 시스템에 디버거를 설치해야 할 수 있습니다. 4 단계의 테스트 하네스와 결합하면 일반적으로 당황스러운 문제를 해결할 수 있습니다.
단위 테스트. 앱에서 버그를 테스트하는 것은 노이즈가 너무 많고 변수 요인이 너무 많기 때문에 종종 끔찍합니다. 일반적으로 (건초) 스택이 클수록 문제를 정확히 파악하기가 더 어려워집니다. 단위 테스트 프레임 워크를 창의적으로 확장하여 에지 케이스를 수용하면 선별하는 데 몇 시간 또는 며칠을 절약 할 수 있습니다.
은색 총알이 없다고 말하면서. 나도 네 아픔을 느낀다.
이 버그와 관련된 사전 및 사후 조건 확인 방법을 추가합니다.
계약에 의한 디자인을 볼 수 있습니다.
많은 인내심, 조용한기도 및 저주와 함께 다음이 필요합니다.
- 사용자 작업을 기록하는 좋은 메커니즘
- 사용자가 일부 작업 (애플리케이션, 데이터베이스 등의 상태)을 수행 할 때 데이터 상태를 수집하는 좋은 메커니즘
- 서버 환경 (예 : 특정 시간에 실행되는 바이러스 백신 소프트웨어 등)을 확인하고 오류 시간을 기록하고 추세를 찾을 수 있는지 확인합니다.
- 더 많은기도와 저주 ...
HTH.
Windows를 사용 중이고 "버그"가 비 관리 코드 (C / C ++)의 충돌 또는 일종의 손상이라고 가정 하고 Microsoft의 Application Verifier 를 살펴보십시오 . 이 도구에는 런타임 중에 항목을 확인하기 위해 활성화 할 수있는 여러 중지가 있습니다. 버그가 발생하는 시나리오에 대한 아이디어가있는 경우 AppVerifer를 실행하여 시나리오 (또는 시나리오의 스트레스 버전)를 실행 해보십시오. AppVerifier에서 pageheap을 켜거나 / RTCcsu 스위치를 사용하여 코드를 컴파일하는 것을 고려하십시오 ( 자세한 내용 은 http://msdn.microsoft.com/en-us/library/8wtf2dfz.aspx 참조 ).
" Heisenbugs "는 진단하는 데 뛰어난 기술이 필요합니다. 여기 사람들의 도움을 받으려면 더 자세히 설명하고 다양한 테스트와 검사를 참을성있게 듣고 여기에 결과를보고하고 해결할 때까지 반복해야합니다. 리소스 측면에서 너무 비쌉니다).
실제 상황, 언어, DB, 운영 시스템, 작업량 추정, 과거에 발생한 시간 및 기타 무수한 사항을 알려주고 이미 수행 한 테스트, 진행 상황 및 진행 상황을 나열해야 할 것입니다. 더 많은 작업을 수행하고 결과를 공유 할 수 있습니다.
그리고 이것은 우리가 집단적으로 그것을 찾을 수 있다는 것을 보장하지 않습니다.
사용자가 해왔 던 모든 것을 적어 두는 것이 좋습니다. 그런 버그 보고서를 10 개 가지고 있다면 그것들을 연결하는 무언가를 찾을 수 있습니다.
Read the stack trace carefully and try to guess what could be happened; then try to trace\log every line of code that potentially can cause trouble.
Keep your focus on disposing resources; many sneaky sporadical bugs i found were related to close\dispose things :).
For .NET projects You can use Elmah (Error Logging Modules and Handlers) to monitor you application for un-caught exceptions, it's very simple to install and provides a very nice interface to browse unknown errors
http://code.google.com/p/elmah/
This saved me just today in catching a very random error that was occuring during a registration process
Other than that I can only recommend trying to get as much information from your users as possible and having a thorough understanding of the project workflow
They mostly come out at night.... mostly
The team that I work with has enlisted the users in recording their time they spend in our app with CamStudio when we've got a pesky bug to track down. It's easy to install and for them to use, and makes reproducing those nagging bugs much easier, since you can watch what the users are doing. It also has no relationship to the language you're working in, since it's just recording the windows desktop.
However, this route seems to be viable only if you're developing corporate apps and have good relationships with your users.
This varies (as you say), but some of the things that are handy with this can be
- immediately going into the debugger when the problem occurs and dumping all the threads (or the equivalent, such as dumping the core immediately or whatever.)
- running with logging turned on but otherwise entirely in release/production mode. (This is possible in some random environments like c and rails but not many others.)
- do stuff to make the edge conditions on the machine worse... force low memory / high load / more threads / serving more requests
- Making sure that you're actually listening to what the users encountering the problem are actually saying. Making sure that they're actually explaining the relevant details. This seems to be the one that breaks people in the field a lot. Trying to reproduce the wrong problem is boring.
- Get used to reading assembly that was produced by optimizing compilers. This seems to stop people sometimes, and it isn't applicable to all languages/platforms, but it can help
- Be prepared to accept that it is your (the developer's) fault. Don't get into the trap of insisting the code is perfect.
- sometimes you need to actually track the problem down on the machine it is happening on.
@p.marino - not enough rep to comment =/
tl;dr - build failures due to time of day
You mentioned time of day and that caught my eye. Had a bug once were someone stayed later at work on night, tried to build and commit before they left and kept getting a failure. They eventually gave up and went home. When they caught in the next morning it built fine, they committed (probably should have been more suspiscious =] ) and the build worked for everyone. A week or two later someone stayed late and had an unexpected build failure. Turns out there was a bug in the code that made any build after 7PM break >.>
We also found a bug in one seldom used corner of the project this january that caused problems marshalling between different schemas because we were not accounting for the different calendars being 0 AND 1 month based. So if no one had messed with that part of the project we wouldn't have possibly found the bug until jan. 2011
These were easier to fix than threading issues, but still interesting I think.
hire some testers!
This has worked for really weird heisenbugs. (I'd also recommend getting a copy of "Debugging" by Dave Argans, these ideas are partly derived form using his ideas!)
(0) Check the ram of the system using something like Memtest86!
The whole system exhibits the problem, so make a test jig that exercises the whole thing. Say it's a server side thing with a GUI, you run the whole thing with a GUI test framework doing the necessary input to provoke the problem.
It doesn't fail 100% of the time, so you have to make it fail more often.
Start by cutting the system in half ( binary chop) worse case, you have to remove sub-systems one at a time. stub them out if they can't be commented out.
See if it still fails. Does it fail more often ?
Keep proper test records, and only change one variable at a time!
Worst case you use the jig and you test for weeks to get meaningful statistics. This is HARD; but remember, the jig is doing the work.
I've got No threads and only one process, and I don't talk to hardware
If the system has no threads, no communicating processes and contacts no hardware; it's tricky; heisenbugs are generally synchronization, but in the no-thread no processes case it's more likely to be uninitialized data, or data used after being released, either on the heap or the stack. Try to use a checker like valgrind.
For threaded/multi-process problems:
Try running it on a different number of CPU's. If it's running on 1, try on 4! Try forcing a 4-computer system onto 1. It'll mostly ensure things happen one at a time.
If there are threads or communicating processes this can shake out bugs.
If this is not helping but you suspect it's synchronization or threading, try changing the OS time-slice size. Make it as fine as your OS vendor allows! Sometimes this has made race conditions happen almost every time!
Obversely, try going slower on the timeslices.
Then you set the test jig running with debugger(s) attached all over the place and wait for the test jig to stop on a fault.
If all else fails, put the hardware in the freezer and run it there. The timing of everything will be shifted.
Debugging is hard and time consuming especially if you are unable to deterministically reproduce the problem. My advice to you is to find out the steps to reproduce it deterministically (not just sometimes).
There has been a lot of research in the field of failure reproduction in the past years and is still very active. Record&Replay techniques have been (so far) the research direction of most researchers. This is what you need to do:
1) Analyze the source code and determine what are the sources of non-determinism in the application, that is, what are the aspects that may take your application through different execution paths (e.g. user input, OS signals)
2) Log them in the next time you execute the application
3) When your application fails again, you have the steps-to-reproduce the failure in your log.
If your log still does not reproduce the failure, then you are dealing with a concurrency bug. In that case, you should take a look at how your application accesses shared variables. Do not attempt to record the accesses to shared variables, because you would be logging too much data, thereby causing severe slowdowns and large logs. Unfortunately, there is not much I can say that would help you to reproduce concurrency bugs, because research still has a long way to go in this subject. The best I can do is to provide a reference to the most recent advance (so far) in the topic of deterministic replay of concurrency bugs:
http://www.gsd.inesc-id.pt/~nmachado/software/Symbiosis_Tutorial.html
Best regards
Use an enhanced crash reporter. In the Delphi environment, we have EurekaLog and MadExcept. Other tools exist in other environments. Or you can diagnose the core dump. You're looking for the stack trace, which will show you where it's blowing up, how it got there, what's in memory, etc.. It's also useful to have a screenshot of the app, if it's a user-interaction thing. And info about the machine that it crashed on (OS version and patch, what else is running at the time, etc..) Both of the tools that I mentioned can do this.
If it's something that happens with a few users but you can't reproduce it, and they can, go sit with them and watch. If it's not apparent, switch seats - you "drive", and they tell you what to do. You'll uncover the subtle usability issues that way. double-clicks on a single-click button, for example, initiating re-entrancy in the OnClick event. That sort of thing. If the users are remote, use WebEx, Wink, etc., to record them crashing it, so you can analyze the playback.
참고URL : https://stackoverflow.com/questions/2515903/how-do-you-reproduce-bugs-that-occur-sporadically
'Program Tip' 카테고리의 다른 글
django 템플릿 시스템, 모델 내부에서 함수 호출 (0) | 2020.11.30 |
---|---|
Rails에서 before_filter 건너 뛰기 (0) | 2020.11.30 |
프로그래밍 방식으로 현재 페이지 가져 오기 (0) | 2020.11.30 |
HTML 캔버스 전체 화면 (0) | 2020.11.30 |
프로그래밍 방식으로 ListView에서 항목을 선택하는 방법은 무엇입니까? (0) | 2020.11.30 |