diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a1e1f66 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +sudo: required + +services: + - docker + +compiler: + - gcc + +group: edge + +before_install: + - docker pull absalomedia/hhvm-newrelic-ext + - docker ps -a + - docker info + +script: + - docker build -t absalomedia/hhvm-newrelic-ext . + +notifications: + on_success: never + on_failure: never + +os: + - linux diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b06c452 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM absalomedia/hhvm-dev + +MAINTAINER Lawrence Meckan + +RUN apt-get update \ + && apt-get -y install wget curl unzip git \ + && apt-get -y upgrade \ + && apt-get -y autoremove \ + && apt-get -y clean \ + && rm -rf /tmp/* /var/tmp/* + +RUN mkdir -p /usr/src/newrelic && \ + cd /usr/src/newrelic +RUN echo "Downloading New Relic SDK ..." && wget https://download.newrelic.com/agent_sdk/nr_agent_sdk-v0.16.1.0-beta.x86_64.tar.gz +RUN tar -xzf nr_agent_sdk-*.tar.gz +RUN cp nr_agent_sdk-*/include/* /usr/local/include/ && \ + cp nr_agent_sdk-*/lib/* /usr/local/lib/ +RUN export CXX="g++-4.9" + +WORKDIR /usr/src + +RUN git clone https://github.com/absalomedia/hhvm-newrelic-ext.git && \ + cd hhvm-newrelic-ext && \ + hphpize && \ + cmake . && \ + make && \ + make install \ No newline at end of file diff --git a/README.md b/README.md index 45b32b9..ab409b8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # (Unofficial) New Relic extension for HHVM +[![Build Status](https://travis-ci.org/absalomedia/hhvm-newrelic-ext.svg?branch=master)](https://travis-ci.org/absalomedia/hhvm-newrelic-ext) + This is an unofficial New Relic extension for [HHVM](http://hhvm.com/) which implements the [New Relic PHP agent API](https://docs.newrelic.com/docs/agents/php-agent/configuration/php-agent-api) as much as possible. It's build on the [New Relic SDK agent](https://docs.newrelic.com/docs/agents/agent-sdk/using-agent-sdk/getting-started-agent-sdk), which doesn't support all the features needed for rebuilding the PHP agent API and has some other caveats @@ -61,6 +63,8 @@ For compiling the extension, you need the dev package as well, eg. on Ubuntu/Deb sudo apt-get install hhvm-dev ``` +There are known issues on Ubuntu 12.04 and Debian Wheezy + ### Compile the extension ``` @@ -94,7 +98,7 @@ When you use the PHP agent of New Relic, you're certainly used to have function * You have to explicitely enable it * It slows down your request by factor 2 or more -* It can only show up to 2'000 function calls +* It can only show up to 2000 function calls * If you use an unpatched HHVM 3.5 or less (fixed in HHVM 3.6), it's even slower ### Enabling Function Level Profiling diff --git a/ext_newrelic.php b/ext_newrelic.php index 5db2bb8..6c8779c 100644 --- a/ext_newrelic.php +++ b/ext_newrelic.php @@ -124,8 +124,16 @@ static function endAll(): void { } static function errorCallback($type, $message, $c) { - $errno = $errno & error_reporting(); - if($errno == 0) return false; + switch($type) + { + case E_WARNING: // 2 // + case E_NOTICE: // 8 // + case E_USER_NOTICE: // 1024 // + case E_STRICT: // 2048 // + case E_DEPRECATED: // 8192 // + case E_USER_DEPRECATED: // 16384 // + return false; + } $exception_type = self::friendlyErrorType($type); $error_message = $message; @@ -321,6 +329,17 @@ function newrelic_pdo_intercept() { }); } +function newrelic_db_start($query) { + $a = _newrelic_parse_query($query); + return newrelic_segment_datastore_begin($a[1], $a[0], $query); +} + +function newrelic_db_end($newrelic_segment) { + if (isset($newrelic_segment) && $newrelic_segment) { + newrelic_segment_end($newrelic_segment); + } +} + // mysqli function newrelic_mysqli_intercept() { fb_intercept('mysqli::hh_real_query', function ($name, $obj, $args, $data, &$done) { @@ -365,7 +384,7 @@ function newrelic_file_get_contents_intercept() { // fread and fwrite (e.g. Redis) function newrelic_fread(resource $handle, int $length) { - if (stream_get_meta_data($handle)['wrapper_type'] != 'plainfile') { + if (empty(stream_get_meta_data($handle)['uri']) && stream_get_meta_data($handle)['wrapper_type'] != 'plainfile') { $seg = newrelic_segment_external_begin('sock_read[' . stream_socket_get_name($handle,true) . ']', 'fread'); } else { $seg = newrelic_segment_external_begin('file', 'fread'); @@ -376,7 +395,7 @@ function newrelic_fread(resource $handle, int $length) { } function newrelic_fwrite( resource $handle, string $string, int $length = -1 ) { - if (stream_get_meta_data($handle)['wrapper_type'] != 'plainfile') { + if (empty(stream_get_meta_data($handle)['uri']) && stream_get_meta_data($handle)['wrapper_type'] != 'plainfile') { $seg = newrelic_segment_external_begin('sock_write[' . stream_socket_get_name($handle,true) . ']', 'fwrite'); } else { $seg = newrelic_segment_external_begin('file', 'fwrite'); @@ -411,7 +430,7 @@ function newrelic_curl_intercept() { // socket_read and socket_write (e.g. MongoDB (mongofill)) function newrelic_socket_read(resource $socket, int $length, int $type = PHP_BINARY_READ) { - if (stream_get_meta_data($socket)['wrapper_type'] != 'plainfile') { + if (empty(stream_get_meta_data($handle)['uri']) && stream_get_meta_data($socket)['wrapper_type'] != 'plainfile') { $seg = newrelic_segment_external_begin('sock_read[' . stream_socket_get_name($socket,true) . ']', 'socket_read'); } else { $seg = newrelic_segment_external_begin('file', 'socket_read'); @@ -422,7 +441,7 @@ function newrelic_socket_read(resource $socket, int $length, int $type = PHP_BIN } function newrelic_socket_write( resource $socket, string $string, int $length = 0 ) { - if (stream_get_meta_data($socket)['wrapper_type'] != 'plainfile') { + if (empty(stream_get_meta_data($handle)['uri']) && stream_get_meta_data($socket)['wrapper_type'] != 'plainfile') { $seg = newrelic_segment_external_begin('sock_write[' . stream_socket_get_name($socket,true) . ']', 'socket_write'); } else { $seg = newrelic_segment_external_begin('file', 'socket_write'); @@ -433,7 +452,7 @@ function newrelic_socket_write( resource $socket, string $string, int $length = } function newrelic_socket_recv( resource $socket , &$buf , int $len , int $flags ) { - if (stream_get_meta_data($socket)['wrapper_type'] != 'plainfile') { + if (empty(stream_get_meta_data($handle)['uri']) && stream_get_meta_data($socket)['wrapper_type'] != 'plainfile') { $seg = newrelic_segment_external_begin('sock_read[' . stream_socket_get_name($socket,true) . ']', 'socket_read'); } else { $seg = newrelic_segment_external_begin('file', 'socket_read'); diff --git a/newrelic.cpp b/newrelic.cpp index aebf60a..ce577b0 100644 --- a/newrelic.cpp +++ b/newrelic.cpp @@ -164,34 +164,39 @@ static void HHVM_FUNCTION(newrelic_set_external_profiler, int64_t maxdepth ) { } static Variant HHVM_FUNCTION(newrelic_get_scoped_generic_segment, const String & name) { - ScopedGenericSegment * segment = nullptr; - // NEWOBJ existsonly until HHVM 3.4 - #ifdef NEWOBJ - segment = NEWOBJ(ScopedGenericSegment)(name.c_str()); - #else - segment = newres(name.c_str()); + #if defined NEWOBJ + auto segment = NEWOBJ(ScopedGenericSegment)(name.c_str()); + // newres exists only until HHVM 3.10.0 + #elif defined newres + auto segment = newres(name.c_str()); + #else + auto segment = req::make(name.c_str()); #endif return Resource(segment); } static Variant HHVM_FUNCTION(newrelic_get_scoped_database_segment, const String & table, const String & operation, const String & sql, const String & sql_trace_rollup_name) { - ScopedDatastoreSegment * segment = nullptr; // NEWOBJ existsonly until HHVM 3.4 #ifdef NEWOBJ - segment = NEWOBJ(ScopedDatastoreSegment)(table.c_str(), operation.c_str(), sql.c_str(), sql_trace_rollup_name.c_str()); + auto segment = NEWOBJ(ScopedDatastoreSegment)(table.c_str(), operation.c_str(), sql.c_str(), sql_trace_rollup_name.c_str()); + // newres exists only until HHVM 3.10 + #elif defined newres + auto segment = newres(table.c_str(), operation.c_str(), sql.c_str(), sql_trace_rollup_name.c_str()); #else - segment = newres(table.c_str(), operation.c_str(), sql.c_str(), sql_trace_rollup_name.c_str()); + auto segment = req::make(table.c_str(), operation.c_str(), sql.c_str(), sql_trace_rollup_name.c_str()); #endif return Resource(segment); } static Variant HHVM_FUNCTION(newrelic_get_scoped_external_segment, const String & host, const String & name) { - ScopedExternalSegment * segment = nullptr; // NEWOBJ existsonly until HHVM 3.4 #ifdef NEWOBJ - segment = NEWOBJ(ScopedExternalSegment)(host.c_str(), name.c_str()); + auto segment = NEWOBJ(ScopedExternalSegment)(host.c_str(), name.c_str()); + // newres exists only until HHVM 3.10 + #elif defined newres + auto segment = newres(host.c_str(), name.c_str()); #else - segment = newres(host.c_str(), name.c_str()); + auto segment = req::make(host.c_str(), name.c_str()); #endif return Resource(segment); } @@ -217,6 +222,7 @@ static class NewRelicExtension : public Extension { app_name = RuntimeOption::EnvVariables["NEWRELIC_APP_NAME"]; app_language = RuntimeOption::EnvVariables["NEWRELIC_APP_LANGUAGE"]; app_language_version = RuntimeOption::EnvVariables["NEWRELIC_APP_LANGUAGE_VERSION"]; + log_properties_file = RuntimeOption::EnvVariables["NEWRELIC_LOG_PROPERTIES_FILE"]; if (app_language.empty()) { app_language = "php-hhvm"; @@ -231,6 +237,7 @@ static class NewRelicExtension : public Extension { setenv("NEWRELIC_APP_NAME", app_name.c_str(), 1); setenv("NEWRELIC_APP_LANGUAGE", app_language.c_str(), 1); setenv("NEWRELIC_APP_LANGUAGE_VERSION", app_language_version.c_str(), 1); + setenv("NEWRELIC_LOG_PROPERTIES_FILE", log_properties_file.c_str(), 1); if (!license_key.empty() && !app_name.empty() && !app_language.empty() && !app_language_version.empty()) config_loaded = true; @@ -335,6 +342,7 @@ static class NewRelicExtension : public Extension { std::string app_name; std::string app_language; std::string app_language_version; + std::string log_properties_file; bool config_loaded; } s_newrelic_extension; diff --git a/newrelic_profiler.h b/newrelic_profiler.h index 9290fc8..ae4222f 100644 --- a/newrelic_profiler.h +++ b/newrelic_profiler.h @@ -44,6 +44,9 @@ namespace HPHP { //virtual Frame *allocateFrame() override; private: + void vscan(IMarker& mark) const override { + } + class NewRelicProfilerFrame : public Frame { public: virtual ~NewRelicProfilerFrame() {