agent/native/ext/AST_debug.cpp (693 lines of code) (raw):
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
extern "C" {
#include <Zend/zend_ast.h>
}
#include <Zend/zend.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_ast.h>
#include "AST_debug.h"
#include "ConfigSnapshot.h"
#include "log.h"
#include <sys/stat.h>
#include <errno.h>
#include <php_version.h>
#include <zend_language_parser.h>
#include <zend_vm_opcodes.h>
#include "util.h"
#include "util_for_PHP.h"
#include "elastic_apm_alloc.h"
#include "AST_util.h"
#define ELASTIC_APM_CURRENT_LOG_CATEGORY ELASTIC_APM_LOG_CATEGORY_AUTO_INSTRUMENT
static bool g_astProcessDebugDumpIsEnabled = false;
static bool g_astProcessDebugDumpConvertedBackToSource = false;
static StringBuffer g_astProcessDebugDumpForPathPrefix;
static StringBuffer g_astProcessDebugDumpOutDir;
String zendAstMagicConstAttrToString( zend_ast_attr attr )
{
switch ( attr )
{
case T_DIR: return "__DIR__";
case T_FILE: return "__FILE__";
case T_LINE: return "__LINE__";
case T_NS_C: return "__NAMESPACE__";
case T_CLASS_C: return "__CLASS__";
case T_TRAIT_C: return "__TRAIT__";
case T_METHOD_C: return "__METHOD__";
case T_FUNC_C: return "__FUNCTION__";
default: return NULL;
}
}
String streamZendAstMagicConstAttr( zend_ast_attr attr, TextOutputStream* txtOutStream )
{
String asString = zendAstMagicConstAttrToString( attr );
return asString == NULL ? streamPrintf( txtOutStream, "UNKNOWN (as int: %d)", (int)attr ) : asString;
}
String zendAstKindToString( zend_ast_kind kind )
{
# define ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( enumMember ) \
case enumMember: \
return (#enumMember) \
/**/
// Up to date with PHP v8.3.2
switch ( kind )
{
/**
* zend_ast_kind enum values as of PHP 7.2
*/
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_AND );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ARG_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ARRAY );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ARRAY_ELEM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ASSIGN );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ASSIGN_OP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ASSIGN_REF );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_BINARY_OP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_BREAK );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CALL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CAST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CATCH );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CATCH_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLASS );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLASS_CONST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLASS_CONST_DECL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLONE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLOSURE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLOSURE_USES );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_COALESCE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONDITIONAL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONST_DECL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONST_ELEM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONTINUE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_DECLARE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_DIM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_DO_WHILE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ECHO );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_EMPTY );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ENCAPS_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_EXIT );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_EXPR_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_FOR );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_FOREACH );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_FUNC_DECL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_GLOBAL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_GOTO );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_GREATER );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_GREATER_EQUAL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_GROUP_USE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_HALT_COMPILER );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_IF );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_IF_ELEM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_INCLUDE_OR_EVAL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_INSTANCEOF );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ISSET );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_LABEL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_MAGIC_CONST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_METHOD );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_METHOD_CALL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_METHOD_REFERENCE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_NAME_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_NAMESPACE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_NEW );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_OR );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PARAM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PARAM_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_POST_DEC );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_POST_INC );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PRE_DEC );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PRE_INC );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PRINT );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROP_DECL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROP_ELEM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_REF );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_RETURN );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_SHELL_EXEC );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_SILENCE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_STATIC );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_STATIC_CALL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_STATIC_PROP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_STMT_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_SWITCH );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_SWITCH_CASE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_SWITCH_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_THROW );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TRAIT_ADAPTATIONS );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TRAIT_ALIAS );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TRAIT_PRECEDENCE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TRY );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TYPE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_UNARY_MINUS );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_UNARY_OP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_UNARY_PLUS );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_UNPACK );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_UNSET );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_USE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_USE_ELEM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_USE_TRAIT );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_VAR );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_WHILE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_YIELD );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_YIELD_FROM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ZNODE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ZVAL );
/**
* values added in PHP 7.3
*/
#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 7, 3, 0 ) /* if PHP version from 7.3.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONSTANT );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONSTANT_CLASS );
#endif
/**
* values added in PHP 7.4
*/
#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 7, 4, 0 ) /* if PHP version from 7.4.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ARROW_FUNC );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ASSIGN_COALESCE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLASS_NAME );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROP_GROUP );
#endif
/**
* values added in PHP 8.0
*/
#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 0, 0 ) /* if PHP version from 8.0.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ATTRIBUTE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ATTRIBUTE_GROUP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ATTRIBUTE_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CLASS_CONST_GROUP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_MATCH );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_MATCH_ARM );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_MATCH_ARM_LIST );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_NAMED_ARG );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_NULLSAFE_METHOD_CALL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_NULLSAFE_PROP );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TYPE_UNION );
#endif
/**
* values added in PHP 8.1
*/
#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 1, 0 ) /* if PHP version from 8.1.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CALLABLE_CONVERT );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_CONST_ENUM_INIT );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_ENUM_CASE );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TYPE_INTERSECTION );
#endif
#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 3, 0 ) /* if PHP version from 8.3.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_MODIFIER_LIST );
#endif
#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 4, 0 ) /* if PHP version from 8.4.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PARENT_PROPERTY_HOOK_CALL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROPERTY_HOOK );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROPERTY_HOOK_SHORT_BODY );
#endif
default:
return NULL;
}
# undef ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE
}
String streamZendAstKind( zend_ast_kind kind, TextOutputStream* txtOutStream )
{
String asString = zendAstKindToString( kind );
return asString == NULL ? streamPrintf( txtOutStream, "UNKNOWN (as int: %d)", (int)kind ) : asString;
}
typedef void (* DebugDumpAstPrintLine )( void* ctx, String text, UInt nestingDepth );
struct DebugDumpAstPrinter
{
DebugDumpAstPrintLine printLine;
void* ctx;
};
typedef struct DebugDumpAstPrinter DebugDumpAstPrinter;
void debugDumpAst( DebugDumpAstPrinter* printer, zend_ast* ast, UInt nestingDepth );
void debugDumpAstPrintLineFormattedText( DebugDumpAstPrinter* printer, String text, UInt nestingDepth )
{
printer->printLine( printer->ctx, text, nestingDepth );
}
UInt getAstLineNumber( zend_ast* ast )
{
return (UInt) zend_ast_get_lineno( ast );
}
void debugDumpAstPrintLineForNull( DebugDumpAstPrinter* printer, UInt nestingDepth )
{
debugDumpAstPrintLineFormattedText( printer, "NULL", nestingDepth );
}
void debugDumpAstPrintLineTemplate( DebugDumpAstPrinter* printer, zend_ast_kind kind, UInt lineNumber, String attrAsString, UInt childCount, String additionalInfo, UInt nestingDepth )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
String text = ( additionalInfo == NULL )
? streamPrintf( &txtOutStream, "%s (line: %u, attr: %s, childCount: %u)"
, streamZendAstKind( kind, &txtOutStream ), lineNumber, attrAsString, childCount )
: streamPrintf( &txtOutStream, "%s (line: %u, attr: %s, childCount: %u, %s)"
, streamZendAstKind( kind, &txtOutStream ), lineNumber, attrAsString, childCount, additionalInfo );
debugDumpAstPrintLineFormattedText( printer, text, nestingDepth );
}
static inline
String streamAstAttribute( zend_ast_attr attr, TextOutputStream* txtOutStream )
{
return streamPrintf( txtOutStream, "%u", attr );
}
void debugDumpAstPrintLineDefault( DebugDumpAstPrinter* printer, zend_ast* ast, UInt nestingDepth )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
String attrAsString = streamAstAttribute( ast->attr, &txtOutStream );
debugDumpAstPrintLineTemplate( printer, ast->kind, getAstLineNumber( ast ), attrAsString, getAstChildren( ast ).count, /* additionalInfo */ NULL, nestingDepth );
}
size_t calcNumberOfNonWhiteChars( StringView strVw )
{
size_t result = 0;
ELASTIC_APM_FOR_EACH_INDEX( i, strVw.length )
{
if ( ! isWhiteSpace( strVw.begin[ i ] ) )
{
++result;
}
}
return result;
}
void debugDumpAstPrintLineForDecl( DebugDumpAstPrinter* printer, zend_ast_decl* astDecl, UInt nestingDepth )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
String attrAsString = streamAstAttribute( astDecl->attr, &txtOutStream );
size_t docCommentNumberOfNonWhiteChars = calcNumberOfNonWhiteChars( nullableZStringToStringView( astDecl->doc_comment ) );
String additionalInfo = streamPrintf(
&txtOutStream
, "name: %s, end line: %u, flags: %u, doc_comment: %s"
, nullableZStringToString( astDecl->name ), (UInt)( astDecl->start_lineno ), (UInt)( astDecl->flags )
, astDecl->doc_comment == NULL ? "NULL" : streamPrintf( &txtOutStream, "[number of non-white chars: %u]", (unsigned)docCommentNumberOfNonWhiteChars )
);
debugDumpAstPrintLineTemplate( printer, astDecl->kind, astDecl->start_lineno, attrAsString, getAstChildren( (zend_ast*)astDecl ).count, additionalInfo, nestingDepth );
}
void debugDumpAstPrintLineForMagicConst( DebugDumpAstPrinter* printer, zend_ast* ast, UInt nestingDepth )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
String attrAsString = streamZendAstMagicConstAttr( ast->attr, &txtOutStream );
debugDumpAstPrintLineTemplate( printer, ast->kind, getAstLineNumber( ast ), attrAsString, getAstChildren( ast ).count, /* additionalInfo */ NULL, nestingDepth );
}
String debugDumpAstZvalStreamVal( zend_ast* ast, TextOutputStream* txtOutStream )
{
zval* zVal = zend_ast_get_zval( ast );
if ( zVal == NULL )
{
return "ast->val is NULL";
}
int zValType = (int)Z_TYPE_P( zVal );
switch ( zValType )
{
case IS_STRING:
{
StringView strVw = zStringToStringView( Z_STR_P( zVal ) );
return streamPrintf( txtOutStream, "type: string, value: %.*s", (int)(strVw.length), strVw.begin );
}
case IS_LONG:
return streamPrintf( txtOutStream, "type: long, value: %" PRId64, (Int64)(Z_LVAL_P( zVal )) );
case IS_DOUBLE:
return streamPrintf( txtOutStream, "type: double, value: %f", (double)(Z_DVAL_P( zVal )) );
case IS_NULL:
return streamPrintf( txtOutStream, "type: null" );
case IS_FALSE:
return streamPrintf( txtOutStream, "type: false" );
case IS_TRUE:
return streamPrintf( txtOutStream, "type: true " );
default:
return streamPrintf( txtOutStream, "type: %s (type ID as int: %d)", zend_get_type_by_const( zValType ), (int)zValType );
}
}
void debugDumpAstPrintLineForZVal( DebugDumpAstPrinter* printer, zend_ast* ast, UInt nestingDepth )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
String attrAsString = streamAstAttribute( ast->attr, &txtOutStream );
String additionalInfo = debugDumpAstZvalStreamVal( ast, &txtOutStream );
debugDumpAstPrintLineTemplate( printer, ast->kind, getAstLineNumber( ast ), attrAsString, getAstChildren( ast ).count, additionalInfo, nestingDepth );
}
void debugDumpAstPrintLineForBinaryOp( DebugDumpAstPrinter* printer, zend_ast* ast, UInt nestingDepth )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
String attrAsString = streamPrintf( &txtOutStream, "opcode: %s (ID as int: %d)", zend_get_opcode_name( (zend_uchar)(ast->attr) ), (int)(ast->attr) );
debugDumpAstPrintLineTemplate( printer, ast->kind, getAstLineNumber( ast ), attrAsString, getAstChildren( ast ).count, /* additionalInfo */ NULL, nestingDepth );
}
void debugDumpAstPrintLineDispatch( DebugDumpAstPrinter* printer, zend_ast* ast, UInt nestingDepth )
{
if ( isAstDecl( ast->kind ) )
{
debugDumpAstPrintLineForDecl( printer, (zend_ast_decl*)ast, nestingDepth );
return;
}
switch ( ast->kind )
{
case ZEND_AST_BINARY_OP:
debugDumpAstPrintLineForBinaryOp( printer, ast, nestingDepth );
return;
case ZEND_AST_MAGIC_CONST:
debugDumpAstPrintLineForMagicConst( printer, ast, nestingDepth );
return;
case ZEND_AST_ZVAL:
debugDumpAstPrintLineForZVal( printer, ast, nestingDepth );
return;
default:
debugDumpAstPrintLineDefault( printer, ast, nestingDepth );
return;
}
}
#pragma clang diagnostic push
#pragma ide diagnostic ignored "misc-no-recursion"
void debugDumpAst( DebugDumpAstPrinter* printer, zend_ast* ast, UInt nestingDepth )
{
if ( ast == NULL )
{
debugDumpAstPrintLineForNull( printer, nestingDepth );
return;
}
debugDumpAstPrintLineDispatch( printer, ast, nestingDepth );
ZendAstPtrArrayView children = getAstChildren( ast );
ELASTIC_APM_FOR_EACH_INDEX( i, children.count )
{
debugDumpAst( printer, children.values[ i ], nestingDepth + 1 );
}
}
#pragma clang diagnostic pop
struct DebugDumpAstPrintToLogCtx
{
LogLevel logLevel;
};
typedef struct DebugDumpAstPrintToLogCtx DebugDumpAstPrintToLogCtx;
void debugDumpAstPrintLineToLog( void* ctx, String text, UInt nestingDepth )
{
const DebugDumpAstPrintToLogCtx* const localCtx = (const DebugDumpAstPrintToLogCtx*)ctx;
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
ELASTIC_APM_LOG_WITH_LEVEL( localCtx->logLevel, "%s%s", streamIndent( nestingDepth, &txtOutStream ), text );
}
void debugDumpAstTreeToLog( zend_ast* ast, LogLevel logLevel )
{
if ( maxEnabledLogLevel() < logLevel )
{
return;
}
DebugDumpAstPrintToLogCtx ctx = (DebugDumpAstPrintToLogCtx){ .logLevel = logLevel };
DebugDumpAstPrinter printer = (DebugDumpAstPrinter){ .printLine = &debugDumpAstPrintLineToLog, .ctx = &ctx };
debugDumpAst( &printer, ast, /* nestingDepth */ 0 );
}
struct DebugDumpAstPrintToTextOutputStreamCtx
{
TextOutputStream* txtOutStream;
String result;
};
typedef struct DebugDumpAstPrintToTextOutputStreamCtx DebugDumpAstPrintToTextOutputStreamCtx;
void debugDumpAstPrintLineToTextOutputStream( void* ctx, String text, UInt nestingDepth )
{
ELASTIC_APM_UNUSED( nestingDepth );
DebugDumpAstPrintToTextOutputStreamCtx* const localCtx = (DebugDumpAstPrintToTextOutputStreamCtx*)ctx;
localCtx->result = streamPrintf( localCtx->txtOutStream, "%s", text );
}
String streamZendAstNode( zend_ast* ast, TextOutputStream* txtOutStream )
{
DebugDumpAstPrintToTextOutputStreamCtx ctx = (DebugDumpAstPrintToTextOutputStreamCtx){ .txtOutStream = txtOutStream, .result = NULL };
DebugDumpAstPrinter printer = (DebugDumpAstPrinter){ .printLine = &debugDumpAstPrintLineToTextOutputStream, .ctx = &ctx };
debugDumpAstPrintLineDispatch( &printer, ast, /* nestingDepth */ 0 );
return ctx.result;
}
struct DebugDumpAstPrintToFileCtx
{
FILE* outFile;
};
typedef struct DebugDumpAstPrintToFileCtx DebugDumpAstPrintToFileCtx;
void debugDumpAstPrintLineToFile( void* ctx, String text, UInt nestingDepth )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
const DebugDumpAstPrintToFileCtx* const localCtx = (const DebugDumpAstPrintToFileCtx*)ctx;
fputs( streamIndent( nestingDepth, &txtOutStream ), localCtx->outFile );
fputs( text, localCtx->outFile );
fputs( "\n", localCtx->outFile );
}
bool isFileSystemPathPrefix( StringView path, StringView pathPrefix )
{
return isStringViewPrefix(
path,
pathPrefix,
/* shouldIgnoreCase */
# ifdef PHP_WIN32
true
# else // #ifdef PHP_WIN32
false
# endif // #ifdef PHP_WIN32
);
}
ResultCode ensureDirectoryExists( String dirFullPath )
{
# ifdef PHP_WIN32
return resultFailure;
# else // #ifdef PHP_WIN32
int mkdirRetVal = mkdir( dirFullPath, /* mode */ S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
if ( mkdirRetVal != 0 )
{
int errnoValue = errno;
if (errnoValue == EEXIST)
{
return resultSuccess;
}
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
ELASTIC_APM_LOG_ERROR( "mkdir failed; dirFullPath: `%s', mkdirRetVal: %d, errno: %d (%s)", dirFullPath, mkdirRetVal, errnoValue, streamErrNo( errnoValue, &txtOutStream ) );
return resultFailure;
}
return resultSuccess;
# endif // #ifdef PHP_WIN32
}
ResultCode ensureDirectoriesExist( StringView fullPath )
{
ELASTIC_APM_ASSERT( ! isEmptyStringView( fullPath ), "fullPath should not be empty" );
ELASTIC_APM_LOG_DEBUG_FUNCTION_ENTRY_MSG( "fullPath: %s", fullPath.begin );
ResultCode resultCode;
StringBuffer dirFullPath = ELASTIC_APM_EMPTY_STRING_BUFFER;
size_t dirFullPathLen = 0;
char directorySeparator =
# ifdef PHP_WIN32
'\\';
# else // #ifdef PHP_WIN32
'/';
# endif // #ifdef PHP_WIN32
ELASTIC_APM_MALLOC_STRING_BUFFER_IF_FAILED_GOTO( /* maxLength */ fullPath.length, /* out */ dirFullPath );
dirFullPath.begin[ 0 ] = '\0';
ELASTIC_APM_CALL_IF_FAILED_GOTO( appendToStringBuffer( /* suffixToAppend */ fullPath, dirFullPath, /* in,out */ &dirFullPathLen ) );
ELASTIC_APM_ASSERT_EQ_UINT64( dirFullPathLen, fullPath.length );
for ( MutableString begin = &( dirFullPath.begin[ 0 ] ), current = begin + 1, end = begin + fullPath.length ; current != end ; )
{
char* pDirSep = strchr( current, directorySeparator );
if ( pDirSep == NULL )
{
break;
}
char savedDirSep = *pDirSep;
*pDirSep = '\0';
ELASTIC_APM_CALL_IF_FAILED_GOTO( ensureDirectoryExists( dirFullPath.begin ) );
*pDirSep = savedDirSep;
current = pDirSep + 1;
}
resultCode = resultSuccess;
finally:
ELASTIC_APM_FREE_STRING_BUFFER_AND_SET_TO_NULL( /* in,out */ dirFullPath );
ELASTIC_APM_UNUSED( resultCode );
ELASTIC_APM_LOG_DEBUG_FUNCTION_EXIT_MSG( "fullPath: %s", fullPath.begin );
return resultCode;
failure:
goto finally;
}
ResultCode buildFileFullPath( StringViewArrayView pathParts, /* out */ StringBuffer* pResult )
{
ResultCode resultCode;
StringBuffer result = ELASTIC_APM_EMPTY_STRING_BUFFER;
size_t length = 0;
size_t contentLength = 0;
ELASTIC_APM_FOR_EACH_INDEX( i, pathParts.count )
{
length += pathParts.values[ i ].length;
}
ELASTIC_APM_MALLOC_STRING_BUFFER_IF_FAILED_GOTO( /* maxLength */ length, /* out */ result );
result.begin[ 0 ] = '\0';
ELASTIC_APM_FOR_EACH_INDEX( i, pathParts.count )
{
ELASTIC_APM_CALL_IF_FAILED_GOTO( appendToStringBuffer( /* suffixToAppend */ pathParts.values[ i ], result, /* in,out */ &contentLength ) );
}
ELASTIC_APM_ASSERT_EQ_UINT64( contentLength, length );
*pResult = result;
result = ELASTIC_APM_EMPTY_STRING_BUFFER;
resultCode = resultSuccess;
finally:
return resultCode;
failure:
ELASTIC_APM_FREE_STRING_BUFFER_AND_SET_TO_NULL( /* in,out */ result );
goto finally;
}
void debugDumpAstSubTreeConvertedBackToSource( StringView compiledFileFullPath, StringView compiledFileRelativePath, zend_ast* ast, StringView isBeforeProcessSuffix, TextOutputStream* txtOutStream )
{
ResultCode resultCode;
FILE* convertedBackToSourceFile = NULL;
int errnoValue = 0;
StringBuffer convertedBackToSourceFileFullPath = ELASTIC_APM_EMPTY_STRING_BUFFER;
zend_string* convertedBackToSourceText = NULL;
String textAsCString;
StringView convertedBackToSourceFileExtensionSuffix = ELASTIC_APM_STRING_LITERAL_TO_VIEW( ".php" );
StringView convertedBackToSourceFileFullPathParts[] = {
stringBufferToView( g_astProcessDebugDumpOutDir ),
compiledFileRelativePath,
isBeforeProcessSuffix,
ELASTIC_APM_STRING_LITERAL_TO_VIEW( ".converted_back_to_source" ),
convertedBackToSourceFileExtensionSuffix
};
ELASTIC_APM_CALL_IF_FAILED_GOTO( buildFileFullPath( ELASTIC_APM_MAKE_ARRAY_VIEW_FROM_STATIC( StringViewArrayView, convertedBackToSourceFileFullPathParts ), /* out */ &convertedBackToSourceFileFullPath ) );
errnoValue = openFile( convertedBackToSourceFileFullPath.begin, "w", /* out */ &convertedBackToSourceFile );
if ( errnoValue != 0 )
{
ELASTIC_APM_LOG_ERROR( "Failed to open file; convertedBackToSourceFileFullPath: %s; errno: %d (%s)", convertedBackToSourceFileFullPath.begin, errnoValue, streamErrNo( errnoValue, txtOutStream ) );
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
}
ELASTIC_APM_LOG_INFO( "Printing AST converted back to source of %s to %s ...", compiledFileFullPath.begin, convertedBackToSourceFileFullPath.begin );
convertedBackToSourceText = zend_ast_export( /* prefix */ "", ast, /* suffix */ "" );
textAsCString = nullableZStringToString( convertedBackToSourceText );
if ( textAsCString == NULL )
{
ELASTIC_APM_LOG_INFO( "Nothing to print for AST of %s converted back to source to %s", compiledFileFullPath.begin, convertedBackToSourceFileFullPath.begin );
}
else
{
fputs( textAsCString, convertedBackToSourceFile );
ELASTIC_APM_LOG_INFO( "Printed AST converted back to source of %s to %s. Contents:\n%s"
, compiledFileFullPath.begin, convertedBackToSourceFileFullPath.begin, nullableZStringToString( convertedBackToSourceText ) );
}
resultCode = resultSuccess;
finally:
if ( convertedBackToSourceFile != NULL )
{
fclose( convertedBackToSourceFile );
convertedBackToSourceFile = NULL;
}
ELASTIC_APM_FREE_STRING_BUFFER_AND_SET_TO_NULL( /* in,out */ convertedBackToSourceFileFullPath );
if ( convertedBackToSourceText != NULL )
{
zend_string_release( convertedBackToSourceText );
convertedBackToSourceText = NULL;
}
ELASTIC_APM_UNUSED( resultCode );
return;
failure:
goto finally;
}
void debugDumpAstSubTreeToFile( StringView compiledFileFullPath, zend_ast* ast, bool isBeforeProcess )
{
ResultCode resultCode;
StringBuffer debugDumpFileFullPath = ELASTIC_APM_EMPTY_STRING_BUFFER;
FILE* debugDumpFile = NULL;
int errnoValue = 0;
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
DebugDumpAstPrintToFileCtx ctx;
DebugDumpAstPrinter printer;
ELASTIC_APM_LOG_DEBUG_FUNCTION_ENTRY_MSG( "compiledFileFullPath: %s", compiledFileFullPath.begin );
StringView pathPrefix = stringBufferToView( g_astProcessDebugDumpForPathPrefix );
if ( ! isFileSystemPathPrefix( compiledFileFullPath, pathPrefix ) )
{
ELASTIC_APM_LOG_DEBUG_FUNCTION_ENTRY_MSG( "Skipping this file because it does not have required prefix: %s", pathPrefix.begin );
return;
}
StringView compiledFileRelativePath = subStringView( compiledFileFullPath, pathPrefix.length );
StringView isBeforeProcessSuffix = isBeforeProcess ? ELASTIC_APM_STRING_LITERAL_TO_VIEW( ".before_AST_process" ) : ELASTIC_APM_STRING_LITERAL_TO_VIEW( ".after_AST_process" );
StringView debugDumpFileExtensionSuffix = ELASTIC_APM_STRING_LITERAL_TO_VIEW( ".txt" );
StringView debugDumpFileFullPathParts[] = {
stringBufferToView( g_astProcessDebugDumpOutDir ),
compiledFileRelativePath,
isBeforeProcessSuffix,
debugDumpFileExtensionSuffix
};
ELASTIC_APM_CALL_IF_FAILED_GOTO( buildFileFullPath( ELASTIC_APM_MAKE_ARRAY_VIEW_FROM_STATIC( StringViewArrayView, debugDumpFileFullPathParts ), /* out */ &debugDumpFileFullPath ) );
ELASTIC_APM_CALL_IF_FAILED_GOTO( ensureDirectoriesExist( stringBufferToView( debugDumpFileFullPath ) ) );
errnoValue = openFile( debugDumpFileFullPath.begin, "w", /* out */ &debugDumpFile );
if ( errnoValue != 0 )
{
ELASTIC_APM_LOG_ERROR( "Failed to open file; debugDumpFileFullPath: %s; errno: %d (%s)", debugDumpFileFullPath.begin, errnoValue, streamErrNo( errnoValue, &txtOutStream ) );
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
}
ELASTIC_APM_LOG_INFO( "Printing AST debug dump of %s to %s ...", compiledFileFullPath.begin, debugDumpFileFullPath.begin );
ctx = (DebugDumpAstPrintToFileCtx){ .outFile = debugDumpFile };
printer = (DebugDumpAstPrinter){ .printLine = &debugDumpAstPrintLineToFile, .ctx = &ctx };
debugDumpAst( &printer, ast, /* nestingDepth */ 0 );
ELASTIC_APM_LOG_INFO( "Printed AST debug dump of %s to %s", compiledFileFullPath.begin, debugDumpFileFullPath.begin );
if ( g_astProcessDebugDumpConvertedBackToSource )
{
debugDumpAstSubTreeConvertedBackToSource( compiledFileFullPath, compiledFileRelativePath, ast, isBeforeProcessSuffix, &txtOutStream );
}
resultCode = resultSuccess;
finally:
ELASTIC_APM_LOG_DEBUG_RESULT_CODE_FUNCTION_EXIT_MSG();
if ( debugDumpFile != NULL )
{
fclose( debugDumpFile );
debugDumpFile = NULL;
}
ELASTIC_APM_FREE_STRING_BUFFER_AND_SET_TO_NULL( /* in,out */ debugDumpFileFullPath );
ELASTIC_APM_UNUSED( resultCode );
return;
failure:
goto finally;
}
void debugDumpAstTree( StringView compiledFileFullPath, zend_ast* ast, bool isBeforeProcess )
{
LogLevel logLevel = g_astProcessDebugDumpIsEnabled ? logLevel_debug : logLevel_trace;
ELASTIC_APM_LOG_FUNCTION_ENTRY_MSG_WITH_LEVEL( logLevel, "compiledFileFullPath: %s, isBeforeProcess: %s, g_astProcessDebugDumpIsEnabled: %s"
, compiledFileFullPath.begin, boolToString( isBeforeProcess ), boolToString( g_astProcessDebugDumpIsEnabled ) );
debugDumpAstTreeToLog( ast, g_astProcessDebugDumpIsEnabled ? logLevel_debug : logLevel_trace );
if ( g_astProcessDebugDumpIsEnabled )
{
debugDumpAstSubTreeToFile( compiledFileFullPath, ast, isBeforeProcess );
}
ELASTIC_APM_LOG_FUNCTION_EXIT_MSG_WITH_LEVEL( logLevel, "compiledFileFullPath: %s, isBeforeProcess: %s, g_astProcessDebugDumpIsEnabled: %s"
, compiledFileFullPath.begin, boolToString( isBeforeProcess ), boolToString( g_astProcessDebugDumpIsEnabled ) );
}
StringView directorySeparatorAsStringView()
{
return ELASTIC_APM_STRING_LITERAL_TO_VIEW(
# ifdef PHP_WIN32
"\\"
# else // #ifdef PHP_WIN32
"/"
# endif // #ifdef PHP_WIN32
);
}
ResultCode ensureTrailingDirectorySeparator( StringView inPath, /* out */ StringBuffer* result )
{
ELASTIC_APM_LOG_TRACE_FUNCTION_ENTRY_MSG( "inPath: %s", inPath.begin );
ResultCode resultCode;
StringBuffer outPathBuf = ELASTIC_APM_EMPTY_STRING_BUFFER;
size_t outPathLen = 0;
size_t outPathMaxLen = inPath.length + 1;
StringView directorySeparator = directorySeparatorAsStringView();
ELASTIC_APM_MALLOC_STRING_BUFFER_IF_FAILED_GOTO( /* maxLength */ outPathMaxLen, /* out */ outPathBuf );
outPathBuf.begin[ 0 ] = '\0';
ELASTIC_APM_CALL_IF_FAILED_GOTO( appendToStringBuffer( /* suffixToAppend */ inPath, outPathBuf, /* in,out */ &outPathLen ) );
if ( ! isStringViewSuffix( inPath, directorySeparator ) )
{
ELASTIC_APM_CALL_IF_FAILED_GOTO( appendToStringBuffer( /* suffixToAppend */ directorySeparator, outPathBuf, /* in,out */ &outPathLen ) );
}
*result = outPathBuf;
outPathBuf = ELASTIC_APM_EMPTY_STRING_BUFFER;
resultCode = resultSuccess;
finally:
ELASTIC_APM_LOG_TRACE_RESULT_CODE_FUNCTION_EXIT_MSG( "inPath: %s", inPath.begin );
return resultCode;
failure:
ELASTIC_APM_FREE_STRING_BUFFER_AND_SET_TO_NULL( /* in,out */ outPathBuf );
goto finally;
#undef ELASTIC_APM_DIRECTORY_SEPARATOR
}
void astProcessDebugDumpOnRequestInit( const ConfigSnapshot* config )
{
ResultCode resultCode;
if ( config->astProcessDebugDumpOutDir == NULL )
{
return;
}
StringView pathPrefix = config->astProcessDebugDumpForPathPrefix == NULL ? ELASTIC_APM_EMPTY_STRING_VIEW : stringToView( config->astProcessDebugDumpForPathPrefix );
ELASTIC_APM_CALL_IF_FAILED_GOTO( ensureTrailingDirectorySeparator( pathPrefix, /* out */ &g_astProcessDebugDumpForPathPrefix ) );
ELASTIC_APM_CALL_IF_FAILED_GOTO( ensureTrailingDirectorySeparator( stringToView( config->astProcessDebugDumpOutDir ), /* out */ &g_astProcessDebugDumpOutDir ) );
g_astProcessDebugDumpConvertedBackToSource = config->astProcessDebugDumpConvertedBackToSource;
g_astProcessDebugDumpIsEnabled = true;
resultCode = resultSuccess;
finally:
ELASTIC_APM_UNUSED( resultCode );
return;
failure:
goto finally;
}
void astProcessDebugDumpOnRequestShutdown()
{
ELASTIC_APM_FREE_STRING_BUFFER_AND_SET_TO_NULL( /* in,out */ g_astProcessDebugDumpOutDir );
ELASTIC_APM_FREE_STRING_BUFFER_AND_SET_TO_NULL( /* in,out */ g_astProcessDebugDumpForPathPrefix );
g_astProcessDebugDumpConvertedBackToSource = false;
g_astProcessDebugDumpIsEnabled = false;
}