ndk-stack-parser.c revision 5647b5efe1584bfcb3ed10ca617f7b1d73f63d9b
15e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Copyright (C) 2007-2011 The Android Open Source Project
25e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine**
35e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine** This software is licensed under the terms of the GNU General Public
45e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine** License version 2, as published by the Free Software Foundation, and
55e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine** may be copied, distributed, and modified under those terms.
65e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine**
75e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine** This program is distributed in the hope that it will be useful,
85e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine** but WITHOUT ANY WARRANTY; without even the implied warranty of
95e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
105e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine** GNU General Public License for more details.
115e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine*/
125e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
135e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/*
145e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * Contains implementation of a class DumpFile of routines that implements
155e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * access to a log file.
165e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine */
175e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
185647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh#include <inttypes.h>
195e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#include <stdio.h>
205e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#include <stdlib.h>
215e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#include <string.h>
225e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#include <errno.h>
23f9e88edc21c894886747e899dc29c02a28728d65Vladimir Chtchetkine#include "regex/regex.h"
247ecfaaa424d561d670f643993bb9b373db85d832Vladimir Chtchetkine#include "elff/elff_api.h"
255e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
265e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#include "ndk-stack-parser.h"
275e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
285e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Enumerates states of the crash parser.
295e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine */
305e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinetypedef enum NDK_CRASH_PARSER_STATE {
315e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Parser expects the beginning of the crash dump. */
325e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  EXPECTS_CRASH_DUMP,
335e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Parser expects the build fingerprint, or process and thread information. */
345e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  EXPECTS_BUILD_FINGREPRINT_OR_PID,
355e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Parser expects the process and thread information. */
365e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  EXPECTS_PID,
375e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Parser expects the signal information, or the first crash frame. */
385e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  EXPECTS_SIGNAL_OR_FRAME,
395e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Parser expects a crash frame. */
405e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  EXPECTS_FRAME,
415e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine} NDK_CRASH_PARSER_STATE;
425e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
435e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Crash parser descriptor.
445e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine */
455e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestruct NdkCrashParser {
465e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Handle to the stream where to print the output. */
475e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  FILE*                 out_handle;
485e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
495e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Path to the root folder where symbols are stored. */
505e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  char*                 sym_root;
515e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
525e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  /* Current state of the parser. */
535e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  NDK_CRASH_PARSER_STATE state;
54c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner
55c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  /* Compiled regular expressions */
56c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  regex_t     re_pid_header;
57c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  regex_t     re_sig_header;
58c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  regex_t     re_frame_header;
595e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine};
605e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
615e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Crash dumps begin with a string containing this substring. */
625e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char _crash_dump_header[] =
635e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***";
645e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
655e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Build fingerprint contains this substring. */
665e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char _build_fingerprint_header[] = "Build fingerprint:";
675e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
685e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Regular expression for the process ID information line. */
697ecfaaa424d561d670f643993bb9b373db85d832Vladimir Chtchetkinestatic const char _pid_header[] = "pid: [0-9]+, tid: [0-9]+.*";
705e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
715e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Regular expression for the signal information line. */
727ecfaaa424d561d670f643993bb9b373db85d832Vladimir Chtchetkinestatic const char _sig_header[] = "signal*[ \t][0-9]+";
735e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
745e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Regular expression for the frame information line. */
75b3682c489e0c54b1ab05f78e8f91d0642bf975e1Vladimir Chtchetkinestatic const char _frame_header[] = "\\#[0-9]+[ |\t]+[pc|eip]+:*[ |\t]+([0-9a-f]{8})*";
765e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
775e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#ifndef min
785e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#define min(a,b) (((a) < (b)) ? a : b)
795e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#endif
805e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
815e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Parses a line representing a crash frame.
825e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * This routine will try to obtain source file / line information for the
835e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * frame's address, and print that information to the specified output handle.
845e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * Param:
855e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  parser - NdkCrashParser descriptor, created and initialized with a call to
865e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *    NdkCrashParser routine.
875e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  frame - Line containing crash frame.
885e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * Return:
895e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  0 If source file information has been found and printed, or -1 if that
905e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  information was not available.
915e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine */
925e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic int ParseFrame(NdkCrashParser* parser, const char* frame);
935e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
945e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Matches a string against a regular expression.
955e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * Param:
965e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  line - String to matches against the regular expression.
975e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  regex - Regular expression to match the string against.
985e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  match - Upon successful match contains information about the part of the
995e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *    string that matches the regular expression.
1005e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * Return:
1015e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  Boolean: 1 if a match has been found, or 0 if match has not been found in
1025e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  the string.
1035e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine */
104c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turnerstatic int MatchRegex(const char* line, const regex_t* regex, regmatch_t* match);
1055e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1065e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Returns pointer to the next separator (a space, or a tab) in the string. */
1075e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char* next_separator(const char* str);
1085e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1095e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Returns pointer to the next token (a character other than space, or a tab)
1105e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * in the string.
1115e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine */
1125e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char* next_token(const char* str);
1135e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1145e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine/* Gets next token from the string.
1155e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * param:
1165e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  str - String where to get the next token from. Note that if string begins
1175e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *    with a separator, this routine will return first token after that
1185e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *    separator. If string begins with a token, this routine will return next
1195e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *    token after that.
1205e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  token - Upon success contains a copy of the next token in the string.
1215e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  size - Size of the 'token' buffer.
1225e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine * Return:
1235e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine *  Beginning of the returned token in the string.
1245e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine */
1255e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char* get_next_token(const char* str, char* token, size_t size);
1265e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1275e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir ChtchetkineNdkCrashParser*
1285e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir ChtchetkineCreateNdkCrashParser(FILE* out_handle, const char* sym_root)
1295e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
130c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  NdkCrashParser* parser;
131c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner
132c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  parser = (NdkCrashParser*)calloc(sizeof(*parser), 1);
133c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  if (parser == NULL)
134c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      return NULL;
135c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner
136c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  parser->out_handle = out_handle;
137c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  parser->state      = EXPECTS_CRASH_DUMP;
138c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner
139c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  parser->sym_root = strdup(sym_root);
140c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  if (!parser->sym_root)
141c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      goto BAD_INIT;
142c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner
143c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  if (regcomp(&parser->re_pid_header, _pid_header, REG_EXTENDED | REG_NEWLINE) ||
144c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      regcomp(&parser->re_sig_header, _sig_header, REG_EXTENDED | REG_NEWLINE) ||
145c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      regcomp(&parser->re_frame_header, _frame_header, REG_EXTENDED | REG_NEWLINE))
146c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      goto BAD_INIT;
1475e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1485e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  return parser;
149c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner
150c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' TurnerBAD_INIT:
151c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  DestroyNdkCrashParser(parser);
152c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  return NULL;
1535e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
1545e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1555e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinevoid
1565e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir ChtchetkineDestroyNdkCrashParser(NdkCrashParser* parser)
1575e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
1585e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  if (parser != NULL) {
159c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    /* Release compiled regular expressions */
160c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    regfree(&parser->re_frame_header);
161c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    regfree(&parser->re_sig_header);
162c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    regfree(&parser->re_pid_header);
163c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    /* Release symbol path */
164c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    free(parser->sym_root);
165c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    /* Release parser itself */
1665e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    free(parser);
1675e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
1685e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
1695e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1705e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkineint
1715e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir ChtchetkineParseLine(NdkCrashParser* parser, const char* line)
1725e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
1735e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  regmatch_t match;
1745647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  int found = 0;
1755e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1765e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  if (line == NULL || *line == '\0') {
1775e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    // Nothing to parse.
1785e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    return 1;
1795e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
1805e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1815e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // Lets see if this is the beginning of a crash dump.
1825e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  if (strstr(line, _crash_dump_header) != NULL) {
1835e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    if (parser->state != EXPECTS_CRASH_DUMP) {
1845e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      // Printing another crash dump was in progress. Mark the end of it.
1855e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      fprintf(parser->out_handle, "Crash dump is completed\n\n");
1865e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    }
1875e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1885e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    // New crash dump begins.
1895e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    fprintf(parser->out_handle, "********** Crash dump: **********\n");
1905e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    parser->state = EXPECTS_BUILD_FINGREPRINT_OR_PID;
1915e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1925e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    return 0;
1935e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
1945e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
1955e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  switch (parser->state) {
1965e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    case EXPECTS_BUILD_FINGREPRINT_OR_PID:
1975e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      if (strstr(line, _build_fingerprint_header) != NULL) {
1985e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        fprintf(parser->out_handle, "%s\n",
1995e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine                strstr(line, _build_fingerprint_header));
2005e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        parser->state = EXPECTS_PID;
2015647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh        found = 1;
2025e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      }
2035e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      // Let it fall through to the EXPECTS_PID, in case the dump doesn't
2045e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      // contain build fingerprint.
2055e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    case EXPECTS_PID:
206c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      if (MatchRegex(line, &parser->re_pid_header, &match)) {
2075e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        fprintf(parser->out_handle, "%s\n", line + match.rm_so);
2085e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        parser->state = EXPECTS_SIGNAL_OR_FRAME;
2095e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        return 0;
2105e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      } else {
2115647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh        return !found;
2125e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      }
2135e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2145e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    case EXPECTS_SIGNAL_OR_FRAME:
215c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      if (MatchRegex(line, &parser->re_sig_header, &match)) {
2165e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        fprintf(parser->out_handle, "%s\n", line + match.rm_so);
2175e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        parser->state = EXPECTS_FRAME;
2185647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh        found = 1;
2195e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      }
2205e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      // Let it fall through to the EXPECTS_FRAME, in case the dump doesn't
2215e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      // contain signal fingerprint.
2225e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    case EXPECTS_FRAME:
223c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      if (MatchRegex(line, &parser->re_frame_header, &match)) {
2245e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        parser->state = EXPECTS_FRAME;
2255e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        return ParseFrame(parser, line + match.rm_so);
2265e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      } else {
2275647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh        return !found;
2285e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      }
2295e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2305e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    default:
2315e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      return 1;
2325e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
2335e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
2345e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2355e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic int
236c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' TurnerMatchRegex(const char* line, const regex_t* regex, regmatch_t* match)
2375e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
238c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  int err = regexec(regex, line, 1, match, 0x00400/*REG_TRACE*/);
2395e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#if 0
24027db2fc052a03217689a4be833ad79458742d7faLogan Chien  char rerr[4096];
24127db2fc052a03217689a4be833ad79458742d7faLogan Chien  if (err) {
24227db2fc052a03217689a4be833ad79458742d7faLogan Chien    regerror(err, regex, rerr, sizeof(rerr));
24327db2fc052a03217689a4be833ad79458742d7faLogan Chien    fprintf(stderr, "regexec(%s, %s) has failed: %s\n", line, regex, rerr);
24427db2fc052a03217689a4be833ad79458742d7faLogan Chien  }
2455e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine#endif
2467ecfaaa424d561d670f643993bb9b373db85d832Vladimir Chtchetkine
247c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  return err == 0;
2485e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
2495e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2505e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char*
2515e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinenext_separator(const char* str)
2525e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
2535e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  return str + strcspn(str, " \t");
2545e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
2555e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2565e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char*
2575e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinenext_token(const char* str)
2585e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
2595e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  str = next_separator(str);
2605e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  return str + strspn(str, " \t");
2615e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
2625e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2635e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkinestatic const char*
2645e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkineget_next_token(const char* str, char* token, size_t size)
2655e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
2665e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  const char* start = next_token(str);
2675e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  const char* end = next_separator(start);
2685e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  if (start != end) {
269273fd4fcae7d1ee68ff3096108f623c6db6007a8Logan Chien    const size_t to_copy = min((size_t)(end - start), (size - 1));
2705e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    memcpy(token, start, to_copy);
2715e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    token[to_copy] = '\0';
2725e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    return start;
2735e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  } else {
2745e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    return NULL;
2755e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
2765e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
2775e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2785e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkineint
2795e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir ChtchetkineParseFrame(NdkCrashParser* parser, const char* frame)
2805e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine{
2815e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  uint64_t address;
2825e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  const char* wrk;
2835e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  char* eptr;
2845e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  char pc_address[17];
2855e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  char module_path[2048];
286c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  char* module_name;
2875e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  char sym_file[2048];
2885647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh#if !defined(WITH_LIBBFD)
2895e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  ELFF_HANDLE elff_handle;
2905e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  Elf_AddressInfo pc_info;
2915647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh#else
2925647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  const int ac = 5;
2935647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  char *av[ac];
2945647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  FILE *f;
2955647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh#endif
2965e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
297c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  fprintf(parser->out_handle, "Stack frame %s", frame);
2985e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
2995e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // Advance to the instruction pointer token.
3005e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  wrk = strstr(frame, "pc");
3015e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  if (wrk == NULL) {
3025e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    wrk = strstr(frame, "eip");
3035e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    if (wrk == NULL) {
3045e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      wrk = strstr(frame, "ip");
3055e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      if (wrk == NULL) {
3065e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        fprintf(parser->out_handle,
3075e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine                "Parser is unable to locate instruction pointer token.\n");
3085e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine        return -1;
3095e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine      }
3105e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    }
3115e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
3125e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
3135e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // Next token after the instruction pointer token is its address.
3145e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  wrk = get_next_token(wrk, pc_address, sizeof(pc_address));
3155e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // PC address is a hex value. Get it.
3165e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  eptr = pc_address + strlen(pc_address);
3175e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  address = strtoul(pc_address, &eptr, 16);
3185e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
3195e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // Next token is module path.
3205e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  get_next_token(wrk, module_path, sizeof(module_path));
3215e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
322c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  // Extract basename of module, we should not care about its path
323c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  // on the device.
324c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  module_name = strrchr(module_path,'/');
325c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  if (module_name == NULL)
326c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      module_name = module_path;
327c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  else {
328c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      module_name += 1;
329c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      if (*module_name == '\0') {
330c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner          /* Trailing slash in the module path, this should not happen */
331c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner          /* Back-off with the full module-path */
332c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner          module_name = module_path;
333c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      }
334c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner  }
335c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner
3365e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // Build path to the symbol file.
3375079ebbb32c44bb3a4bc3602d696521eba6abfffLogan Chien  snprintf(sym_file, sizeof(sym_file), "%s/%s", parser->sym_root, module_name);
3385e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine
3395647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh#if defined(WITH_LIBBFD)
3405647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  if ((f=fopen(sym_file, "r")) == NULL) {
3415647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh    if (errno == ENOENT) {
3425647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh        printf("\n");
3435647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh    } else {
3445647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh        printf(": Unable to open symbol file %s. Error (%d): %s\n",
3455647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh                sym_file, errno, strerror(errno));
3465647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh    }
3475647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh    return -1;
3485647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  }
3495647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh
3505647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  // call addr2line if sym_file exist
3515647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  extern int addr2line_main (int argc, char **argv);
3525647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh
3535647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  av[0] = "ndk-stack";
3545647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  av[1] = "-fpC";  // f:function, p:pretty-print, C:demangle
3555647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  av[2] = "-e";    // e:exe-filename
3565647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  av[3] = sym_file;
3575647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  av[4] = pc_address;
3585647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  (void)address;
3595647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh
3605647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  printf(": Routine ");
3615647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh  return addr2line_main(ac, av);
3625647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh#else
3635e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // Init ELFF wrapper for the symbol file.
3645e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  elff_handle = elff_init(sym_file);
3655e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  if (elff_handle == NULL) {
366c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    if (errno == ENOENT) {
367c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner        fprintf(parser->out_handle, "\n");
368c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    } else {
369c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner        fprintf(parser->out_handle,
370c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner                ": Unable to open symbol file %s. Error (%d): %s\n",
371c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner                sym_file, errno, strerror(errno));
372c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner    }
3735e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    return -1;
3745e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
3755e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  // Extract address info from the symbol file.
3765e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  if (!elff_get_pc_address_info(elff_handle, address, &pc_info)) {
3775e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    if (pc_info.dir_name != NULL) {
378c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      fprintf(parser->out_handle, ": Routine %s in %s/%s:%d\n",
3795e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine              pc_info.routine_name, pc_info.dir_name, pc_info.file_name,
3805e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine              pc_info.line_number);
3815e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    } else {
382c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner      fprintf(parser->out_handle, ": Routine %s in %s:%d\n",
3835e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine              pc_info.routine_name, pc_info.file_name, pc_info.line_number);
3845e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    }
3855e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    elff_free_pc_address_info(elff_handle, &pc_info);
3865e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    elff_close(elff_handle);
3875e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    return 0;
3885e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  } else {
3895e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    fprintf(parser->out_handle,
390c0bf90ae0f4f84875d0af5d7166b57deb77e57a3David 'Digit' Turner            ": Unable to locate routine information for address %x in module %s\n",
3915e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine            (uint32_t)address, sym_file);
3925e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    elff_close(elff_handle);
3935e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine    return -1;
3945e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine  }
3955647b5efe1584bfcb3ed10ca617f7b1d73f63d9bAndrew Hsieh#endif // WITH_LIBBFD
3965e0720014efeafbf6228ae4cd93b3968c1de53fcVladimir Chtchetkine}
397