2011-08-02 37 views
8

Tôi đang viết để xem liệu có ai trong số các bạn đã từng thấy hoặc nghe nói về triển khai ý tưởng mà tôi sắp mô tả hay không.printf() gỡ lỗi thư viện bằng cách sử dụng bảng chuỗi "bộ giải mã vòng"

Tôi quan tâm đến việc phát triển thư viện gỡ lỗi kiểu printf cho một mục tiêu được nhúng. Mục tiêu cực kỳ xa và ngân sách băng thông giữa tôi và mục tiêu cực kỳ chặt chẽ, vì vậy tôi muốn có thể nhận được thông báo gỡ lỗi ở định dạng rất hiệu quả.

Khá thường xuyên, báo cáo debug trông giống như sau:

myDebugLibraryPrintf("Inside loop, processing item %d out of %d.\n", i, numItems); 

Tất nhiên, khi điều này được mở rộng sang văn bản, chuỗi in là một cái gì đó như "loop Bên trong, xử lý mục 5 trong 10 \ n ", tổng cộng ~ 42 byte hoặc hơn. Hơn 90% dữ liệu được in ra bởi câu lệnh này là tĩnh, theo nghĩa đen - được biết đến tại thời gian biên dịch. Tất nhiên, chỉ có "5" và "10" không được biết tại thời gian biên dịch.

Điều tôi muốn làm là chỉ có thể gửi lại hai số nguyên đó (8 byte thay vì 42). Khi tôi đã nhận được dữ liệu đó, tôi sẽ có một số loại "bộ giải mã" cho phép tôi "reconstitute" dữ liệu nhận được và in ra thông báo gỡ lỗi đầy đủ tại đây tại vị trí của tôi.

Tôi sẽ tự động tạo "vòng giải mã" (như một phần của quy trình xây dựng) cho mỗi myDebugLibraryPrintf() tuyên bố một ID duy nhất tại thời gian biên dịch và tạo bảng ánh xạ các ID duy nhất đó . Sau đó, bất kỳ lúc nào myDebugLibraryPrintf() được gọi trên đích, nó truyền ID duy nhất và bất kỳ giá trị varargs "%d", "%f", v.v ... được xem trong chuỗi định dạng, nhưng chuỗi định dạng không được truyền đi. (Có lẽ tôi sẽ không cho phép "%s" mục bây giờ ...) Quay lại vị trí của tôi, chúng tôi sẽ có một chương trình tìm kiếm các ID duy nhất trong bảng, tìm chuỗi định dạng thích hợp và sử dụng nó để tạo lại gỡ lỗi ban đầu thông điệp.

Tôi cảm thấy như ai đó có thể đã có ý tưởng này trước đây và tôi nghĩ có thể một người nào đó trong cộng đồng đã thấy thứ gì đó giống như nó (hoặc thậm chí biết thư viện nguồn mở thực hiện điều này).

ràng buộc:

  • Để làm rõ, tôi đang đối phó với C/C++ ở đây, và tôi không quan tâm đến một -complete 100% thực hiện thay thế printf() - những thứ như phi các chuỗi định dạng theo nghĩa đen, %s (chuỗi) định dạng thông số, hoặc nhiều định dạng nâng cao hơn như đặt chiều rộng hoặc độ chính xác trong danh sách varargs với %*.*d không cần phải được hỗ trợ.

  • Tôi muốn bảng chuỗi được tạo tự động như một phần của quá trình xây dựng để thêm gỡ rối không liên quan đến công việc nhiều hơn việc thêm printf truyền thống(). Nếu có nhiều hơn số tiền tối thiểu của nỗ lực là cần thiết, không ai trong dự án của tôi sẽ sử dụng nó.

  • Làm thêm công việc như là một phần của quá trình tạo để tạo bảng chuỗi được giả định khá nhiều. May mắn thay, tôi có quyền kiểm soát tất cả các mã nguồn mà tôi quan tâm đến việc sử dụng thư viện này, và tôi có rất nhiều tính linh hoạt trong quá trình xây dựng.

Cảm ơn!

+2

Những gì bạn đang đề xuất là một hình thức đơn giản của nén dữ liệu. Bạn có thể tiết kiệm rất nhiều thời gian và công sức, và vẫn nhận được 90% lợi ích, đơn giản bằng cách lọc đầu ra gỡ lỗi của chương trình của bạn thông qua gzip trước khi gửi nó qua liên kết và lọc nó qua gunzip ở đầu kia. gzip/gunzip sẽ tự động xây dựng các bảng biểu tượng và thực hiện nén cho bạn, mà không hạn chế đầu ra chương trình của bạn theo cách mà lược đồ mã thông báo thủ công sẽ làm như thế nào. –

+0

Có phải C hoặc C++ không? (Biên tập viên Tag dường như không đồng ý) – AShelly

+0

@ Jeremy Friesner: Trong các cuộc thảo luận của tôi với các đồng nghiệp, nén cũng xuất hiện như một lựa chọn, và dường như nó có thể là một lựa chọn tốt - tuy nhiên, 'd có được lợi ích so sánh từ nó. Trong ví dụ tôi đã đưa ra, tôi đã có thể gửi hiệu quả 42 byte giá trị thông tin trong 8 byte - tiết kiệm không gian là 80%. Gzip thực sự có thể đạt được tiết kiệm không gian 80% đối với loại dữ liệu này không? (Tôi cần phải làm một thử nghiệm để tìm hiểu.) Tất nhiên, ngay cả khi gzip không thể đạt được cùng một khoản tiết kiệm, tôi có thể sẵn sàng chấp nhận sự không hiệu quả tương đối trong tên của độ phức tạp thấp hơn. – jeremytrimble

Trả lời

3

Tôi chỉ thấy ý tưởng này được triển khai với một bộ chuỗi được xác định trước. Mã sẽ trông giống như debug_print(INSIDE_LOOP_MSG_ID, i, n).Khi các nhà phát triển muốn thêm thông điệp mới, họ sẽ phải đặt văn bản mới trong một tệp tiêu đề cụ thể và cung cấp cho nó một ID mới.

Tôi nghĩ rằng ý tưởng tạo ra nó trên bay từ một tuyên bố in bình thường nhìn là một thách thức thú vị. Tôi đã không đi qua bất kỳ hiện thực hiện có.

Một ý tưởng có thể là macro/mẫu biến đối số chuỗi đầu tiên thành hash value at compile time. Vì vậy, nhà phát triển viết debug_print("test %d",i), được biên soạn thành debug_port_send(0x1d3s, i). Viết một kịch bản hậu xử lý để trích xuất các chuỗi và băm để sử dụng ở phía nhận thức nên đơn giản. (cách đơn giản nhất để giải quyết xung đột băm sẽ là cung cấp thông báo lỗi và buộc người dùng phải thay đổi từ ngữ một chút).

chỉnh sửa:
Vì vậy, tôi đã thử điều này với hàm băm biên dịch tại liên kết ở trên.

#define QQuot_(x) #x 
#define QQuote(x) QQuot_(x) 
#define Debug_Print(s, v) (Send(CONSTHASH(QQuote(__LINE__)##s), *((long*)&(v)))) 

void Send(long hash, long value) 
{ 
    printf("Sending %x %x\n", hash, value); //replace with COMMS 
} 


int main() 
{ 
    int i = 1; 
    float f= 3.14f; 
    Debug_Print("This is a test %d", i); 
    i++; 
    Debug_Print("This is a test %d", i); 
    Debug_Print("This was test %f", f); 
} 

Với một chút thông minh hơn, bạn có thể hỗ trợ nhiều đối số. Kiểm tra sự không đồng ý cho thấy rằng tất cả các băm thực sự được tính toán tại thời gian biên dịch. Đầu ra là như mong đợi, không có va chạm từ các chuỗi giống hệt nhau. (This page khẳng định hex là đúng cho 3,14):

Sending 94b7555c 1 
Sending 62fce13e 2 
Sending 506e9a0c 4048f5c3 

Tất cả bạn cần bây giờ là một kịch bản xử lý văn bản có thể được chạy trên các mã được chiết xuất các chuỗi từ Debug_Print, tính toán băm và populates một bảng của bạn bên nhận. Người nhận nhận được giá trị băm từ cuộc gọi Send, tra cứu chuỗi đi kèm với nó và chuyển điều đó, cùng với (các) đối số đến cuộc gọi printf bình thường.

Vấn đề duy nhất tôi thấy là các macro được lồng trong băm thời gian biên dịch là confusing my refactoring plug-in and killing my IDE responsiveness. Vô hiệu hóa bổ trợ đã xóa sự cố đó.

+2

Tôi đã suy nghĩ về việc tạo ID duy nhất dựa trên '__FILE__' và' __LINE__' mà tại đó printf() được gọi. Vì tôi có TẤT CẢ mã nguồn quan tâm, sẽ không khó để gán một số riêng biệt cho mỗi tệp nguồn (tự động hoặc rõ ràng) và sau đó tạo một ID duy nhất 32 bit bằng cách sử dụng một cái gì đó như 'uniqueID = (fileId < <14) | lineNum', nhưng ý tưởng băm của bạn thực sự có thể cho phép tôi loại bỏ một ID duy nhất 16 bit, đặc biệt nếu tôi bao gồm tệp và dòng như một phần của khóa băm và sử dụng [gperf] (http://www.gnu.org/s/gperf /) để tạo hàm băm. – jeremytrimble

+0

Tôi sẽ chỉ định các hằng số tên dài để lập chỉ mục vào một bảng chuỗi. Vì vậy, nếu chuỗi là "Bên trong vòng lặp, mục xử lý% d trong số% d. \ N" Tôi sẽ sử dụng một định danh Inside_loop_processing_item_d_out_of_d (= chỉ số vào một bảng string) Nó cho phép bạn có một rất câu lệnh debug có thể đọc được và đồng thời, tránh phải đi đến macro/biên dịch băm thời gian. Hy vọng rằng, bạn sẽ không đạt đến giới hạn về độ dài biểu tượng cho hầu hết các câu lệnh in. Bảng biểu tượng sẽ cần phải được xây dựng bằng tay. – ritesh

+0

Điều đó có vi phạm ràng buộc số 2 không? "không có nhiều công việc hơn là thêm một printf truyền thống()." – AShelly

1

Tôi đã nhìn thấy một cái gì đó hoàn thành một cái gì đó tương tự trên nền tảng ARM. Tôi tin rằng nó được gọi là "Embedded Trace Macrocell". Một loạt các macro dịch các câu lệnh như TRACE_POWER_SYSTEM_VOLTAGE_REGULATOR_TRIGGER(inputX); thành hai thanh ghi ghi vào sổ đăng ký ETM. Lưu ý rằng ONLY này chấp nhận các số nguyên 16bit, 32bit và 64bit làm đối số.

Chúng tôi có thể sử dụng các công cụ ARM để trích xuất các bộ đệm (dấu thời gian) này. Sau đó, chúng tôi áp dụng một chút tiền biên dịch thủ đoạn gian trá để chuyển đổi đầu tiên (index) đăng ký viết vào một tập tin đầu ra trông như thế này:

timestamp | POWER SYSTEM | VOLTAGE REGULATOR TRIGGER | 0x2380FF23 

Mã này đã được kiểm tra để xác định kiểu dữ liệu của đối số, vì vậy chúng ta không phải bận tâm. Nó cũng có thể được chú thích với dấu thời gian "thời gian thực" (thay vì ms từ powerup), và số tệp và dòng của các câu lệnh theo dõi.

ARM được thiết lập để lưu bộ đệm tròn bên trong này (và rất nhanh), vì vậy nó có thể được sử dụng trong sản xuất. Ngay cả khi bạn không có hỗ trợ phần cứng, mặc dù ... một số khía cạnh của điều này có thể dễ dàng sao chép.

Lưu ý rằng nó cực kỳ quan trọng khi phân tích một dấu vết, bạn chỉ sử dụng tệp 'giải mã' khớp với phiên bản cụ thể của mã đang chạy trên thiết bị.

+0

Đây cũng là một ý tưởng hữu ích - không hoàn toàn là những gì tôi đã tưởng tượng, nhưng thú vị. Điều ETM này nhắc tôi về [http://www.mc.com/products/software/tatl/]("Công cụ phân tích và thư viện theo dõi ") (" TATL "- không liên quan đến Zelda) mà tôi đã sử dụng PPCs trong quá khứ, nhưng nhanh hơn thông qua hỗ trợ harware (luôn luôn mát mẻ). Bạn đã chạm vào vấn đề bảo trì chính trong câu cuối cùng - để giải quyết vấn đề này, tôi đã nghĩ đến việc theo dõi các phiên bản khác nhau của bảng chuỗi thông qua một loại chữ ký (mật mã) và báo cáo mục tiêu được nhúng sử dụng lúc khởi động. – jeremytrimble

0

Tôi dường như gợi lại nhiều công cụ để trích xuất chuỗi ký tự cho mục đích quốc tế hóa. Các chuỗi GNU có thể trích xuất các chuỗi trực tiếp từ tệp thực thi. Điều này sẽ giúp với một phần của nhiệm vụ.

0

Tôi đã gặp vấn đề tương tự PLUS Tôi muốn giảm kích thước hình ảnh (do flash nhúng nhỏ). Giải pháp của tôi là gửi tên tệp và dòng (phải là 14-20 Byte) và có trình phân tích cú pháp nguồn ở phía máy chủ, sẽ tạo bản đồ của các văn bản thực tế. Bằng cách này, mã thực tế sẽ không chứa chuỗi "định dạng", nhưng chỉ có một chuỗi "tên tệp" cho mỗi tệp. Hơn nữa, tên tệp có thể dễ dàng được thay thế bằng enum (không giống như thay thế mọi chuỗi trong mã) để giảm thông lượng COMM.

Tôi hy vọng mẫu psaudo-mã sẽ giúp làm rõ ý tưởng:

/* target code */ 
#define PRINT(format,...) send(__FILE__,__LINE__,__VA_ARGS__) 
... 

/* host code (c++) */ 
void PrintComm(istream& in) 
{ 
    string fileName; 
    int line,nParams; 
    int* params; 
    in>>fileName>>line>>nParams; 
    if (nParams>0) 
    { 
     params = new int[nParams]; 
     for (int i=0; i<nParams; ++i) 
      in>>params[i]; 
    } 
    const char* format = FindFormat(fileName,line); 
    ... 
    delete[] params; 
} 
Các vấn đề liên quan