agent/native/ext/supportability.cpp (418 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.
*/
#include "supportability.h"
#include "supportability_zend.h"
#include <php.h>
#include <ext/standard/info.h>
#include <SAPI.h>
#include "php_elastic_apm.h"
#include "log.h"
#include "ConfigManager.h"
#include "util_for_PHP.h"
#include "elastic_apm_assert.h"
#include "MemoryTracker.h"
#define ELASTIC_APM_CURRENT_LOG_CATEGORY ELASTIC_APM_LOG_CATEGORY_SUPPORT
static const String redacted = "***";
static
String redactIfSecret( String value, bool isSecret )
{
return value == NULL ? NULL : ( isSecret ? redacted : value );
}
void php_info_printSectionHeading( StructuredTextPrinter* structTxtPrinter, String heading )
{
ELASTIC_APM_UNUSED( structTxtPrinter );
php_info_print_table_start();
php_info_print_table_header( 1, heading );
php_info_print_table_end();
}
void php_info_printTableBegin( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns )
{
ELASTIC_APM_UNUSED( structTxtPrinter );
ELASTIC_APM_UNUSED( numberOfColumns );
php_info_print_table_start();
}
static
void php_info_printTableCells(
StructuredTextPrinter* structTxtPrinter
, size_t numberOfColumns
, String columns[]
, void (* variadicPrintCellsFunc )( int numberOfColumns, ... )
)
{
ELASTIC_APM_UNUSED( structTxtPrinter );
switch ( numberOfColumns )
{
case 0:
variadicPrintCellsFunc( 0 );
return;
case 1:
variadicPrintCellsFunc( 1, columns[ 0 ] );
return;
case 2:
variadicPrintCellsFunc( 2, columns[ 0 ], columns[ 1 ] );
return;
case 3:
variadicPrintCellsFunc( 3, columns[ 0 ], columns[ 1 ], columns[ 2 ] );
return;
case 4:
variadicPrintCellsFunc( 4, columns[ 0 ], columns[ 1 ], columns[ 2 ], columns[ 3 ] );
return;
default:
variadicPrintCellsFunc( 5, columns[ 0 ], columns[ 1 ], columns[ 2 ], columns[ 3 ], columns[ 4 ] );
return;
}
}
void php_info_printTableHeader( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns, String columnHeaders[] )
{
php_info_printTableCells(
structTxtPrinter
, numberOfColumns
, columnHeaders
, php_info_print_table_header );
}
void php_info_printTableRow( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns, String columns[] )
{
ELASTIC_APM_UNUSED( structTxtPrinter );
php_info_printTableCells(
structTxtPrinter
, numberOfColumns
, columns
, php_info_print_table_row );
}
void php_info_printTableEnd( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns )
{
ELASTIC_APM_UNUSED( structTxtPrinter );
ELASTIC_APM_UNUSED( numberOfColumns );
php_info_print_table_end();
}
void init_php_info_StructuredTextPrinter( StructuredTextPrinter* structTxtPrinter )
{
structTxtPrinter->printSectionHeading = php_info_printSectionHeading;
structTxtPrinter->printTableBegin = php_info_printTableBegin;
structTxtPrinter->printTableHeader = php_info_printTableHeader;
structTxtPrinter->printTableRow = php_info_printTableRow;
structTxtPrinter->printTableEnd = php_info_printTableEnd;
}
static
void printConfigurationInfo( StructuredTextPrinter* structTxtPrinter )
{
const ConfigManager* const cfgManager = getGlobalTracer()->configManager;
structTxtPrinter->printSectionHeading( structTxtPrinter, "Configuration" );
String columnHeaders[] = { "Option", "Parsed value", "Raw value", "Source" };
enum { numberOfColumns = ELASTIC_APM_STATIC_ARRAY_SIZE( columnHeaders ) };
structTxtPrinter->printTableBegin( structTxtPrinter, numberOfColumns );
structTxtPrinter->printTableHeader( structTxtPrinter, numberOfColumns, columnHeaders );
ELASTIC_APM_FOR_EACH_OPTION_ID( optId )
{
GetConfigManagerOptionMetadataResult getMetaRes;
GetConfigManagerOptionValueByIdResult getValRes;
char txtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ];
getValRes.txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
getValRes.txtOutStream.shouldEncloseUserString = ( sapi_module.phpinfo_as_text != 0 );
getConfigManagerOptionMetadata( cfgManager, optId, &getMetaRes );
getConfigManagerOptionValueById( cfgManager, optId, &getValRes );
String columns[ numberOfColumns ] =
{
getMetaRes.optName
, redactIfSecret( getValRes.streamedParsedValue, getMetaRes.isSecret )
, redactIfSecret( getValRes.rawValue, getMetaRes.isSecret )
, getValRes.rawValueSourceDescription == NULL ? "Default" : getValRes.rawValueSourceDescription
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
structTxtPrinter->printTableEnd( structTxtPrinter, numberOfColumns );
}
static
void printIniEntries( StructuredTextPrinter* structTxtPrinter )
{
const ConfigManager* const cfgManager = getGlobalTracer()->configManager;
structTxtPrinter->printSectionHeading( structTxtPrinter, "INI entries" );
String columnHeaders[] =
{
"Name"
, "Raw value used for the current config"
, "Interpreted raw value used for the current config"
, "Current value"
};
enum { numberOfColumns = ELASTIC_APM_STATIC_ARRAY_SIZE( columnHeaders ) };
structTxtPrinter->printTableBegin( structTxtPrinter, numberOfColumns );
structTxtPrinter->printTableHeader( structTxtPrinter, numberOfColumns, columnHeaders );
ELASTIC_APM_FOR_EACH_OPTION_ID( optId )
{
GetConfigManagerOptionMetadataResult getMetaRes;
String originalRawValue = NULL;
String interpretedRawValue = NULL;
getConfigManagerOptionMetadata( cfgManager, optId, &getMetaRes );
getConfigManagerRawData(
cfgManager,
optId,
rawConfigSourceId_iniFile,
/* out */ &originalRawValue,
/* out */ &interpretedRawValue );
char txtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
bool currentValueExists;
String columns[ numberOfColumns ] =
{
streamStringView( getMetaRes.iniName, &txtOutStream )
, redactIfSecret( originalRawValue, getMetaRes.isSecret )
, redactIfSecret( interpretedRawValue, getMetaRes.isSecret )
, redactIfSecret( readRawOptionValueFromIni( cfgManager, optId, ¤tValueExists ), getMetaRes.isSecret )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
structTxtPrinter->printTableEnd( structTxtPrinter, numberOfColumns );
}
static
void printEnvVars( StructuredTextPrinter* structTxtPrinter )
{
const ConfigManager* const cfgManager = getGlobalTracer()->configManager;
structTxtPrinter->printSectionHeading( structTxtPrinter, "Environment variables" );
String columnHeaders[] = { "Name", "Value used for the current config", "Current value" };
enum { numberOfColumns = ELASTIC_APM_STATIC_ARRAY_SIZE( columnHeaders ) };
structTxtPrinter->printTableBegin( structTxtPrinter, numberOfColumns );
structTxtPrinter->printTableHeader( structTxtPrinter, numberOfColumns, columnHeaders );
ELASTIC_APM_FOR_EACH_OPTION_ID( optId )
{
GetConfigManagerOptionMetadataResult getMetaRes;
String originalRawValue = NULL;
String interpretedRawValue = NULL;
getConfigManagerOptionMetadata( cfgManager, optId, &getMetaRes );
getConfigManagerRawData(
cfgManager,
optId,
rawConfigSourceId_envVars,
/* out */ &originalRawValue,
/* out */ &interpretedRawValue );
String columns[ numberOfColumns ] =
{
getMetaRes.envVarName
, redactIfSecret( originalRawValue, getMetaRes.isSecret )
, redactIfSecret( readRawOptionValueFromEnvVars( cfgManager, optId ), getMetaRes.isSecret )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
structTxtPrinter->printTableEnd( structTxtPrinter, numberOfColumns );
}
static
void printMiscSelfDiagnostics( StructuredTextPrinter* structTxtPrinter )
{
structTxtPrinter->printSectionHeading( structTxtPrinter, "Misc. self diagnostics" );
char txtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
String columnHeaders[] = { "What", "Current", "Default" };
enum { numberOfColumns = ELASTIC_APM_STATIC_ARRAY_SIZE( columnHeaders ) };
structTxtPrinter->printTableBegin( structTxtPrinter, numberOfColumns );
structTxtPrinter->printTableHeader( structTxtPrinter, numberOfColumns, columnHeaders );
{
String columns[ numberOfColumns ] =
{
"Assert level"
, streamAssertLevel( getGlobalAssertLevel(), &txtOutStream )
, streamAssertLevel( ELASTIC_APM_ASSERT_DEFAULT_LEVEL, &txtOutStream )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
{
textOutputStreamRewind( &txtOutStream );
String columns[ numberOfColumns ] =
{
"Memory tracking level"
, streamMemoryTrackingLevel( getGlobalMemoryTracker()->level, &txtOutStream )
, streamMemoryTrackingLevel( ELASTIC_APM_MEMORY_TRACKING_DEFAULT_LEVEL, &txtOutStream )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
{
textOutputStreamRewind( &txtOutStream );
String columns[ numberOfColumns ] =
{
"Abort on memory leak"
, boolToString( getGlobalMemoryTracker()->abortOnMemoryLeak )
, boolToString( ELASTIC_APM_MEMORY_TRACKING_DEFAULT_ABORT_ON_MEMORY_LEAK )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
{
textOutputStreamRewind( &txtOutStream );
String columns[ numberOfColumns ] =
{
"Internal checks level"
, streamInternalChecksLevel( getGlobalInternalChecksLevel(), &txtOutStream )
, streamInternalChecksLevel( ELASTIC_APM_INTERNAL_CHECKS_DEFAULT_LEVEL, &txtOutStream )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
{
textOutputStreamRewind( &txtOutStream );
String columns[ numberOfColumns - 1 ] =
{
"NDEBUG defined"
#ifdef NDEBUG
, "Yes"
#else
, "No"
#endif
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
{
textOutputStreamRewind( &txtOutStream );
String columns[ numberOfColumns - 1 ] =
{
"ELASTIC_APM_IS_DEBUG_BUILD_01"
, streamInt( (int)(ELASTIC_APM_IS_DEBUG_BUILD_01), &txtOutStream )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
}
structTxtPrinter->printTableEnd( structTxtPrinter, numberOfColumns );
}
static
void printEffectiveLogLevels( StructuredTextPrinter* structTxtPrinter )
{
char txtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
const Logger* const logger = &( getGlobalTracer()->logger );
structTxtPrinter->printSectionHeading( structTxtPrinter, "Effective log levels" );
String columnHeaders[] = { "Sink", "Current", "Default" };
enum { numberOfColumns = ELASTIC_APM_STATIC_ARRAY_SIZE( columnHeaders ) };
structTxtPrinter->printTableBegin( structTxtPrinter, numberOfColumns );
structTxtPrinter->printTableHeader( structTxtPrinter, numberOfColumns, columnHeaders );
ELASTIC_APM_FOR_EACH_LOG_SINK_TYPE( logSinkType )
{
String columns[ numberOfColumns ] =
{
logSinkTypeName[ logSinkType ]
, streamLogLevel( logger->config.levelPerSinkType[ logSinkType ], &txtOutStream )
, streamLogLevel( defaultLogLevelPerSinkType[ logSinkType ], &txtOutStream )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
textOutputStreamRewind( &txtOutStream );
}
String columns[ numberOfColumns ] =
{
"Max enabled log level"
, streamLogLevel( logger->maxEnabledLevel, &txtOutStream )
, streamLogLevel( calcMaxEnabledLogLevel( defaultLogLevelPerSinkType ), &txtOutStream )
};
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
structTxtPrinter->printTableEnd( structTxtPrinter, numberOfColumns );
}
static
void printMiscInfo( StructuredTextPrinter* structTxtPrinter )
{
structTxtPrinter->printSectionHeading( structTxtPrinter, "Misc. info" );
enum { numberOfColumns = 2 };
structTxtPrinter->printTableBegin( structTxtPrinter, numberOfColumns );
String columns[numberOfColumns] = { "Version", PHP_ELASTIC_APM_VERSION };
structTxtPrinter->printTableRow( structTxtPrinter, ELASTIC_APM_STATIC_ARRAY_SIZE( columns ), columns );
structTxtPrinter->printTableEnd( structTxtPrinter, numberOfColumns );
}
void printSupportabilityInfo( StructuredTextPrinter* structTxtPrinter )
{
ELASTIC_APM_LOG_TRACE_FUNCTION_ENTRY();
printMiscInfo( structTxtPrinter );
printConfigurationInfo( structTxtPrinter );
printIniEntries( structTxtPrinter );
printEnvVars( structTxtPrinter );
printMiscSelfDiagnostics( structTxtPrinter );
printEffectiveLogLevels( structTxtPrinter );
ELASTIC_APM_LOG_TRACE_FUNCTION_EXIT();
}
void elasticApmModuleInfo( zend_module_entry* zend_module )
{
ELASTIC_APM_LOG_TRACE_FUNCTION_ENTRY();
StructuredTextPrinter structTxtPrinter;
init_php_info_StructuredTextPrinter( &structTxtPrinter );
printSupportabilityInfo( &structTxtPrinter );
structTxtPrinter.printSectionHeading( &structTxtPrinter, "INI entries (displayed by default PHP mechanism)" );
DISPLAY_INI_ENTRIES();
ELASTIC_APM_LOG_TRACE_FUNCTION_EXIT();
}
static
const zend_string* iniEntryValue( zend_ini_entry* iniEntry, int type )
{
return ( type == ZEND_INI_DISPLAY_ORIG ) ? ( iniEntry->modified ? iniEntry->orig_value : iniEntry->value ) : iniEntry->value;
}
void displaySecretIniValue( zend_ini_entry* iniEntry, int type )
{
const String noValue = ELASTIC_APM_CFG_OPT_HAS_NO_VALUE;
const String valueToPrint = isNullOrEmtpyZstring( iniEntryValue( iniEntry, type ) ) ? noValue : redacted;
php_printf( sapi_module.phpinfo_as_text ? "%s" : "<i>%s</i>", valueToPrint );
}
void printSectionHeadingToTextOutputStream( StructuredTextPrinter* structTxtPrinter, String heading )
{
StructuredTextToOutputStreamPrinter* structTxtToOutStreamPrinter = (StructuredTextToOutputStreamPrinter*)structTxtPrinter;
streamStringView( structTxtToOutStreamPrinter->prefix, structTxtToOutStreamPrinter->txtOutStream );
streamPrintf( structTxtToOutStreamPrinter->txtOutStream, "\n" );
streamStringView( structTxtToOutStreamPrinter->prefix, structTxtToOutStreamPrinter->txtOutStream );
streamPrintf( structTxtToOutStreamPrinter->txtOutStream, "%s\n", heading );
}
#define ELASTIC_APM_MIN_TABLE_CELL_MIN_WIDTH 25
// Section A
// +---------------------------------+---------------------------------+---------------------------------+
// | Header 1 | Header 2 | Header 3 |
// +---------------------------------+---------------------------------+---------------------------------+
// | 1234567890123456789001234567890 | 1234567890123456789001234567890 | abc |
// | 456 | abc | 1234567890123456789001234567890 |
// +---------------------------------+---------------------------------+---------------------------------+
//
// Section B
// +---------------------------------+---------------------------------+---------------------------------+
// | Header 1 | Header 2 | Header 3 |
// +---------------------------------+---------------------------------+---------------------------------+
// | 1234567890123456789001234567890 | 1234567890123456789001234567890 | abc |
// | 456 | abc | 1234567890123456789001234567890 |
// +---------------------------------+---------------------------------+---------------------------------+
static
void beginTableLineToTextOutputStream( StructuredTextToOutputStreamPrinter* structTxtToOutStreamPrinter )
{
streamStringView( structTxtToOutStreamPrinter->prefix, structTxtToOutStreamPrinter->txtOutStream );
streamIndent( /* nestingDepth */ 1, structTxtToOutStreamPrinter->txtOutStream );
}
void printTableHorizontalBorder( size_t numberOfColumns, StructuredTextToOutputStreamPrinter* structTxtToOutStreamPrinter )
{
if ( numberOfColumns == 0 ) return;
beginTableLineToTextOutputStream( structTxtToOutStreamPrinter );
streamChar( '+', structTxtToOutStreamPrinter->txtOutStream );
ELASTIC_APM_REPEAT_N_TIMES( numberOfColumns )
{
ELASTIC_APM_REPEAT_N_TIMES( ELASTIC_APM_MIN_TABLE_CELL_MIN_WIDTH + 2 )
{
streamChar( '-', structTxtToOutStreamPrinter->txtOutStream );
}
streamChar( '+', structTxtToOutStreamPrinter->txtOutStream );
}
streamPrintf( structTxtToOutStreamPrinter->txtOutStream, "\n" );
}
void printTableBeginToTextOutputStream( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns )
{
printTableHorizontalBorder( numberOfColumns, (StructuredTextToOutputStreamPrinter*)structTxtPrinter );
}
static
void printTableRowToTextOutputStream( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns, String columns[] )
{
if ( numberOfColumns == 0 ) return;
StructuredTextToOutputStreamPrinter* structTxtToOutStreamPrinter = (StructuredTextToOutputStreamPrinter*)structTxtPrinter;
beginTableLineToTextOutputStream( structTxtToOutStreamPrinter );
streamString( "|", structTxtToOutStreamPrinter->txtOutStream );
ELASTIC_APM_FOR_EACH_INDEX( i, numberOfColumns )
{
streamString( " ", structTxtToOutStreamPrinter->txtOutStream );
streamPrintf( structTxtToOutStreamPrinter->txtOutStream
, "%-" ELASTIC_APM_PP_STRINGIZE( ELASTIC_APM_MIN_TABLE_CELL_MIN_WIDTH ) "s"
, columns[ i ] );
streamString( " |", structTxtToOutStreamPrinter->txtOutStream );
}
streamPrintf( structTxtToOutStreamPrinter->txtOutStream, "\n" );
}
static
void printTableHeaderToTextOutputStream( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns, String columnHeaders[] )
{
if ( numberOfColumns == 0 ) return;
StructuredTextToOutputStreamPrinter* structTxtToOutStreamPrinter = (StructuredTextToOutputStreamPrinter*)structTxtPrinter;
printTableRowToTextOutputStream( structTxtPrinter, numberOfColumns, columnHeaders );
printTableHorizontalBorder( numberOfColumns, structTxtToOutStreamPrinter );
}
void printTableEndToTextOutputStream( StructuredTextPrinter* structTxtPrinter, size_t numberOfColumns )
{
printTableHorizontalBorder( numberOfColumns, (StructuredTextToOutputStreamPrinter*)structTxtPrinter );
}
#undef ELASTIC_APM_MIN_TABLE_CELL_MIN_WIDTH
void initStructuredTextToOutputStreamPrinter(
/* in */ TextOutputStream* txtOutStream
, StringView prefix
, /* out */ StructuredTextToOutputStreamPrinter* structTxtToOutStreamPrinter
)
{
structTxtToOutStreamPrinter->base.printSectionHeading = printSectionHeadingToTextOutputStream;
structTxtToOutStreamPrinter->base.printTableBegin = printTableBeginToTextOutputStream;
structTxtToOutStreamPrinter->base.printTableHeader = printTableHeaderToTextOutputStream;
structTxtToOutStreamPrinter->base.printTableRow = printTableRowToTextOutputStream;
structTxtToOutStreamPrinter->base.printTableEnd = printTableEndToTextOutputStream;
structTxtToOutStreamPrinter->txtOutStream = txtOutStream;
structTxtToOutStreamPrinter->prefix = prefix;
}