summaryrefslogtreecommitdiffstats
path: root/src/btparser/frame.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/btparser/frame.c')
-rw-r--r--src/btparser/frame.c1027
1 files changed, 1027 insertions, 0 deletions
diff --git a/src/btparser/frame.c b/src/btparser/frame.c
new file mode 100644
index 00000000..83680910
--- /dev/null
+++ b/src/btparser/frame.c
@@ -0,0 +1,1027 @@
+/*
+ frame.c
+
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "frame.h"
+#include "strbuf.h"
+#include "utils.h"
+#include "location.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+struct btp_frame *
+btp_frame_new()
+{
+ struct btp_frame *frame = btp_malloc(sizeof(struct btp_frame));
+ btp_frame_init(frame);
+ return frame;
+}
+
+void
+btp_frame_init(struct btp_frame *frame)
+{
+ frame->function_name = NULL;
+ frame->function_type = NULL;
+ frame->number = 0;
+ frame->source_file = NULL;
+ frame->source_line = -1;
+ frame->signal_handler_called = false;
+ frame->address = -1;
+ frame->next = NULL;
+}
+
+void
+btp_frame_free(struct btp_frame *frame)
+{
+ if (!frame)
+ return;
+ free(frame->function_name);
+ free(frame->function_type);
+ free(frame->source_file);
+ free(frame);
+}
+
+struct btp_frame *
+btp_frame_dup(struct btp_frame *frame, bool siblings)
+{
+ struct btp_frame *result = btp_frame_new();
+ memcpy(result, frame, sizeof(struct btp_frame));
+
+ /* Handle siblings. */
+ if (siblings)
+ {
+ if (result->next)
+ result->next = btp_frame_dup(result->next, true);
+ }
+ else
+ result->next = NULL; /* Do not copy that. */
+
+ /* Duplicate all strings if the copy is not shallow. */
+ if (result->function_name)
+ result->function_name = btp_strdup(result->function_name);
+ if (result->function_type)
+ result->function_type = btp_strdup(result->function_type);
+ if (result->source_file)
+ result->source_file = btp_strdup(result->source_file);
+
+ return result;
+}
+
+bool
+btp_frame_calls_func(struct btp_frame *frame,
+ const char *function_name)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name);
+}
+
+bool
+btp_frame_calls_func_in_file(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ NULL != strstr(frame->source_file, source_file);
+}
+
+bool
+btp_frame_calls_func_in_file2(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ (NULL != strstr(frame->source_file, source_file0) ||
+ NULL != strstr(frame->source_file, source_file1));
+}
+
+bool
+btp_frame_calls_func_in_file3(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1,
+ const char *source_file2)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ (NULL != strstr(frame->source_file, source_file0) ||
+ NULL != strstr(frame->source_file, source_file1) ||
+ NULL != strstr(frame->source_file, source_file2));
+}
+
+bool
+btp_frame_calls_func_in_file4(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1,
+ const char *source_file2,
+ const char *source_file3)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ (NULL != strstr(frame->source_file, source_file0) ||
+ NULL != strstr(frame->source_file, source_file1) ||
+ NULL != strstr(frame->source_file, source_file2) ||
+ NULL != strstr(frame->source_file, source_file3));
+}
+
+int
+btp_frame_cmp(struct btp_frame *f1,
+ struct btp_frame *f2,
+ bool compare_number)
+{
+ /* Singnal handler. */
+ if (f1->signal_handler_called)
+ {
+ if (!f2->signal_handler_called)
+ return 1;
+
+ /* Both contain signal handler called. */
+ return 0;
+ }
+ else
+ {
+ if (f2->signal_handler_called)
+ return -1;
+ /* No signal handler called, continue. */
+ }
+
+ /* Function. */
+ int function_name = btp_strcmp0(f1->function_name, f2->function_name);
+ if (function_name != 0)
+ return function_name;
+ int function_type = btp_strcmp0(f1->function_type, f2->function_type);
+ if (function_type != 0)
+ return function_type;
+
+ /* Sourcefile. */
+ int source_file = btp_strcmp0(f1->source_file, f2->source_file);
+ if (source_file != 0)
+ return source_file;
+
+ /* Sourceline. */
+ int source_line = f1->source_line - f2->source_line;
+ if (source_line != 0)
+ return source_line;
+
+ /* Frame number. */
+ if (compare_number)
+ {
+ int number = f1->number - f2->number;
+ if (number != 0)
+ return number;
+ }
+
+ return 0;
+}
+
+void
+btp_frame_add_sibling(struct btp_frame *a, struct btp_frame *b)
+{
+ struct btp_frame *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+}
+
+void
+btp_frame_append_to_str(struct btp_frame *frame,
+ struct btp_strbuf *str,
+ bool verbose)
+{
+ if (verbose)
+ btp_strbuf_append_strf(str, " #%d", frame->number);
+ else
+ btp_strbuf_append_str(str, " ");
+
+ if (frame->function_type)
+ btp_strbuf_append_strf(str, " %s", frame->function_type);
+ if (frame->function_name)
+ btp_strbuf_append_strf(str, " %s", frame->function_name);
+ if (verbose && frame->source_file)
+ {
+ if (frame->function_name)
+ btp_strbuf_append_str(str, " at");
+ btp_strbuf_append_strf(str, " %s", frame->source_file);
+ }
+
+ if (frame->signal_handler_called)
+ btp_strbuf_append_str(str, " <signal handler called>");
+
+ btp_strbuf_append_str(str, "\n");
+}
+
+/**
+ * Find string a or b in input, whatever comes first.
+ * If no string is found, return the \0 character at the end of input.
+ */
+static char *
+findfirstabnul(char *input, const char *a, const char *b)
+{
+ size_t alen = strlen(a);
+ size_t blen = strlen(b);
+ char *p = input;
+ while (*p)
+ {
+ if (strncmp(p, a, alen) == 0)
+ return p;
+ if (strncmp(p, b, blen) == 0)
+ return p;
+ ++p;
+ }
+ return p;
+}
+
+struct btp_frame *
+btp_frame_parse(char **input,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ struct btp_frame *header = btp_frame_parse_header(input, location);
+ if (!header)
+ return NULL;
+
+ /* Skip the variables section for now. */
+ /* Todo: speedup by implementing strstrnul. */
+ local_input = findfirstabnul(local_input, "\n#", "\nThread");
+ if (*local_input != '\0')
+ ++local_input; /* ++ skips the newline */
+
+ if (btp_debug_parser)
+ {
+ printf("frame #%u %s\n",
+ header->number,
+ header->function_name ? header->function_name : "signal handler called");
+ }
+
+ *input = local_input;
+ return header;
+}
+
+int
+btp_frame_parse_frame_start(char **input, unsigned *number)
+{
+ char *local_input = *input;
+
+ /* Read the hash sign. */
+ if (!btp_skip_char(&local_input, '#'))
+ return 0;
+ int count = 1;
+
+ /* Read the frame position. */
+ int digits = btp_parse_unsigned_integer(&local_input, number);
+ count += digits;
+ if (0 == digits)
+ return 0;
+
+ /* Read all the spaces after the positon. */
+ int spaces = btp_skip_char_sequence(&local_input, ' ');
+ count += spaces;
+ if (0 == spaces)
+ return 0;
+
+ *input = local_input;
+ return count;
+}
+
+int
+btp_frame_parseadd_operator(char **input, struct btp_strbuf *target)
+{
+ char *local_input = *input;
+ if (0 == btp_skip_string(&local_input, "operator"))
+ return 0;
+
+#define OP(x) \
+ if (0 < btp_skip_string(&local_input, x)) \
+ { \
+ btp_strbuf_append_str(target, "operator"); \
+ btp_strbuf_append_str(target, x); \
+ int length = local_input - *input; \
+ *input = local_input; \
+ return length; \
+ }
+
+ OP(">>=")OP(">>")OP(">=")OP(">");
+ OP("<<=")OP("<<")OP("<=")OP("<");
+ OP("->*")OP("->")OP("-");
+ OP("==")OP("=");
+ OP("&&")OP("&=")OP("&");
+ OP("||")OP("|=")OP("|");
+ OP("++")OP("+=")OP("+");
+ OP("--")OP("-=")OP("-");
+ OP("/=")OP("/");
+ OP("*=")OP("*");
+ OP("%=")OP("%");
+ OP("!=")OP("!");
+ OP("~");
+ OP("()");
+ OP("[]");
+ OP(",");
+ OP("^=")OP("^");
+ OP(" new[]")OP(" new");
+ OP(" delete[]")OP(" delete");
+ /* User defined operators are not parsed.
+ Should they be? */
+#undef OP
+ return 0;
+}
+
+#define FUNCTION_NAME_CHARS BTP_alnum "@.:=!*+-[]~&/%^|,_"
+
+int
+btp_frame_parse_function_name_chunk(char **input,
+ bool space_allowed,
+ char **target)
+{
+ char *local_input = *input;
+ struct btp_strbuf *buf = btp_strbuf_new();
+ while (*local_input)
+ {
+ if (0 < btp_frame_parseadd_operator(&local_input, buf))
+ {
+ /* Space is allowed after operator even when it
+ is not normally allowed. */
+ if (btp_skip_char(&local_input, ' '))
+ {
+ /* ...but if ( follows, it is not allowed. */
+ if (btp_skip_char(&local_input, '('))
+ {
+ /* Return back both the space and (. */
+ local_input -= 2;
+ }
+ else
+ btp_strbuf_append_char(buf, ' ');
+ }
+ }
+
+ if (strchr(FUNCTION_NAME_CHARS, *local_input) == NULL)
+ {
+ if (!space_allowed || strchr(" ", *local_input) == NULL)
+ break;
+ }
+
+ btp_strbuf_append_char(buf, *local_input);
+ ++local_input;
+ }
+
+ if (buf->len == 0)
+ {
+ btp_strbuf_free(buf);
+ return 0;
+ }
+
+ *target = btp_strbuf_free_nobuf(buf);
+ int total_char_count = local_input - *input;
+ *input = local_input;
+ return total_char_count;
+}
+
+int
+btp_frame_parse_function_name_braces(char **input, char **target)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '('))
+ return 0;
+
+ struct btp_strbuf *buf = btp_strbuf_new();
+ btp_strbuf_append_char(buf, '(');
+ while (true)
+ {
+ char *namechunk = NULL;
+ if (0 < btp_frame_parse_function_name_chunk(&local_input, true, &namechunk) ||
+ 0 < btp_frame_parse_function_name_braces(&local_input, &namechunk) ||
+ 0 < btp_frame_parse_function_name_template(&local_input, &namechunk))
+ {
+ btp_strbuf_append_str(buf, namechunk);
+ free(namechunk);
+ }
+ else
+ break;
+ }
+
+ if (!btp_skip_char(&local_input, ')'))
+ {
+ btp_strbuf_free(buf);
+ return 0;
+ }
+
+ btp_strbuf_append_char(buf, ')');
+ *target = btp_strbuf_free_nobuf(buf);
+ int total_char_count = local_input - *input;
+ *input = local_input;
+ return total_char_count;
+}
+
+int
+btp_frame_parse_function_name_template(char **input, char **target)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '<'))
+ return 0;
+
+ struct btp_strbuf *buf = btp_strbuf_new();
+ btp_strbuf_append_char(buf, '<');
+ while (true)
+ {
+ char *namechunk = NULL;
+ if (0 < btp_frame_parse_function_name_chunk(&local_input, true, &namechunk) ||
+ 0 < btp_frame_parse_function_name_braces(&local_input, &namechunk) ||
+ 0 < btp_frame_parse_function_name_template(&local_input, &namechunk))
+ {
+ btp_strbuf_append_str(buf, namechunk);
+ free(namechunk);
+ }
+ else
+ break;
+ }
+
+ if (!btp_skip_char(&local_input, '>'))
+ {
+ btp_strbuf_free(buf);
+ return 0;
+ }
+
+ btp_strbuf_append_char(buf, '>');
+ *target = btp_strbuf_free_nobuf(buf);
+ int total_char_count = local_input - *input;
+ *input = local_input;
+ return total_char_count;
+}
+
+bool
+btp_frame_parse_function_name(char **input,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location)
+{
+ /* Handle unknown function name, represended by double question
+ mark. */
+ if (btp_parse_string(input, "??", function_name))
+ {
+ *function_type = NULL;
+ location->column += 2;
+ return true;
+ }
+
+ char *local_input = *input;
+ /* Up to three parts of function name. */
+ struct btp_strbuf *buf0 = btp_strbuf_new(), *buf1 = NULL;
+
+ /* First character:
+ '~' for destructor
+ '*' for ????
+ '_a-zA-Z' for mangled/nonmangled function name
+ '(' to start "(anonymous namespace)::" or something
+ */
+ char first;
+ char *namechunk;
+ if (btp_parse_char_limited(&local_input, "~*_" BTP_alpha, &first))
+ {
+ /* If it's a start of 'o'perator, put the 'o' back! */
+ if (first == 'o')
+ --local_input;
+ else
+ {
+ btp_strbuf_append_char(buf0, first);
+ ++location->column;
+ }
+ }
+ else
+ {
+ int chars = btp_frame_parse_function_name_braces(&local_input,
+ &namechunk);
+ if (0 < chars)
+ {
+ btp_strbuf_append_str(buf0, namechunk);
+ free(namechunk);
+ location->column += chars;
+ }
+ else
+ {
+ location->message = "Expected function name.";
+ btp_strbuf_free(buf0);
+ return false;
+ }
+ }
+
+ /* The rest consists of function name, braces, templates...*/
+ while (true)
+ {
+ char *namechunk = NULL;
+ int chars = btp_frame_parse_function_name_chunk(&local_input,
+ false,
+ &namechunk);
+
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_braces(&local_input,
+ &namechunk);
+ }
+
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_template(&local_input,
+ &namechunk);
+ }
+
+ if (0 == chars)
+ break;
+
+ btp_strbuf_append_str(buf0, namechunk);
+ free(namechunk);
+ location->column += chars;
+ }
+
+ /* Function name MUST be ended by empty space. */
+ char space;
+ if (!btp_parse_char_limited(&local_input, BTP_space, &space))
+ {
+ btp_strbuf_free(buf0);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+
+ /* Some C++ function names and function types might contain suffix
+ " const". */
+ int chars = btp_skip_string(&local_input, "const");
+ if (0 < chars)
+ {
+ btp_strbuf_append_char(buf0, space);
+ btp_location_eat_char(location, space);
+ btp_strbuf_append_str(buf0, "const");
+ location->column += chars;
+
+ /* Check the empty space after function name again.*/
+ if (!btp_parse_char_limited(&local_input, BTP_space, &space))
+ {
+ /* Function name MUST be ended by empty space. */
+ btp_strbuf_free(buf0);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+ }
+
+ /* Maybe the first series was just a type of the function, and now
+ the real function follows. Now, we know it must not start with
+ '(', nor with '<'. */
+ chars = btp_frame_parse_function_name_chunk(&local_input,
+ false,
+ &namechunk);
+ if (0 < chars)
+ {
+ /* Eat the space separator first. */
+ btp_location_eat_char(location, space);
+
+ buf1 = btp_strbuf_new();
+ btp_strbuf_append_str(buf1, namechunk);
+ free(namechunk);
+ location->column += chars;
+
+ /* The rest consists of a function name parts, braces, templates...*/
+ while (true)
+ {
+ char *namechunk = NULL;
+ chars = btp_frame_parse_function_name_chunk(&local_input,
+ false,
+ &namechunk);
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_braces(&local_input,
+ &namechunk);
+ }
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_template(&local_input,
+ &namechunk);
+ }
+ if (0 == chars)
+ break;
+
+ btp_strbuf_append_str(buf1, namechunk);
+ free(namechunk);
+ location->column += chars;
+ }
+
+ /* Function name MUST be ended by empty space. */
+ if (!btp_parse_char_limited(&local_input, BTP_space, &space))
+ {
+ btp_strbuf_free(buf0);
+ btp_strbuf_free(buf1);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+ }
+
+ /* Again, some C++ function names might contain suffix " const" */
+ chars = btp_skip_string(&local_input, "const");
+ if (0 < chars)
+ {
+ struct btp_strbuf *buf = buf1 ? buf1 : buf0;
+ btp_strbuf_append_char(buf, space);
+ btp_location_eat_char(location, space);
+ btp_strbuf_append_str(buf, "const");
+ location->column += chars;
+
+ /* Check the empty space after function name again.*/
+ if (!btp_skip_char_limited(&local_input, BTP_space))
+ {
+ /* Function name MUST be ended by empty space. */
+ btp_strbuf_free(buf0);
+ btp_strbuf_free(buf1);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+ }
+
+ /* Return back to the empty space. */
+ --local_input;
+
+ if (buf1)
+ {
+ *function_name = btp_strbuf_free_nobuf(buf1);
+ *function_type = btp_strbuf_free_nobuf(buf0);
+ }
+ else
+ {
+ *function_name = btp_strbuf_free_nobuf(buf0);
+ *function_type = NULL;
+ }
+
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_skip_function_args(char **input, struct btp_location *location)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '('))
+ {
+ location->message = "Expected '(' to start function argument list.";
+ return false;
+ }
+ location->column += 1;
+
+ int depth = 0;
+ bool string = false;
+ bool escape = false;
+ do
+ {
+ if (string)
+ {
+ if (escape)
+ escape = false;
+ else if (*local_input == '\\')
+ escape = true;
+ else if (*local_input == '"')
+ string = false;
+ }
+ else
+ {
+ if (*local_input == '"')
+ string = true;
+ else if (*local_input == '(')
+ ++depth;
+ else if (*local_input == ')')
+ {
+ if (depth > 0)
+ --depth;
+ else
+ break;
+ }
+ }
+ btp_location_eat_char(location, *local_input);
+ ++local_input;
+ }
+ while (*local_input);
+
+ if (depth != 0 || string || escape)
+ {
+ location->message = "Unbalanced function parameter list.";
+ return false;
+ }
+
+ if (!btp_skip_char(&local_input, ')'))
+ {
+ location->message = "Expected ')' to close the function parameter list.";
+ return false;
+ }
+ location->column += 1;
+
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_parse_function_call(char **input,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ char *name = NULL, *type = NULL;
+ if (!btp_frame_parse_function_name(&local_input,
+ &name,
+ &type,
+ location))
+ {
+ /* The location message is set by the function returning
+ * false, no need to update it here. */
+ return false;
+ }
+
+ int line, column;
+ if (0 == btp_skip_char_span_location(&local_input,
+ " \n",
+ &line,
+ &column))
+ {
+ free(name);
+ free(type);
+ location->message = "Expected a space or newline after the function name.";
+ return false;
+ }
+ btp_location_add(location, line, column);
+
+ if (!btp_frame_skip_function_args(&local_input, location))
+ {
+ free(name);
+ free(type);
+ /* The location message is set by the function returning
+ * false, no need to update it here. */
+ return false;
+ }
+
+ *function_name = name;
+ *function_type = type;
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_parse_address_in_function(char **input,
+ uint64_t *address,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+
+ /* Read memory address in hexadecimal format. */
+ int digits = btp_parse_hexadecimal_number(&local_input, address);
+ location->column += digits;
+ if (0 == digits)
+ {
+ location->message = "Hexadecimal number representing memory address expected.";
+ return false;
+ }
+
+ /* Skip spaces. */
+ int chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after memory address.";
+ return false;
+ }
+
+ /* Skip keyword "in". */
+ chars = btp_skip_string(&local_input, "in");
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Keyword \"in\" expected after memory address.";
+ return false;
+ }
+
+ /* Skip spaces. */
+ chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after 'in'.";
+ return false;
+ }
+
+ /* C++ specific case for "0xfafa in vtable for function ()" */
+ chars = btp_skip_string(&local_input, "vtable");
+ location->column += chars;
+ if (0 < chars)
+ {
+ chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after 'vtable'.";
+ return false;
+ }
+
+ chars = btp_skip_string(&local_input, "for");
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Keyword \"for\" expected.";
+ return false;
+ }
+
+ chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after 'for'.";
+ return false;
+ }
+ }
+
+ if (!btp_frame_parse_function_call(&local_input,
+ function_name,
+ function_type,
+ location))
+ {
+ /* Do not update location here, it has been modified by the
+ called function. */
+ return false;
+ }
+
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_parse_file_location(char **input,
+ char **file,
+ unsigned *fileline,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ int line, column;
+ if (0 == btp_skip_char_span_location(&local_input, " \n", &line, &column))
+ {
+ location->message = "Expected a space or a newline.";
+ return false;
+ }
+ btp_location_add(location, line, column);
+
+ int chars = btp_skip_string(&local_input, "at");
+ if (0 == chars)
+ {
+ chars = btp_skip_string(&local_input, "from");
+ if (0 == chars)
+ {
+ location->message = "Expected 'at' or 'from'.";
+ return false;
+ }
+ }
+ location->column += chars;
+
+ int spaces = btp_skip_char_sequence(&local_input, ' ');
+ location->column += spaces;
+ if (0 == spaces)
+ {
+ location->message = "Expected a space before file location.";
+ return false;
+ }
+
+ char *file_name;
+ chars = btp_parse_char_span(&local_input, BTP_alnum "_/\\+.-", &file_name);
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Expected a file name.";
+ return false;
+ }
+
+ if (btp_skip_char(&local_input, ':'))
+ {
+ location->column += 1;
+ int digits = btp_parse_unsigned_integer(&local_input, fileline);
+ location->column += digits;
+ if (0 == digits)
+ {
+ free(file_name);
+ location->message = "Expected a line number.";
+ return false;
+ }
+ }
+ else
+ *fileline = -1;
+
+ *file = file_name;
+ *input = local_input;
+ return true;
+}
+
+struct btp_frame *
+btp_frame_parse_header(char **input,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ struct btp_frame *imframe = btp_frame_new(); /* im - intermediate */
+ int chars = btp_frame_parse_frame_start(&local_input,
+ &imframe->number);
+
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Frame start sequence expected.";
+ btp_frame_free(imframe);
+ return NULL;
+ }
+
+ struct btp_location internal_location;
+ btp_location_init(&internal_location);
+ if (btp_frame_parse_address_in_function(&local_input,
+ &imframe->address,
+ &imframe->function_name,
+ &imframe->function_type,
+ &internal_location))
+ {
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+
+ /* Optional section " from file.c:65" */
+ /* Optional section " at file.c:65" */
+ btp_location_init(&internal_location);
+ if (btp_frame_parse_file_location(&local_input,
+ &imframe->source_file,
+ &imframe->source_line,
+ &internal_location))
+ {
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+ }
+ }
+ else
+ {
+ btp_location_init(&internal_location);
+ if (btp_frame_parse_function_call(&local_input,
+ &imframe->function_name,
+ &imframe->function_type,
+ &internal_location))
+ {
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+
+ /* Mandatory section " at file.c:65" */
+ btp_location_init(&internal_location);
+ if (!btp_frame_parse_file_location(&local_input,
+ &imframe->source_file,
+ &imframe->source_line,
+ &internal_location))
+ {
+ location->message = "Function call in the frame header "
+ "misses mandatory \"at file.c:xy\" section";
+ btp_frame_free(imframe);
+ return NULL;
+ }
+
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+ }
+ else
+ {
+ int chars = btp_skip_string(&local_input, "<signal handler called>");
+ if (0 < chars)
+ {
+ location->column += chars;
+ imframe->signal_handler_called = true;
+ }
+ else
+ {
+ location->message = "Frame header variant not recognized.";
+ btp_frame_free(imframe);
+ return NULL;
+ }
+ }
+ }
+
+ *input = local_input;
+ return imframe;
+}