2008-08-30 29 views
11

Tôi đang tìm câu trả lời trong MS VC++.Tìm ra nguồn gốc của một ngoại lệ trong C++ sau khi nó bị bắt?

Khi gỡ lỗi một ứng dụng C++ lớn, rất tiếc là có rất nhiều trường hợp sử dụng ngoại lệ C++. Đôi khi tôi bắt một ngoại lệ một chút sau đó tôi thực sự muốn.

Ví dụ trong mã giả:

FunctionB() 
{ 
    ... 
    throw e; 
    ... 
} 

FunctionA() 
{ 
    ... 
    FunctionB() 
    ... 
} 

try 
{ 
    Function A() 
} 
catch(e) 
{ 
    (<--- breakpoint) 
    ... 
} 

tôi có thể bắt ngoại lệ với một breakpoint khi gỡ lỗi. Nhưng tôi không thể theo dõi lại nếu ngoại lệ xảy ra trong FunctionA() hoặc FunctionB() hoặc một số chức năng khác. (Giả sử sử dụng ngoại lệ rộng rãi và một phiên bản rất lớn của ví dụ trên).

Một giải pháp cho vấn đề của tôi là xác định và lưu ngăn xếp cuộc gọi trong hàm tạo ngoại lệ (tức là trước khi bị bắt). Nhưng điều này đòi hỏi tôi phải lấy tất cả các ngoại lệ từ lớp ngoại lệ cơ sở này. Nó cũng sẽ đòi hỏi rất nhiều mã, và có lẽ làm chậm chương trình của tôi.

Có cách nào dễ dàng hơn đòi hỏi ít công việc hơn không? Mà không cần phải thay đổi cơ sở mã lớn của tôi?

Có giải pháp nào tốt hơn cho vấn đề này bằng các ngôn ngữ khác không?

Trả lời

11

Nếu bạn chỉ quan tâm đến nơi ngoại lệ đến từ đâu, bạn chỉ có thể viết một macro đơn giản như

#define throwException(message) \ 
    {       \ 
     std::ostringstream oss; \ 
     oss << __FILE __ << " " << __LINE__ << " " \ 
      << __FUNC__ << " " << message; \ 
     throw std::exception(oss.str().c_str()); \ 
    } 

mà sẽ thêm tên tập tin, số dòng và tên hàm đến các văn bản ngoại lệ (nếu trình biên dịch cung cấp các macro tương ứng).

Sau đó ném ngoại lệ bằng

throwException("An unknown enum value has been passed!"); 
0

Các ngôn ngữ khác? Vâng, trong Java bạn gọi e.printStackTrace(); Nó không đơn giản hơn thế nhiều.

1

Không có cách nào tiêu chuẩn để thực hiện việc này.

Hơn nữa, ngăn xếp cuộc gọi thường phải được ghi lại tại thời điểm ngoại lệ là được ném; khi nó đã được bắt được ngăn xếp đã bị hủy, do đó bạn không còn biết điều gì đang diễn ra tại thời điểm bị ném.

Trong VC++ trên Win32/Win64, bạn có thể có được kết quả có thể sử dụng-đủ bằng cách ghi lại các giá trị từ các trình biên dịch _ReturnAddress nội tại() và đảm bảo rằng constructor lớp ngoại trừ của bạn là __declspec (noinline). Cùng với thư viện biểu tượng gỡ lỗi, tôi nghĩ bạn có thể lấy tên hàm (và số dòng, nếu .pdb của bạn chứa nó) tương ứng với địa chỉ trả về sử dụng SymGetLineFromAddr64.

4

Không có cách nào để tìm ra nguồn ngoại lệ sau khi bị bắt, trừ khi bạn đưa thông tin đó vào khi nó được ném. Vào thời điểm bạn bắt được ngoại lệ, ngăn xếp đã bị bỏ trống và không có cách nào để tạo lại trạng thái trước đó của ngăn xếp.

Đề xuất của bạn để bao gồm dấu vết ngăn xếp trong hàm tạo là đặt cược tốt nhất của bạn. Có, nó tốn thời gian trong quá trình xây dựng, nhưng có lẽ bạn không nên ném ngoại lệ thường xuyên đến nỗi đây là một mối quan tâm. Làm cho tất cả các ngoại lệ của bạn được kế thừa từ một cơ sở mới cũng có thể nhiều hơn mức bạn cần. Bạn chỉ có thể có các ngoại lệ có liên quan kế thừa (cảm ơn bạn, đa thừa kế), và có một bắt riêng cho những người đó.

Bạn có thể sử dụng chức năng StackTrace64 để tạo dấu vết (tôi cũng tin rằng có các cách khác). Hãy xem this article để biết mã ví dụ.

+0

thực sự ngăn xếp không được bỏ cho đến khi tất cả các trình xử lý bắt được yêu cầu. (c + + ngoại lệ mô hình, thực hiện trên cửa sổ SEH, làm cho một vài đi qua chuỗi ngoại lệ SEH). và hoàn toàn có thể lấy được nguồn gốc của ngoại lệ (ví dụ trong trình gỡ lỗi) nằm bên trong bắt này. – deemok

6

Có một cuốn sách tuyệt vời được viết bởi John Robbins giải quyết nhiều câu hỏi gỡ lỗi khó. Cuốn sách được gọi là Debugging Applications for Microsoft .NET and Microsoft Windows.Mặc dù có tiêu đề, cuốn sách chứa một loạt thông tin về gỡ lỗi các ứng dụng gốc C++.

Trong cuốn sách này, có một phần dài tất cả về cách để có được ngăn xếp cuộc gọi cho trường hợp ngoại lệ được ném. Nếu tôi nhớ chính xác, một số lời khuyên của ông liên quan đến việc sử dụng xử lý ngoại lệ có cấu trúc (SEH) thay vì (hoặc ngoài) ngoại lệ C++. Tôi thực sự không thể giới thiệu cuốn sách đủ cao.

+0

Thậm chí vượt quá số phiếu bầu bổ sung mà tôi đã đưa ra, tôi muốn nhấn mạnh một lần nữa rằng cuốn sách của John Robbins là một cuốn SÁCH VƯỢT QUA. Tôi cũng khuyên bạn nên sử dụng nó. – pestophagous

0

Trong trường hợp bất cứ ai quan tâm đến, một đồng nghiệp trả lời cho câu hỏi này với tôi qua email:

Artem đã viết:

Có một lá cờ để MiniDumpWriteDump() có thể làm sụp đổ tốt hơn bãi mà ý chí cho phép xem trạng thái chương trình đầy đủ, với tất cả các biến toàn cầu, v.v ... Đối với ngăn xếp cuộc gọi, tôi nghi ngờ chúng có thể tốt hơn vì tối ưu hóa ... trừ khi bạn bật (có thể một số) tối ưu hóa.

Ngoài ra, tôi nghĩ rằng vô hiệu hóa các chức năng nội tuyến và tối ưu hóa toàn bộ chương trình sẽ giúp khá nhiều.

Trong thực tế, có rất nhiều loại bãi, có thể bạn có thể chọn một nhỏ đủ nhưng vẫn có biết thêm http://msdn.microsoft.com/en-us/library/ms680519(VS.85).aspx

Những loại sẽ không giúp với cuộc gọi stack tuy nhiên, họ chỉ ảnh hưởng đến lượng biến số mà bạn sẽ có thể nhìn thấy.

Tôi nhận thấy một số loại kết xuất không được hỗ trợ trong phiên bản 5.1 của dbghelp.dll mà chúng tôi sử dụng. Chúng tôi có thể cập nhật nó lên phiên bản mới nhất, 6.9 mặc dù, tôi đã chỉ cần kiểm tra EULA cho MS Debugging Tools - dbghelp.dll mới nhất vẫn là ok để phân phối lại.

12

Bạn đã trỏ đến điểm ngắt trong mã. Vì bạn đang trong trình gỡ rối, bạn có thể đặt điểm ngắt trên hàm tạo của lớp ngoại lệ hoặc đặt trình gỡ lỗi Visual Studio để ngắt tất cả các ngoại lệ được ném (Debug-> Ngoại lệ Nhấp vào ngoại lệ C++, chọn tùy chọn được ném và không bắt buộc)

+0

duh, tại sao tôi không nghĩ về điều đó - được lưu lại bởi Stack Overflow! – paquetp

2

đây là cách tôi làm điều đó trong C++ sử dụng thư viện GCC: constructor

#include <execinfo.h> // Backtrace 
#include <cxxabi.h> // Demangling 

vector<Str> backtrace(size_t numskip) { 
    vector<Str> result; 
    std::vector<void*> bt(100); 
    bt.resize(backtrace(&(*bt.begin()), bt.size())); 
    char **btsyms = backtrace_symbols(&(*bt.begin()), bt.size()); 
    if (btsyms) { 
     for (size_t i = numskip; i < bt.size(); i++) { 
      Aiss in(btsyms[i]); 
      int idx = 0; Astr nt, addr, mangled; 
      in >> idx >> nt >> addr >> mangled; 
      if (mangled == "start") break; 
      int status = 0; 
      char *demangled = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status); 

      Str frame = (status==0) ? Str(demangled, demangled+strlen(demangled)) : 
             Str(mangled.begin(), mangled.end()); 
      result.push_back(frame); 

      free(demangled); 
     } 
     free(btsyms); 
    } 
    return result; 
} 

của ngoại lệ của bạn chỉ đơn giản là có thể gọi chức năng này và lưu trữ đi những vết đống. Nó có tham số numskip vì tôi thích cắt bỏ hàm tạo của ngoại lệ từ các dấu vết ngăn xếp của tôi.

0

Tôi sử dụng ngoại lệ của riêng mình. Bạn có thể xử lý chúng khá đơn giản - chúng cũng chứa văn bản. Tôi sử dụng định dạng:

throw Exception("comms::serial::serial()", "Something failed!"); 

Ngoài ra tôi có một định dạng ngoại lệ thứ hai:

throw Exception("comms::serial::serial()", ::GetLastError()); 

mà sau đó được chuyển đổi từ một giá trị DWORD với thông điệp thực tế sử dụng FormatMessage. Sử dụng định dạng where/what sẽ cho bạn thấy điều gì đã xảy ra và chức năng nào.

1

Trong mã gốc, bạn có thể chụp một bức ảnh khi đi bộ đến đường dây điện thoại bằng cách cài đặt Vectored Exception handler. VC++ thực hiện các ngoại lệ C++ trên các ngoại lệ SEH và một trình xử lý ngoại lệ vectơ được đưa ra trước tiên trước bất kỳ trình xử lý dựa trên khung nào. Tuy nhiên, hãy cẩn thận, các vấn đề được đưa ra bởi việc xử lý ngoại lệ vectơ có thể khó chẩn đoán.

Đồng thời Mike Stall has some warnings về cách sử dụng ứng dụng trong ứng dụng có mã được quản lý.Cuối cùng, đọc Matt Pietrek's article và đảm bảo rằng bạn hiểu SEH và xử lý ngoại lệ vectơ trước khi bạn thử điều này. (Không có gì cảm thấy khá xấu khi theo dõi một vấn đề quan trọng đối với mã bạn đã thêm giúp theo dõi các vấn đề quan trọng.)

1

Nếu bạn đang gỡ lỗi khỏi IDE, hãy vào Debug-> Exceptions, nhấn Thrown for C++ exception.

4

Đặt một breakpoint trong các nhà xây dựng đối tượng ngoại lệ. Bạn sẽ nhận được breakpoint của bạn trước khi ngoại lệ được ném.

+1

Điều này làm việc đặc biệt tốt nếu tất cả các ngoại lệ của bạn được bắt nguồn từ một lớp cơ sở duy nhất. –

1

Tôi tin rằng MSDev cho phép bạn đặt điểm ngắt khi một ngoại lệ được ném.

Hoặc đặt điểm ngắt trên hàm tạo của đối tượng ngoại lệ của bạn.

Các vấn đề liên quan