2013-06-12 2 views
0

Я получаю ошибку сегментации, которую я сузил до цикла for в функции обратного вызова. Странно, потому что программа раньше работала, теперь это не так!Фиксация ошибки сегментации

struct debuggerth_command debuggerth_protocol[] = { /* 
    * Note: These strings are NOT null-terminated. The 
    * strings are 4 bytes long for memory alignment and 
    * integer-cast comparisons. 
    */ 

    { "run ", debuggerth_startprocess }, 
    { "stop", 0 }, 
    { "inp ", 0 }, 
    { "sig ", 0 }, 
    { 0, 0 } 
}; 

И это код:

int debuggerth_callback (struct libwebsocket_context * context, 
        struct libwebsocket * wsi, 
        enum libwebsocket_callback_reasons reason, 
        void * user, 
        void * in, 
        size_t len){ 

switch (reason) { 

case LWS_CALLBACK_RECEIVE: 

    if (len < 4){ 
     /* send error */ 
     return -1; 
    } 

    /* Getting a segmentation fault 
    * within this loop. 
    */ 

    // I used this break to determine where the seg fault starts 
    // break 

    int i = 0; 
    for (; debuggerth_protocol[i].cmd; i++) 
     if (cmpcmd (debuggerth_protocol[i].cmd, in)) break; 


    //break; 

    if (!debuggerth_protocol[i].cmd){ 
     int byteswritten = sprintf 
      (debuggerth_message, 
      debuggerth_format, 
      debuggerth_headers[0], 
      debuggerth_errors [0]); 

     libwebsocket_write (wsi, debuggerth_message, 
             byteswritten, 
             LWS_WRITE_TEXT); 
     return -1; 
    } 

    break; 

Это сравнение строк макроса:

#define cmpcmd(cmd, str) ((*(int*)(cmd)) == (*(int*)(str))) 

Кто-нибудь есть какие-нибудь идеи?

+0

Почему 'debuggerth_protocol [i] .cmd' значение сравнения в вашем цикле for? Не будет ли это строкой, когда это должно быть числовое значение (например, количество команд внутри вашего 'debuggerth_protocol array'? – Alan

+0

Вы проверили' in! = 0'? – dyp

+0

@DyP, я этого не сделал. библиотека, которая делает обратный вызов, проверяет это заранее.Я могу просто быть уверенным, хотя. – tay10r

ответ

3

Одна идея: полагаясь на то, что ваши строки точно соответствуют размеру int, это довольно ужасно.

Люди часто пытаются делать такие умные вещи, которые могут быть сильно укушены при изменении базовых допущений, таких как переход на платформу, где тип int - это восемь байтов.

Я бы угробить этот макрос и переписать его использовать strcmp или strncmp(а).


Существует также несколько других вещей, которые нужно сделать.

Сначала распечатайте (или используйте отладчик для проверки) всех переменных перед тем, как попытаться их использовать. Может быть, in NULL.

Или, возможно, вы пытаетесь вызвать команды NULL, как stopsig или, или даже если вы получите команду, это не в таблице, и вы слепо называем его, когда i равно 4. Эти конкретные возможности в коде не показаны, после цикла, поэтому он чист, хотя я бы хотел подумать о просвещенных, спекуляциях с моей стороны.


Другая возможность заключается в том, что вы работаете с архитектурой, которая запрещает несвязанный доступ. Некоторые архитектуры оптимизированы для доступа на определенных границах (например, получение 32-битных значений из 32-разрядных согласованных адресов) и будут работать медленнее, если вы нарушите это выравнивание.

Тем не менее, архитектуры не позволят без выровненного доступа вообще, вместо этого давая что-то вроде ошибки шины, если вы попробуете.

Поскольку теперь вы указали в комментарии, что используете ARM, это почти наверняка. См. here для получения дополнительной информации.

Если это так, это даже больше причина избавиться от сложного макроса и использовать более традиционное решение.


(а): Вы также можете исследовать термин «строгий сглаживанием» в какой-то момент, так как это может быть технически неопределенное поведение.

+0

Да, я понимаю, что вы имеете в виду. Я тестировал этот макрос, и он выполнялся почти в два раза быстрее, чем 'strcmp', поэтому я хотел использовать его в этой программе. Вероятно, я должен использовать тип данных, которому гарантировано 4 байта. В любом случае, я проверил его заранее и это 4 байта. – tay10r

+5

2x так же быстро, но это segfaults ... – Alan

+1

@Alan, хороший. Одна из моих любимых мантр: вы не можете быть менее оптимизированы, чем «неправильные» :-) – paxdiablo

0

Учитывая, что это работает на ARM, я думаю, ваша проблема в том, что он выполняет неравновесный доступ к памяти, который либо провалится, либо будет довольно медленным.Это не совсем ошибка. See this question for example, так как предложено -Wcast-align, вероятно, отметить это как опасное. Вы можете включить обходное решение для программного обеспечения, но это, вероятно, медленнее, чем просто его исправление в коде.

Одним из вариантов было бы использовать memcmp, который gcc может скомпилировать до чего-то почти простого, как прочитанное слово, в том случае, если оно выровнено.

Другой вариант, если производительность критическая, заключается в том, чтобы отключить цикл в случае, когда оператор выдает первый байт команды. Затем просто проверьте следующие символы как ожидалось.

0

Я рассмотрел некоторые изменения в моем коде, как предложил @Jonothan Leffler. Это изменение, которое я сделал:

struct debuggerth_command { 
    char * cmd; 
    int (*function)(struct debuggerth_session *, char * input); 
}; 

struct debuggerth_command { 
    char cmd[4]; // changed this an array 
    int (*function)(struct debuggerth_session *, char * input); 
}; 

Так что, когда я инициализируется структура здесь:

struct debuggerth_command debuggerth_protocol[] = { /* 
    * Note: These strings are NOT null-terminated. The 
    * strings are 4 bytes long for memory alignment and 
    * integer-cast comparisons. 
    */ 

    { "run ", debuggerth_startprocess }, 
    { "stop", 0 }, 
    { "inp ", 0 }, 
    { "sig ", 0 }, 
    { 0, 0 } /* Zero used to be a pointer value, 
       * but now it's the first element in a 
       * 4 byte array 
       */ 
}; 

который изменил оценку цикла for:

int i = 0; 
for (; debuggerth_protocol[i].cmd; i++) 
    if (cmpcmd (debuggerth_protocol[i].cmd, in)) break; 

Чтобы всегда оценить истинный , потому что cmd теперь действительный указатель на 4-байтовый массив, из которых первое значение равно 0.

Я удалю макрос, поскольку он может плохо работать на некоторых архитектурах. Но не удалось ли это исправить с использованием функции C11alignas?

+0

Итак, урок: если бы вы использовали стандартную петлю, которая сравнивала числовое значение (например, количество команд в массиве), вы бы не стали вводить эту проблему для отладки. Любой, кто использует ваш код, может легко добавить команды после маркера «end-command-array» и привести ваш код к ядру. – Alan

+0

Я не думаю, что кто-то это сделает. Очень легко сказать, что используется указатель нулевого окончания. Edit: Ну, теперь, я немного изменил цикл. – tay10r

+0

Собственно, я возьму этот совет. Я изменил цикл for на традиционный, используя 'sizeof (commands)/sizeof (struct ..)' – tay10r

Смежные вопросы