Skip to content

Commit

Permalink
Fixed calling post hook if instrumented functions throws (#459) (#1223)
Browse files Browse the repository at this point in the history
  • Loading branch information
intuibase committed Sep 5, 2024
1 parent ecdd641 commit 79887bd
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 6 deletions.
11 changes: 7 additions & 4 deletions agent/native/ext/tracer_PHP_part.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
* under the License.
*/

#include "tracer_PHP_part.h"
#include "log.h"
#include "ConfigSnapshot.h"
#include "Exceptions.h"
#include "Tracer.h"
#include "util_for_PHP.h"
#include "basic_macros.h"
#include "elastic_apm_API.h"
#include "ConfigSnapshot.h"
#include "log.h"
#include "util_for_PHP.h"

#define ELASTIC_APM_CURRENT_LOG_CATEGORY ELASTIC_APM_LOG_CATEGORY_C_TO_PHP

Expand Down Expand Up @@ -267,6 +267,8 @@ void tracerPhpPartInternalFuncCallPostHook( uint32_t dbgInterceptRegistrationId,
ResultCode resultCode;
zval phpPartArgs[ 2 ];

elasticapm::php::AutomaticExceptionStateRestorer restorer;

if (!canInvokeTracerPhpPart()) {
if (switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ )) {
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
Expand All @@ -275,6 +277,7 @@ void tracerPhpPartInternalFuncCallPostHook( uint32_t dbgInterceptRegistrationId,
}
}


// The first argument to PHP part's interceptedCallPostHook() is $hasExitedByException (bool)
ZVAL_FALSE( &( phpPartArgs[ 0 ] ) );

Expand Down
3 changes: 1 addition & 2 deletions agent/native/ext/util_for_PHP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,7 @@ ResultCode callPhpFunction( StringView phpFunctionName, uint32_t argsCount, zval
, args );
if ( callUserFunctionRetVal != SUCCESS )
{
ELASTIC_APM_LOG_ERROR( "call_user_function failed. Return value: %d. PHP function name: `%.*s'. argsCount: %u."
, callUserFunctionRetVal, (int) phpFunctionName.length, phpFunctionName.begin, argsCount );
ELASTIC_APM_LOG_ERROR( "call_user_function failed. Return value: %d. PHP function name: `%.*s'. argsCount: %u. Exception: %p", callUserFunctionRetVal, (int) phpFunctionName.length, phpFunctionName.begin, argsCount, EG( exception ) );
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
}

Expand Down
56 changes: 56 additions & 0 deletions agent/native/libphpbridge/code/Exceptions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or 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 "Exceptions.h"
#include <Zend/zend_exceptions.h>

namespace elasticapm::php
{

SavedException saveExceptionState()
{
SavedException savedException;
savedException.exception = EG( exception );
savedException.prev_exception = EG( prev_exception );
savedException.opline_before_exception = EG( opline_before_exception );

EG( exception ) = nullptr;
EG( prev_exception ) = nullptr;
EG( opline_before_exception ) = nullptr;

if ( EG( current_execute_data ) )
{
savedException.opline = EG( current_execute_data )->opline;
}
return savedException;
}

void restoreExceptionState( SavedException savedException )
{
EG( exception ) = savedException.exception;
EG( prev_exception ) = savedException.prev_exception;
EG( opline_before_exception ) = savedException.opline_before_exception;

if ( EG( current_execute_data ) && savedException.opline.has_value() )
{
EG( current_execute_data )->opline = savedException.opline.value();
}
}

}// namespace elasticapm::php
61 changes: 61 additions & 0 deletions agent/native/libphpbridge/code/Exceptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or 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.
*/

#pragma once

#include <Zend/zend_compile.h>
#include <Zend/zend_globals.h>
#include <Zend/zend_types.h>
#include <optional>

namespace elasticapm::php
{

struct SavedException {
zend_object* exception = nullptr;
zend_object* prev_exception = nullptr;
const zend_op* opline_before_exception = nullptr;
std::optional< const zend_op* > opline;
};

SavedException saveExceptionState();
void restoreExceptionState( SavedException savedException );


class AutomaticExceptionStateRestorer
{
public:
AutomaticExceptionStateRestorer()
: savedException( saveExceptionState() )
{
}
~AutomaticExceptionStateRestorer()
{
restoreExceptionState( savedException );
}
auto getException()
{
return savedException.exception;
}

private:
SavedException savedException;
};

}// namespace elasticapm::php

0 comments on commit 79887bd

Please sign in to comment.