package.xml0000664000175000017500000053652015011062311012462 0ustar derickderick xdebug pecl.php.net Xdebug is a debugging and productivity extension for PHP Xdebug and provides a range of features to improve the PHP development experience. Step Debugging A way to step through your code in your IDE or editor while the script is executing. Improvements to PHP's error reporting An improved var_dump() function, stack traces for Notices, Warnings, Errors and Exceptions to highlight the code path to the error Tracing Writes every function call, with arguments and invocation location to disk. Optionally also includes every variable assignment and return value for each function. Profiling Allows you, with the help of visualisation tools, to analyse the performance of your PHP application and find bottlenecks. Code Coverage Analysis To show which parts of your code base are executed when running unit tests with PHP Unit. Derick Rethans derick derick@xdebug.org yes 2025-05-14 3.4.3 3.4.3 stable stable Xdebug-1.03 Wed, May 14, 2025 - Xdebug 3.4.3 = Fixed bugs: - Fixed issue #2322: Xdebug tries to open debugging connection in destructors during shutdown - Fixed issue #2325: Referred chrome browser extension is no longer working - Fixed issue #2326: Step debugger finishes if property debugging handler in PHP throws an exception - Fixed issue #2331: Segmentation fault with 'invalid' variable names - Fixed issue #2339: Trying to throw an exception can cause a zend_mm_heap corrupted error under specific circumstances - Fixed issue #2340: Xdebug case sensitivity issues on some files introduced since 3.3.0 - Fixed issue #2343: Fatal error on virtual property hook step debugging - Fixed issue #2348: Xdebug does not resolve breakpoints in property hooks 8.0.0 8.4.99 1.9.1 xdebug 2025-03-09 3.4.2 3.4.2 stable stable Xdebug-1.03 Sun, Mar 09, 2025 - Xdebug 3.4.2 = Fixed bugs: - Fixed issue #2313: var_dump does not output some Russian characters - Fixed issue #2314: Class properties with hooks are always shown as null - Fixed issue #2315: xdebug_dump_superglobals() leaks memory - Fixed issue #2317: Code coverage leaks memory - Fixed issue #2321: Segfault when null is assigned to a superglobal - Fixed issue #2323: xdebug_notify() does not respect xdebug.var_display_max_* Settings - Fixed issue #2327: Segmentation Fault 139 if exception thrown in callback since PHP 8.4 2025-01-06 3.4.1 3.4.1 stable stable Xdebug-1.03 Mon, Jan 06, 2025 - Xdebug 3.4.1 = Fixed bugs: - Fixed issue #2306: Segmentation fault on each HTTP request when not listening to debugging connections - Fixed issue #2307: Segmentation fault due to a superglobal being a reference while checking for triggers - Fixed issue #2309: Installation on Windows with PHP PIE failing - Fixed issue #2310: xdebug 3.4.0 crashes php8.1-fpm after script execution 2024-11-28 3.4.0 3.4.0 stable stable Xdebug-1.03 Thu, Nov 28, 2024 - Xdebug 3.4.0 + New features: - Fixed issue #2239: Add 'XDEBUG_IGNORE' GET/POST/COOKIE/ENV to make the step debugger ignore that specific request - Fixed issue #2281: PHP 8.4 support + Improvements - Fixed issue #2261: Send control socket location in init packet = Fixed bugs: - Fixed issue #2262: PHP 8.4: Closure names need different wrapping algorithm - Fixed issue #2283: SoapClient usage causes segfault with codecoverage - Fixed issue #2294: Nette Tester always crashes in all test jobs when running with XDebug 3.4.0beta1 active - Fixed issue #2304: Seg fault on throw exception - Fixed issue #2305: Segfault when checking whether to ignore creating a debug connection during shutdown functions 2024-10-04 3.4.0beta1 3.4.0beta1 beta beta Xdebug-1.03 Fri, Oct 04, 2024 - Xdebug 3.4.0beta1 = Fixed bugs: - Fixed issue #2261: Send control socket location in init packet - Fixed issue #2281: PHP 8.4 support 2024-05-31 3.4.0alpha1 3.4.0alpha1 beta beta Xdebug-1.03 Fri, May 31, 2024 - Xdebug 3.4.0alpha1 = Fixed bugs: - Fixed issue #2239: Add 'XDEBUG_IGNORE' GET/POST/COOKIE/ENV to make the step debugger ignore that specific request - Fixed issue #2262: PHP 8.4: Closure names need different wrapping algorithm 2024-04-15 3.3.2 3.3.2 stable stable Xdebug-1.03 Mon, Apr 15, 2024 - Xdebug 3.3.2 = Fixed bugs: - Fixed issue #2216: With PHP8.3 and Apache 2.4.58 error_reporting() causing Apache process to hang - Fixed issue #2230: Crash when xdebug and blackfire extensions are active - Fixed issue #2233: High and continuous Apache server CPU use 2023-12-14 3.3.1 3.3.1 stable stable Xdebug-1.03 Thu, Dec 14, 2023 - Xdebug 3.3.1 = Fixed bugs: - Fixed issue #2220: Test failure - Fixed issue #2221: Crash when other extensions run PHP code without the stack being initialised yet - Fixed issue #2223: Xdebug's constants are not available with `xdebug.mode=off` - Fixed issue #2226: xdebug_get_function_stack(['from_exception']) does not always find stored trace - Fixed issue #2227: Crash with return value and observers - Fixed issue #2228: Return value can not be fetched with property_get if top frame is an internal function 2023-11-30 3.3.0 3.3.0 stable stable Xdebug-1.03 Thu, Nov 30, 2023 - Xdebug 3.3.0 + New features: - Fixed issue #2171: Support for PHP 8.3 - Fixed issue #1732: Add support for flame graph outputs - Fixed issue #2219: Add control socket on Linux to obtain information and initiate debugger or breakpoint - Fixed issue #1562: Add 'local_vars' option to 'xdebug_get_function_stack' to include variables for each st - Fixed issue #2194: Add 'params_as_values' option to 'xdebug_get_function_stack' to return data as values - Fixed issue #2195: Add 'from_exception' option to 'xdebug_get_function_stack' to return the stack trace where an exception was thrown + Improvements: - Fixed issue #2077: Bring back xdebug.collect_params - Fixed issue #2170: Show contents of Spl's ArrayIterator - Fixed issue #2172: Show contents of SplDoublyLinkedList and SplPriorityQueue - Fixed issue #2183: Bubble up exception message when using code evalution through protocol - Fixed issue #2188: Step over with fibers does still step into fiber routines - Fixed issue #2197: Add time index and memory to output of xdebug_get_function_stack - Fixed issue #2203: Increase default max nesting time out from 256 to 512 - Fixed issue #2206: Optimise debugger breakpoints checking - Fixed issue #2207: Add filenames for include and friends to flamegraph output - Fixed issue #2217: xdebug://gateway pseudo host does not support IPv6 = Fixed bugs: - Fixed issue #450: "Incomplete" backtraces when an exception gets rethrown - Fixed issue #476: Exception chaining does not work properly - Fixed issue #1155: Local variables are not shown when execution break in error_handler - Fixed issue #2000: Debugger evaluate expression: "can't evaluate expression" - Fixed issue #2027: Branch/path code coverage for traits drops trait name since 3.1.0 - Fixed issue #2132: Errors when mountinfo does not have enough information for finding systemd private tmp directory - Fixed issue #2200: PECL package file has wrong max PHP version number, and peclweb refuses the package - Fixed issue #2208: Superfluous `...` (three omission dots) in var_dump() - Fixed issue #2210: Flamegraphs crash when using `start_with_request` - Fixed issue #2211: File wrappers get wrong filename location in stack. - Fixed issue #2214: Array keys aren't escaped in traces 2023-10-19 3.3.0alpha3 3.3.0alpha3 beta beta Xdebug-1.03 Thu, Oct 19, 2023 - Xdebug 3.3.0alpha3 = Fixed bugs: - Fixed issue #1732: Add support for flame graph outputs - Fixed issue #2000: Debugger evaluate expression: "can't evaluate expression" - Fixed issue #2077: Bring back xdebug.collect_params - Fixed issue #2203: Increase default max nesting time out from 256 to 512 - Fixed issue #2206: Optimise debugger breakpoints checking 2023-09-06 3.3.0alpha2 3.3.0alpha2 beta beta Xdebug-1.03 Wed, Sep 06, 2023 - Xdebug 3.3.0alpha2 = Fixed bugs: - Fixed issue #2200: PECL package file has wrong max PHP version number, and peclweb refuses the package 2023-09-06 3.3.0alpha1 3.3.0alpha1 beta beta Xdebug-1.03 Wed, Sep 06, 2023 - Xdebug 3.3.0alpha1 + New features: - Fixed issue #2171: Support for PHP 8.3 + Improvements: - Fixed issue #1562: Add 'local_vars' option to 'xdebug_get_function_stack' to include variables for each st - Fixed issue #2170: Show contents of Spl's ArrayIterator while debugging - Fixed issue #2172: Show contents of SplDoublyLinkedList and SplPriorityQueue while debugging - Fixed issue #2183: Bubble up exception message when using code evalution through protocol - Fixed issue #2188: Step over with fibers does still step into fiber routines - Fixed issue #2194: Add 'params_as_values' option to 'xdebug_get_function_stack' to return data as values - Fixed issue #2195: Add 'from_exception' option to 'xdebug_get_function_stack' to return the stack trace where an exception was thrown - Fixed issue #2197: Add time index and memory to output of xdebug_get_function_stack = Fixed bugs: - Fixed issue #450: "Incomplete" backtraces when an exception gets rethrown - Fixed issue #476: Exception chaining does not work properly - Fixed issue #2132: Errors when mountinfo does not have enough information for finding systemd private tmp directory 2023-07-14 3.2.2 3.2.2 stable stable Xdebug-1.03 Fri, Jul 14, 2023 - Xdebug 3.2.2 = Fixed bugs: - Fixed issue #2175: Crash with EXC_BAD_ACCESS in xdebug_str_create - Fixed issue #2180: Crash on extended SplFixedArray - Fixed issue #2182: Segfault with ArrayObject on stack - Fixed issue #2186: Segfault with trampoline functions and debugger activation 2023-03-21 3.2.1 3.2.1 stable stable Xdebug-1.03 Tue, Mar 21, 2023 - Xdebug 3.2.1 = Fixed bugs: - Fixed issue #2144: Xdebug 3.2.0 ignores xdebug.mode and enables all features - Fixed issue #2145: Xdebug 3.2.0 crash PHP on Windows if xdebug.mode = off - Fixed issue #2146: apache2 segfaulting with version 3.2.0 on PHP 8.0 - Fixed issue #2148: Icon for link to docs in xdebug_info() HTML output does not always render correctly 2022-12-08 3.2.0 3.2.0 stable stable BSD style Thu, Dec 08, 2022 - Xdebug 3.2.0 + New features: - Fixed issue #1819: Allow a list of headers in 'xdebug.client_discovery_header' - Fixed issue #2079: Add pseudo hosts xdebug://gateway and xdebug://nameserver - Fixed issue #2086: Include return value in return breakpoint interruption response - Fixed issue #2087: Introduce step for the return state and virtual property for return value + Improvements: - Fixed issue #2062: Xdebug now records whether systemd's PrivateTmp is used in its diagnostics information - Fixed issue #2104: Add support for PHP 8.2 "SensitiveParameter" attribute - Fixed issue #2117: Removed emulated properties for closures, as PHP 8.2 adds debug information for them - Fixed issue #2122: Local variables are now available when using start_upon_error - Fixed issue #2123: Add warning in log and diagnositics information when a breakpoint is set on a non-existing file - Fixed issue #2138: Step debugger now disconnects and continues running the script, when the debugging client closes the connection - Fixed issue #2136: Duplicate line/conditional breakpoints are now rejected - Deprecations: - Fixed issue #2014: Drop support for PHP 7.2 - Fixed issue #2102: Drop support for PHP 7.3 - Fixed issue #2103: Drop support for PHP 7.4 = Fixed bugs: - Fixed issue #2002: xdebug_trace_handler_t handler members are not always checked for NULL when executing - Fixed issue #2045: Inapproriate frowny face - Fixed issue #2089: Alpine Linux does not support res_ninit - Fixed issue #2093: Fatal error: linux/rtnetlink.h: No such file or directory linux/rtnetlink.h - Fixed issue #2098: With breakpoint_include_return_value enabled step_out break at every function - Fixed issue #2105: 3.2.0alpha1 package misses the php-header.h file - Fixed issue #2108: Segfault on PHP8.1 with PHPUnit 10 when path coverage is enabled - Fixed issue #2113: Crash at step_into after thrown exception with return value debugging en - Fixed issue #2121: Xdebug does not use local independent float-to-string functions - Fixed issue #2124: Xdebug incorrectly reports that there are no children for static closure properties, even though there are - Fixed issue #2125: Crash with PHP 8.2 on 32-bit due to change in "not set" value with CATCH opcode - Fixed issue #2126: Problems with retrieving global variables - Fixed issue #2127: Tracing does not handle NUL char in anonymous closure scope - Fixed issue #2133: Warning with regards to extra NUL character in xdebug_setcookie call - Fixed issue #2134: Xdebug stops at the line where the exception is created, not where it is thrown - Fixed issue #2135: Xdebug stops twice at the same line after a call breakpoint or xdebug_break() 2022-11-10 3.2.0RC2 3.2.0RC2 beta beta BSD style Thu, Nov 10, 2022 - Xdebug 3.2.0RC2 = Fixed bugs: - Fixed issue #2100: "Fatal error: debuginfo() must return an array" when Exception is thrown from debugInfo in PHP 8.x - Fixed issue #2101: When a temporary breakpoint is hit, breakpoint_list should show it as disabled - Fixed issue #2126: Problems with retrieving global variables - Fixed issue #2127: Tracing does not handle NUL char in anonymous closure scope - Fixed issue #2129: Cannot read snapshot Gzip-compressed data is corrupt - Fixed issue #2133: Warning with regards to extra NUL character in xdebug_setcookie call - Fixed issue #2134: Xdebug stops at the line where the exception is created, not where it is thrown - Fixed issue #2135: Xdebug stops twice at the same line after a call breakpoint or xdebug_break() - Fixed issue #2136: Duplicate line/conditional breakpoints are not rejected 2022-10-10 3.2.0RC1 3.2.0RC1 beta beta BSD style Mon, Oct 10, 2022 - Xdebug 3.2.0RC1 = Fixed bugs: - Fixed issue #2113: Crash at step_into after thrown exception with return value debugging en - Fixed issue #2117: Removed emulated properties for closures, as PHP 8.2 adds debug information for them - Fixed issue #2121: Xdebug does not use local independent float-to-string functions - Fixed issue #2122: Local variables are not available when using start_upon_error - Fixed issue #2123: Add warning in log and diagnositics information when a breakpoint is set on a non-existing file - Fixed issue #2124: Xdebug incorrectly reports that there are no children for static closure properties, even thought there are - Fixed issue #2125: Crash with PHP 8.2 on 32-bit due to change in "not set" value with CATCH opcode 2022-08-24 3.2.0alpha3 3.2.0alpha3 beta beta BSD style Wed, Aug 24, 2022 - Xdebug 3.2.0alpha3 + Improvements: - Fixed issue #2112: Force 'return_value' breakpoint information and step to 'on' temporarily 2022-07-25 3.2.0alpha2 3.2.0alpha2 beta beta BSD style Mon, Jul 25, 2022 - Xdebug 3.2.0alpha2 = Fixed bugs: - Fixed issue #2105: 3.2.0alpha1 package misses the php-header.h file 2022-07-20 3.2.0alpha1 3.2.0alpha1 beta beta BSD style Wed, Jul 20, 2022 - Xdebug 3.2.0alpha1 + New features: - Fixed issue #1819: Allow a list of headers in 'xdebug.client_discovery_header' - Fixed issue #2079: Add pseudo hosts xdebug://gateway and xdebug://nameserver - Fixed issue #2087: Introduce step for the return state and virtual property for return value - Fixed issue #2104: Add support for PHP 8.2 "SensitiveParameter" attribute + Improvements: - Fixed issue #2086: Include return value in return breakpoint interruption response - Removed features: - Fixed issue #2014: Drop support for PHP 7.2 - Fixed issue #2102: Drop support for PHP 7.3 - Fixed issue #2103: Drop support for PHP 7.4 = Fixed bugs: - Fixed issue #2002: xdebug_trace_handler_t handler members are not always checked for NULL when executing - Fixed issue #2045: Inapproriate frowny face - Fixed issue #2062: Profiler can't able to write cachegrind file at /tmp - Fixed issue #2089: Alpine Linux does not support res_ninit - Fixed issue #2093: Fatal error: linux/rtnetlink.h: No such file or directory linux/rtnetlink.h - Fixed issue #2098: With breakpoint_include_return_value enabled step_out break at every function 2022-11-08 3.1.6 3.1.6 stable stable BSD style Tue, Nov 08, 2022 - Xdebug 3.1.6 = Fixed bugs: - Fixed issue #2100: "Fatal error: debuginfo() must return an array" when Exception is thrown from debugInfo in PHP 8.x - Fixed issue #2101: When a temporary breakpoint is hit, breakpoint_list should show it as disabled - Fixed issue #2129: Cannot read snapshot Gzip-compressed data is corrupt 2022-06-06 3.1.5 3.1.5 stable stable BSD style Mon, Jun 06, 2022 - Xdebug 3.1.5 = Fixed bugs: - Fixed issue #2056: Install documentation gives wrong arch for installation on M1 Macs - Fixed issue #2082: phpize --clean removes required clocks.m4 file - Fixed issue #2083: Constant defined with an enum case produce double "facet" attribute in context_get response - Fixed issue #2085: Crash when used with source guardian encoded files - Fixed issue #2090: Segfault in __callStatic() after FFI initialization 2022-04-04 3.1.4 3.1.4 stable stable BSD style Mon, Apr 04, 2022 - Xdebug 3.1.4 = Fixed bugs: - Fixed issue #2006: Removing second call breakpoint with same function name - Fixed issue #2060: XDebug breaks the Symfony "PhpFilesAdapter" cache adapter - Fixed issue #2061: Possible use after free with GC Stats - Fixed issue #2063: Can't inspect ArrayObject storage elements - Fixed issue #2064: Segmentation fault in symfony cache - Fixed issue #2068: Debug session can be started with "XDEBUG_SESSION_START=anything" when xdebug.trigger_value is set - Fixed issue #2069: Warn when profiler_append is used together with zlib compression - Fixed issue #2075: Code coverage misses static array assignment lines 2022-02-01 3.1.3 3.1.3 stable stable BSD style Tue, Feb 01, 2022 - Xdebug 3.1.3 = Fixed bugs: - Fixed issue #2049: evaling broken code (still) causes unhandled exception in PHP 7.4 - Fixed issue #2052: Memory leak when a trace file can't be opened because xdebug.trace_output_name is invalid - Fixed issue #2054: Slowdown when calling a function with long string parameters - Fixed issue #2055: Debugger creates XML with double facet attribute 2021-12-01 3.1.2 3.1.2 stable stable BSD style Wed, Dec 01, 2021 - Xdebug 3.1.2 = Fixed bugs: - Fixed issue #2036: Segfault on fiber switch in finally block in garbage collected fiber - Fixed issue #2037: Crash when profile file can not be created - Fixed issue #2041: __debugInfo is not used for var_dump output - Fixed issue #2046: Segault on xdebug_get_function_stack inside a Fiber 2021-10-15 3.1.1 3.1.1 stable stable BSD style Fri, Oct 15, 2021 - Xdebug 3.1.1 = Fixed bugs: - Fixed issue #2016: apache gives no output with xdebug 3.1.0b2 installed - Fixed issue #2024: Apache restarts in a loop under PHP 8.1.0 RC3 - Fixed issue #2029: incorrect and inaccurate date and time displayed in xdebug.log and trace files - Fixed issue #2030: PhpStorm step-debug not working on PHP 8.0.11 - Fixed issue #2032: Use runtime PHP version in DBGp and info pages instead of compiled-against version - Fixed issue #2034: Xdebug throws a Segmentation fault when 'set_time_limit' function is disabled - Fixed issue #2035: Xdebug block everything with localhost in XAMMP 2021-10-04 3.1.0 3.1.0 stable stable BSD style Mon, Oct 04, 2021 - Xdebug 3.1.0 = Fixed bugs: - Fixed issue #1472: Add assignments to computer readable trace format - Fixed issue #1537: Add links to documentation to various different "features" after wizard has run - Fixed issue #1738: Add xdebug_notify() function to send data through DBGp to a debugging client - Fixed issue #1853: Enable profile compression for cachegrind files - Fixed issue #1890: Add connected client and protocol features to diagnostic page - Fixed issue #1898: API for querying the currently active mode(s) - Fixed issue #1933: Allow for cloud ID to be set through the trigger - Fixed issue #1938: Branches in traits aren't marked as executed - Fixed issue #1948: Do not redirect warning and error messages to PHP's error log if an Xdebug log is active - Fixed issue #1949: private properties for internal classes can't be fetched for debugging - Fixed issue #1963: php exit code = -1073741819 when xdebug.mode = off (Windows Thread Safe Only) - Fixed issue #1969: Provide breakpoint ID / info in DBGp run command responses - Fixed issue #1970: xdebug_get_function_stack with unnamed (internal) parameters have wrong index - Fixed issue #1972: Add support for PHP 8.1 Fibers - Fixed issue #1974: Add gzip support to trace files - Fixed issue #1976: Switch debug session cookie to Lax, and remove expiry time - Fixed issue #1978: Xdebug's log messages are cut off at 512 bytes - Fixed issue #1980: PHP 8.1: Mark enum classes as "enum" - Fixed issue #1986: Add support for multiple trigger values - Fixed issue #1989: Profiling does not output correct class when parent keyword is used - Fixed issue #1992: Code Coverage with filter produces Segmentation fault on xdebug_stop_code_coverage() - Fixed issue #1993: eval-ing broken code causes stepping to break - Fixed issue #1996: Add support for Closure visualisation in traces, debugging, and Xdebug's var_dump - Fixed issue #1997: Added xdebug_connect_to_client() to attempt a debugging connect while running code - Fixed issue #1998: Double facet attribute generated for enums that are stored in properties - Fixed issue #1999: Add "readonly" facet to PHP 8.1 readonly properties - Fixed issue #2001: Add 'xdebug.use_compression' setting to turn on/off compression for profiling files - Fixed issue #2004: Figure out what "XDEBUG_SHOW_FNAME_TODO" define is for - Fixed issue #2007: xdebug 3.x fails to build on OS X 10.11 or earlier due to clock_gettime_nsec_np requirement - Fixed issue #2008: Using the XDEBUG_SESSION cookie could bypass shared-secret checks - Fixed issue #2009: xdebug_stop_code_coverage's argument has type mismatch - Fixed issue #2011: Closures as protected properties have double facet XML attribute - Fixed issue #2013: Support PHP 8.1 - Fixed issue #2018: zlib compression support on Windows - Fixed issue #2019: Xdebug crash because of uninitialized memory - Fixed issue #2020: segfault if xdebug.dump.GET=* and integer key without value in URL - Fixed issue #2021: Segmentation fault due to NULL bytes in internal anonymous class names - Fixed issue #2025: Anonymous classes which extend are not detected as anonymous classes since PHP 8.0 2021-09-07 3.1.0beta2 3.1.0beta2 beta beta BSD style Tue, Sep 07, 2021 - Xdebug 3.1.0beta2 = Fixed bugs: - This is a packaging fix only release. The package missed a file that were needed for building on PHP 7.2 and 8.1. 2021-09-05 3.1.0beta1 3.1.0beta1 beta beta BSD style Sun, Sep 05, 2021 - Xdebug 3.1.0beta1 + New features: - Fixed issue #1738: Add xdebug_notify() function to send data through DBGp to a debugging client - Fixed issue #1853: Enable profile compression for cachegrind files - Fixed issue #1898: API for querying the currently active mode(s) - Fixed issue #1972: Add support for PHP 8.1 Fibers - Fixed issue #1974: Add gzip support to trace files - Fixed issue #1997: Added xdebug_connect_to_client() to attempt a debugging connect while running code - Fixed issue #2001: Add 'xdebug.use_compression' setting to turn on/off compression for profiling files - Fixed issue #2013: Support PHP 8.1 + Improvements: - Fixed issue #1472: Add assignments to computer readable trace format - Fixed issue #1890: Add connected client and protocol features to diagnostic page - Fixed issue #1933: Allow for cloud ID to be set through the trigger - Fixed issue #1969: Provide breakpoint ID / info in DBGp run command responses - Fixed issue #1976: Switch debug session cookie to Lax, and remove expiry time - Fixed issue #1980: PHP 8.1: Mark enum classes as "enum" - Fixed issue #1986: Add support for multiple trigger values - Fixed issue #1996: Add support for Closure visualisation in traces, debugging, and Xdebug's var_dump - Fixed issue #1999: Add "readonly" facet to PHP 8.1 readonly properties = Fixed bugs: - Fixed issue #1938: Branches in traits aren't marked as executed - Fixed issue #1948: Do not redirect warning and error messages to PHP's error log if an Xdebug log is active - Fixed issue #1949: private properties for internal classes can't be fetched for debugging - Fixed issue #1963: php exit code = -1073741819 when xdebug.mode = off (Windows Thread Safe Only) - Fixed issue #1970: xdebug_get_function_stack with unnamed (internal) parameters have wrong index - Fixed issue #1978: Xdebug's log messages are cut off at 512 bytes - Fixed issue #1989: Profiling does not output correct class when parent keyword is used - Fixed issue #1992: Code Coverage with filter produces Segmentation fault on xdebug_stop_code_coverage() - Fixed issue #1993: eval-ing broken code causes stepping to break - Fixed issue #1998: Double facet attribute generated for enums that are stored in properties - Fixed issue #2004: Figure out what "XDEBUG_SHOW_FNAME_TODO" define is for - Fixed issue #2008: Using the XDEBUG_SESSION cookie could bypass shared-secret checks - Fixed issue #2009: xdebug_stop_code_coverage's argument has type mismatch - Fixed issue #2011: Closures as protected properties have double facet XML attribute + Documentation - Fixed issue #1537: Add links to documentation to various different "features" after wizard has run 2021-04-08 3.0.4 3.0.4 stable stable BSD style Thu, Apr 08, 2021 - Xdebug 3.0.4 = Fixed bugs: - Fixed issue #1802: Improve xdebug.org home page - Fixed issue #1944: tracing is started without trigger, when profiler is also enabled - Fixed issue #1947: xdebug_info() settings section does not show the modes that are overridden by XDEBUG_MODE - Fixed issue #1950: Assignment trace with ASSIGN_OBJ_REF crashes - Fixed issue #1954: Calling xdebug_start_trace without mode including tracing results in a fatal error 2021-02-22 3.0.3 3.0.3 stable stable BSD style Mon, Feb 22, 2021 - Xdebug 3.0.3 = Fixed bugs: - Fixed issue #1930: No local variables with trigger and xdebug_break() - Fixed issue #1931: xdebug_info() output misses configuration settings if phpinfo() has been called - Fixed issue #1932: One line in multi-line string concatenation is not covered - Fixed issue #1940: Wrong type used for showing GC Stats reports 2021-01-04 3.0.2 3.0.2 stable stable BSD style Mon, Jan 04, 2021 - Xdebug 3.0.2 = Fixed bugs: - Fixed issue #1907: Empty exception message when setting the $message property to a stringable object - Fixed issue #1910: Code coverage misses constructor property promotion code - Fixed issue #1914: Compillation failure on OpenBSD - Fixed issue #1915: Debugger should only start with XDEBUG_SESSION and not XDEBUG_PROFILE - Fixed issue #1918: Warn if PHP's Garbage Collection is disabled in gc_stats mode - Fixed issue #1919: Crash when enabling filter without the right mode active - Fixed issue #1921: Xdebug does not start step debugging if start_with_request=trigger - Fixed issue #1922: Code coverage misses array assignment lines - Fixed issue #1924: Deprecated INI settings displayed in phpinfo() - Fixed issue #1925: xdebug.start_with_request and start_upon_error display inconsistent values - Fixed issue #1926: Add Xdebug mode's source to xdebug_info() output - Fixed issue #1927: Crash when calling xdebug_stop_trace without a trace in progress - Fixed issue #1928: xdebug_stop_gcstats() can also return false 2020-12-04 3.0.1 3.0.1 stable stable BSD style Fri, Dec 4, 2020 - xdebug 3.0.1 = Fixed bugs: - Fixed issue #1893: Crash with ext-fiber and xdebug.mode=coverage - Fixed issue #1896: Segfault with closures that are not created from user code - Fixed issue #1897: Crash when removing a breakpoint - Fixed issue #1900: Update README and add run-xdebug-tests.php to package - Fixed issue #1901: Stack traces are shown (with a broken time) when Xdebug's mode includes 'debug' but not 'develop' or 'trace' - Fixed issue #1902: Compillation failure on AIX - Fixed issue #1903: Constants should always be available, regardless of which mode Xdebug is in - Fixed issue #1904: Profile and trace files using %t or %u do not get the right names - Fixed issue #1905: Debugger does not disable request timeouts 2020-11-25 3.0.0 3.0.0 stable stable BSD style Wed, Nov 25, 2020 - xdebug 3.0.0 Xdebug 3 includes major changes in functionality compared to Xdebug 2. The primary way how you turn on functionality is through the new xdebug.mode PHP configuration setting. This made it possible to massively increase performance in many of Xdebug's sub systems as Xdebug is now much more conservative in which hooks are enabled. Configuration changes, massive performance improvements, and PHP 8 support are the primary features in Xdebug 3, but there is much more. The upgrade guide lists the changes in great detail, please read it: https://xdebug.org/docs/upgrade_guide ------------- + New features: - Implemented issue #1762: Introduce feature modes - Implemented issue #1793: Add xdebug.start_upon_error setting to cover the removed xdebug.remote_mode=jit feature. - Implemented issue #1797: Implement generic logging - Implemented issue #1801: Rename mode 'display' to mode 'develop' - Implemented issue #1831: Add diagnostics function xdebug_info() - Implemented issue #1833: Add links to documentation in diagnostic log - Implemented issue #1837: Support for associative variadic variable names (PHP 8) - Implemented issue #1841: Add support for PHP 8 'match' keyword + Improvements: - Implemented issue #1680: Update var dumping routines to include relevant information for interned strings and immutable arrays - Implemented issue #1712: Add unit to profiler data types - Implemented issue #1743: Figuring out whether a call is a closure uses string comparisions instead of checking the ACC flag (Benjamin Eberlei) - Implemented issue #1752: Use a stack pool to manage stack entries instead of allocating and deallocating entries - Implemented issue #1755: Overload pcntl_fork() to prevent performance degradation by calling xdebug_get_pid often (Carlos Granados) - Implemented issue #1781: Include 'Xdebug' in max nesting level error message - Implemented issue #1783: Stacktrace needs vertical scrolling on small screens (Tobias Tom) - Implemented issue #1789: Provide PHP stubs for Xdebug's functions - Implemented issue #1807: Document Xdebug installation with yum and apt - Implemented issue #1813: Make sure that the xdebug_init_*_globals don't do more than they need to, and that init is only done when xdebug.mode != off - Implemented issue #1817: Switch filename storage from char*/size_t to zend_string* - Implemented issue #1818: Switch variable storage from char*/size_t to zend_string* - Implemented issue #1820: Increase time tracing precision (Michael Vorisek) - Implemented issue #1824: Allow Xdebug's mode to be set through an environment variable - Implemented issue #1825: Improve profiler performance by not calling fflush after every function (Michael Vorisek) - Implemented issue #1826: Reduce profiler memory allocation and call overhead - Implemented issue #1829: Switch to 10ns profiler resolution (Michael Vorisek) - Implemented issue #1832: If connect back host can not be contacted, fallback to remote_host/port - Implemented issue #1858: Only open/close log if there is an actual message to log - Implemented issue #1860: Allow xdebug.cloud_id to be set through an environment variable - Implemented issue #1814: Don't obtain the current time when it's not needed - Implemented issue #1835: Add current trace and profile file name, to diagnostic page - Implemented issue #1885: Change xdebug.start_with_ settings to PHP_INI_SYSTEM|PHP_INI_PERDIR - Implemented issue #1889: max_nesting_level should only trigger in "develop" mode - Removed features: - Implemented issue #1795: Deprecate PHP 7.1 support - Implemented issue #1786: Remove idekey value fallback to USER/USERNAME environment variable - Implemented issue #1809: Remove "overload_var_dump" setting - Implemented issue #1810: Remove collect_vars and xdebug_get_declared_vars() - Implemented issue #1812: Remove show_mem_delta setting - Implemented issue #1838: Remove collect_params setting, and always default it to "4" - Implemented issue #1847: Remove xdebug.remote_cookie_expire_time setting - Implemented issue #1016: Removed support for pause-execution (introduced in beta1) - Implemented issue #1868: Remove xdebug_disable and xdebug_enabled - Implemented issue #1883: Function xdebug_is_enabled has been removed = Changes: - Implemented issue #1378: Unfortunate coupling of default_enable=1 and remote_mode=jit - Implemented issue #1773: Replace all xdebug.*_output_dir settings with xdebug.output_dir - Implemented issue #1785: Replace xdebug.remote_mode and xdebug.auto_trace with generic "start-with-request" setting - Implemented issue #1791: Replace xdebug.*trigger*, xdebug.*trigger_value*, with xdebug.start_with_request=trigger and xdebug.trigger_value - Implemented issue #1792: Change start_with_request=always/never to start_with_request=yes/no - Implemented issue #1794: Replace the filter's blacklist/whitelist with exclude/include - Implemented issue #1811: Remove xdebug.collect_includes setting and always include them - Implemented issue #1843: Adjust XDEBUG_CONFIG checks, and document what can be set through it - Implemented issue #1844: Add deprecation warning for removed and renamed configuration setting names - Implemented issue #1845: Rename xdebug.remote_{host,port} to xdebug.client_{host,port} - Implemented issue #1846: Rename setting xdebug.remote_timeout to xdebug.connect_timeout_ms - Implemented issue #1848: Change default Xdebug port from 9000 to 9003 - Implemented issue #1850: Change array variable output in tracing to use modern [] syntax - Implemented issue #1856: Rename xdebug.remote_connect_back to xdebug.discover_client_host - Implemented issue #1857: Rename xdebug.remote_addr_header to xdebug.client_discovery_header = Fixed bugs: - Fixed issue #1608: XDEBUG_CONFIG env var make sessions automatically START ever (at least send the XDEBUG_SESSION cookie) - Fixed issue #1726: Memory leaks spotted in various places in typical error code paths - Fixed issue #1757: Pause-execution feature degrades performance - Fixed issue #1864: Incompatibility with PCS and protobuf extensions - Fixed issue #1870: XDEBUG_SESSION_START URL parameter does not override XDEBUG_SESSION cookie - Fixed issue #1871: The "idekey" is not set when debugging is started through XDEBUG_SESSION cookie - Fixed issue #1873: xdebug_info() segfaults if the diagnostic buffer is empty - Fixed issue #1874: Incompatibility with protobuf extension - Fixed issue #1875: Overflow with large amounts of elements for variadics - Fixed issue #1878: Compilation failure: Socket options TCP_KEEPCNT and TCP_KEEPINTVL do not exist on Solaris 10 Sparc - Fixed issue #1880: Bundled unit test tests/debugger/bug00886.phar misses to load phar extension - Fixed issue #1887: Crash bug with xdebug_call_class and xdebug_call_file - Fixed issue #1756: Php process won't exit after running connected to a client - Fixed issue #1823: Profiler generates negative data for memory usage - Fixed issue #1834: Return type must be bool in overloaded set_time_limit - Fixed issue #1888: Make headers sticky in xdebug_info() output + Documentation - Fixed issue #1865: Document XDEBUG_TRIGGER environment variable - Fixed issue #1866: Document comma separated xdebug.mode values - Fixed issue #1884: Document where Xdebug's settings can be set - Fixed issue #1892: Document changed/removed ini settings in the upgrade guide with the links provided 2020-11-16 3.0.0RC1 3.0.0RC1 beta beta BSD style Mon, Nov 16, 2020 - xdebug 3.0.0RC1 This is a BETA release, and not ready for production environments. Xdebug 3 has many changes. Please read the upgrade guide at https://3.xdebug.org/docs/upgrade_guide Xdebug 3 documentation is available at https://3.xdebug.org/docs ------------- + Improvements: - Implemented issue #1814: Don't obtain the current time when it's not needed - Implemented issue #1885: Change xdebug.start_with_ settings to PHP_INI_SYSTEM|PHP_INI_PERDIR - Removed features: - Implemented issue #1016: Removed support for pause-execution (introduced in beta1) - Implemented issue #1868: Remove xdebug_disable and xdebug_enabled - Implemented issue #1883: Function xdebug_is_enabled has been removed = Fixed bugs: - Fixed issue #1608: XDEBUG_CONFIG env var make sessions automatically START ever (at least send the XDEBUG_SESSION cookie) - Fixed issue #1757: Pause-execution feature degrades performance - Fixed issue #1864: Incompatibility with PCS and protobuf extensions - Fixed issue #1870: XDEBUG_SESSION_START URL parameter does not override XDEBUG_SESSION cookie - Fixed issue #1871: The "idekey" is not set when debugging is started through XDEBUG_SESSION cookie - Fixed issue #1873: xdebug_info() segfaults if the diagnostic buffer is empty - Fixed issue #1874: Incompatibility with protobuf extension - Fixed issue #1875: Overflow with large amounts of elements for variadics - Fixed issue #1878: Compilation failure: Socket options TCP_KEEPCNT and TCP_KEEPINTVL do not exist on Solaris 10 Sparc - Fixed issue #1880: Bundled unit test tests/debugger/bug00886.phar misses to load phar extension - Fixed issue #1887: Crash bug with xdebug_call_class and xdebug_call_file + Documentation - Fixed issue #1865: Document XDEBUG_TRIGGER environment variable - Fixed issue #1866: Document comma separated xdebug.mode values - Fixed issue #1884: Document where Xdebug's settings can be set 2020-10-14 3.0.0beta1 3.0.0beta1 beta beta BSD style Wed, Oct 14, 2020 - xdebug 3.0.0beta1 This is a BETA release, and not ready for production environments. Xdebug 3 has many changes. Please read the upgrade guide at https://3.xdebug.org/docs/upgrade_guide Xdebug 3 documentation is available at https://3.xdebug.org/docs ------------- + New features: - Implemented issue #1762: Introduce feature modes - Implemented issue #1793: Add xdebug.start_upon_error setting to cover the removed xdebug.remote_mode=jit feature. - Implemented issue #1797: Implement generic logging - Implemented issue #1801: Rename mode 'display' to mode 'develop' - Implemented issue #1831: Add diagnostics function xdebug_info() - Implemented issue #1833: Add links to documentation in diagnostic log - Implemented issue #1837: Support for associative variadic variable names (PHP 8) - Implemented issue #1841: Add support for PHP 8 'match' keyword - Removed features: - Implemented issue #1795: Deprecate PHP 7.1 support - Implemented issue #1786: Remove idekey value fallback to USER/USERNAME environment variable - Implemented issue #1809: Remove "overload_var_dump" setting - Implemented issue #1810: Remove collect_vars and xdebug_get_declared_vars() - Implemented issue #1812: Remove show_mem_delta setting - Implemented issue #1838: Remove collect_params setting, and always default it to "4" - Implemented issue #1847: Remove xdebug.remote_cookie_expire_time setting = Changes: - Implemented issue #1378: Unfortunate coupling of default_enable=1 and remote_mode=jit - Implemented issue #1773: Replace all xdebug.*_output_dir settings with xdebug.output_dir - Implemented issue #1785: Replace xdebug.remote_mode and xdebug.auto_trace with generic "start-with-request" setting - Implemented issue #1791: Replace xdebug.*trigger*, xdebug.*trigger_value*, with xdebug.start_with_request=trigger and xdebug.trigger_value - Implemented issue #1792: Change start_with_request=always/never to start_with_request=yes/no - Implemented issue #1794: Replace the filter's blacklist/whitelist with exclude/include - Implemented issue #1811: Remove xdebug.collect_includes setting and always include them - Implemented issue #1844: Add deprecation warning for removed and renamed configuration setting names - Implemented issue #1845: Rename xdebug.remote_{host,port} to xdebug.client_{host,port} - Implemented issue #1846: Rename setting xdebug.remote_timeout to xdebug.connect_timeout_ms - Implemented issue #1848: Change default Xdebug port from 9000 to 9003 - Implemented issue #1850: Change array variable output in tracing to use modern [] syntax - Implemented issue #1856: Rename xdebug.remote_connect_back to xdebug.discover_client_host - Implemented issue #1857: Rename xdebug.remote_addr_header to xdebug.client_discovery_header + Improvements: - Implemented issue #1680: Update var dumping routines to include relevant information for interned strings and immutable arrays - Implemented issue #1712: Add unit to profiler data types - Implemented issue #1743: Figuring out whether a call is a closure uses string comparisions instead of checking the ACC flag (Benjamin Eberlei) - Implemented issue #1752: Use a stack pool to manage stack entries instead of allocating and deallocating entries - Implemented issue #1755: Overload pcntl_fork() to prevent performance degradation by calling xdebug_get_pid often (Carlos Granados) - Implemented issue #1781: Include 'Xdebug' in max nesting level error message - Implemented issue #1783: Stacktrace needs vertical scrolling on small screens (Tobias Tom) - Implemented issue #1789: Provide PHP stubs for Xdebug's functions - Implemented issue #1807: Document Xdebug installation with yum and apt - Implemented issue #1813: Make sure that the xdebug_init_*_globals don't do more than they need to, and that init is only done when xdebug.mode != off - Implemented issue #1817: Switch filename storage from char*/size_t to zend_string* - Implemented issue #1818: Switch variable storage from char*/size_t to zend_string* - Implemented issue #1820: Increase time tracing precision (Michael Vorisek) - Implemented issue #1824: Allow Xdebug's mode to be set through an environment variable - Implemented issue #1825: Improve profiler performance by not calling fflush after every function (Michael Vorisek) - Implemented issue #1826: Reduce profiler memory allocation and call overhead - Implemented issue #1829: Switch to 10ns profiler resolution (Michael Vorisek) - Implemented issue #1832: If connect back host can not be contacted, fallback to remote_host/port - Implemented issue #1858: Only open/close log if there is an actual message to log - Implemented issue #1860: Allow xdebug.cloud_id to be set through an environment variable = Fixed bugs: - Fixed issue #1756: Php process won't exit after running connected to a client - Fixed issue #1823: Profiler generates negative data for memory usage - Fixed issue #1834: Return type must be bool in overloaded set_time_limit 2020-09-28 2.9.8 2.9.8 stable stable BSD style Mon, Sep 28, 2020 - xdebug 2.9.8 = Fixed bugs: - Fixed issue #1851: Paths are not counted as coveraged with loops calling function - Fixed issue #1855: Build issues on FreeBSD 2020-09-16 2.9.7 2.9.7 stable stable BSD style Wed, Sep 16, 2020 - xdebug 2.9.7 = Fixed bugs: - Fixed issue #1839: Add keepalive options to debugging socket 2020-05-29 2.9.6 2.9.6 stable stable BSD style Fri, May 29, 2020 - xdebug 2.9.6 = Fixed bugs: - Fixed issue #1782: Cookie "XDEBUG_SESSION" will be soon rejected because it has the "sameSite" attribute set to none - Fixed issue #1787: Branch coverage data does not always follow the lines/functions format - Fixed issue #1790: Segfault in var_dump() or while debugging with protobuf extension 2020-04-25 2.9.5 2.9.5 stable stable BSD style Sat, Apr 25, 2020 - xdebug 2.9.5 = Fixed bugs: - Fixed issue #1772: Crash with exception thrown inside a destructor - Fixed issue #1775: Segfault when another extension compiles a PHP file during RINIT - Fixed issue #1779: Nested multi-line built-in function in namespace are not covered 2020-03-23 2.9.4 2.9.4 stable stable BSD style Mon, Mar 23, 2020 - xdebug 2.9.4 = Fixed bugs: - Fixed issue #1763: Crash while setting opcode overrides in ZTS mode. - Fixed issue #1766: Using the DBGp detach command disables remote debugging for the whole process. 2020-03-13 2.9.3 2.9.3 stable stable BSD style Fri, Mar 13, 2020 - xdebug 2.9.3 = Fixed bugs: - Fixed issue #1753: Resolved breakpoints use information from wrong files - Fixed issue #1758: Xdebug changes error_get_last results inside a try catch - Fixed issue #1759: User registered opcode handlers should call ones already set by other extensions 2020-01-31 2.9.2 2.9.2 stable stable BSD style Fri, Jan 31, 2020 - xdebug 2.9.2 = Fixed bugs: - Fixed issue #1735: DBGp eval warning promoted to Exception can cause out-of-sync responses - Fixed issue #1736: Segmentation fault when other extensions run PHP in RINIT - Fixed issue #1739: Tracing footer not written 2020-01-16 2.9.1 2.9.1 stable stable BSD style Thu, Jan 16, 2020 - xdebug 2.9.1 = Fixed bugs: - Fixed issue #1721: Header may not contain NUL bytes in Unknown on line 0 - Fixed issue #1727: Debugger stops more often than expected due to resolving breakpoints - Fixed issue #1728: INIT_STATIC_METHOD_CALL is not overloaded - Fixed issue #1731: var_dump with DateTime does not output properties (Ryan Mauger) - Fixed issue #1733: SEND_VAR_NO_REF_EX opcode, used for require(), is not overloaded - Fixed issue #1734: Segfault with DBGp "source" with a out-of-range start line number 2019-12-09 2.9.0 2.9.0 stable stable BSD style Mon, Dec 9, 2019 - xdebug 2.9.0 + Improvements: - Fixed issue #1723: Class/function pre-analysis for code coverage speed improvements - Removed features: - Fixed issue #1301: Removed aggregated profiler feature - Fixed issue #1720: Remove superfluous xdebug.remote_handler setting = Fixed bugs: - Fixed issue #1722: Build warning issues on FreeBSD - Fixed issue #1724: Missing property types and uninitialised values in variable dumping routines 2019-12-02 2.8.1 2.8.1 stable stable BSD style Mon, Dec 2, 2019 - xdebug 2.8.1 = Fixed bugs: - Fixed issue #1717: Code coverage turned slow after update from 2.7.2 to 2.8.0 2019-10-31 2.8.0 2.8.0 stable stable BSD style Thu, Oct 31, 2019 - xdebug 2.8.0 = Fixed bugs: - Fixed issue #1665: Segfault with garbage collection and complex function arguments - Fixed issue #1699: Crash during debugging Phalcon project - Fixed issue #1705: Crash while debugging with ionCube being used - Fixed issue #1708: Crash on evaluating object with properties - Fixed issue #1709: Wrong data type breaks tests on Big Endian build - Fixed issue #1713: INIT_FCALL is not overloaded in code coverage 2019-08-26 2.8.0beta2 2.8.0beta2 beta beta BSD style Mon, Aug 26, 2019 - xdebug 2.8.0beta2 = Fixed bugs: - Fixed issue #1540: Code coverage should not run when turned off in php.ini - Fixed issue #1573: Using an exception_handler creates an extra broken profiler file - Fixed issue #1589: function names used in auto_prepend_file missing from profile file - Fixed issue #1613: Wrong name displayed for Recoverable fatal error - Fixed issue #1652: Problems with detach in debugger init stage - Fixed issue #1676: Xdebug doesn't write trace footer for shutdown functions - Fixed issue #1689: Traces show return values and exit information for functions without entry information - Fixed issue #1691: Code Coverage misses fluent interface function call - Fixed issue #1698: Switch PHP 7.4 Windows builds back to VS17 - Fixed issue #1700: Xdebug abuses possibilty immutable class flags 2019-07-25 2.8.0beta1 2.8.0beta1 beta beta BSD style Thu, Jul 25, 2019 - xdebug 2.8.0beta1 = Fixed bugs: - Fixed issue #1679: Code Coverage misses static property as function argument - Fixed issue #1682: Invalid NULL byte in debugger XML with anonymous classes - Fixed issue #1683: Xdebug does not compile due to changes to ASSIGN_ADD and friends operations in PHP 7.4alpha3 - Fixed issue #1687: Use appropriate process ID for logging and "right process" tracking - Fixed issue #1688: Improve performance by using getpid() only when step debugger is active 2019-06-28 2.8.0alpha1 2.8.0alpha1 beta beta BSD style Fri, May 28, 2019 - xdebug 2.8.0alpha1 + Added features: - Implemented issue #1599: Add support for PHP 7.4 + Improvements: - Implemented issue #1388: Support 'resolved' flag for breakpoints - Implemented issue #1664: Run breakpoint resolver when after a new breakpoint is added as well = Fixed bugs: - Fixed issue #1660: Return breakpoints for methods don't break immediately - Removed features: - Fixed issue #1666: Remove xdebug.extended_info setting 2019-05-06 2.7.2 2.7.2 stable stable BSD style Mon, May 6, 2019 - xdebug 2.7.2 = Fixed bugs: - Fixed issue #1488: Rewrite DBGp 'property_set' to always use eval - Fixed issue #1586: error_reporting()'s return value is incorrect during debugger's 'eval' command - Fixed issue #1615: Turn off Zend OPcache when remote debugger is turned on - Fixed issue #1656: remote_connect_back alters header if multiple values are present - Fixed issue #1662: __debugInfo should not be used for user-defined classes 2019-04-05 2.7.1 2.7.1 stable stable BSD style Wed, Apr 5, 2019 - xdebug 2.7.1 = Fixed bugs: - Fixed issue #1646: Missing newline in error message - Fixed issue #1647: Memory corruption when a conditional breakpoint is used - Fixed issue #1641: Perfomance degradation with getpid syscall (Kees Hoekzema) 2019-03-06 2.7.0 2.7.0 stable stable BSD style Wed, Mar 6, 2019 - xdebug 2.7.0 = Fixed bugs: - Fixed issue #1520: Xdebug does not handle variables and properties with "-" in their name - Fixed issue #1577: Code coverage path analysis with chained catch fails in PHP 7.3 - Fixed issue #1639: Compile warning/error on GCC 8 or Clang due to "break intentionally missing" - Fixed issue #1642: Debugger gives: "Warning: Header may not contain NUL bytes" 2019-02-15 2.7.0RC2 2.7.0RC2 beta beta BSD style Fri, Feb 15, 2019 - xdebug 2.7.0RC2 = Fixed bugs: - Fixed issue #1551: Property with value null is not represented well - Fixed issue #1621: Xdebug fails to compile cleanly on 32-bit platforms - Fixed issue #1625: Work around ABI conflicts in PHP 7.3.0/PHP 7.3.1 - Fixed issue #1628: The PHP function name being constructed to record when GC Collection runs, is not freed - Fixed issue #1629: SOAP Client/Server detection code does not handle inherited classes 2019-02-01 2.7.0RC1 2.7.0RC1 beta beta BSD style Fri, Feb 1, 2019 - xdebug 2.7.0RC1 = Fixed bugs: - Fixed issue #1571: File/line information is not shown for closures in namespaces. - Fixed issue #1578: Compile error due to redefinition of "zif_handler" with old GCCs. - Fixed issue #1583: Xdebug crashes when OPcache's compact literals optimisation is on. - Fixed issue #1598: Make path/branch coverage work with OPcache loaded for PHP 7.3 and later. - Fixed issue #1620: Division by zero when GC Stats Collection runs with memory manager disabled. 2018-09-20 2.7.0beta1 2.7.0beta1 beta beta BSD style Thu, Sep 20, 2018 - xdebug 2.7.0beta1 + Improvements: - Fixed issue #1519: PHP 7.3 support 2018-04-01 2.7.0alpha1 2.7.0alpha1 beta beta BSD style Sun, Apr 1, 2018 - xdebug 2.7.0alpha1 = Improvements: - Fixed issue #938: Support remote debugging for PHP scripts that fork. (Sponsored by Brad Wilson) - Fixed issue #1487: Re-enable IPv6 test on Travis. = Fixed bugs: - Fixed issue #1526: Namespace filter does equality match instead of prefix match. - Fixed issue #1532: SIGABRT when using remote debugging and an error is thrown in eval(). - Fixed issue #1543: Various memory leaks due to changes in (internal) string handling. 2018-08-01 2.6.1 2.6.1 stable stable BSD style Wed, Aug 1, 2018 - xdebug 2.6.1 = Fixed bugs: - Fixed issue #1525: Namespace filter does equality match instead of prefix match - Fixed issue #1532: SIGABRT when using remote debugging and an error is thrown in eval() (Philip Hofstetter) - Fixed issue #1543: Various memory leaks due to changes in (internal) string handling - Fixed issue #1556: Crash when register_shutdown_function() is called with a function named call_user_func* - Fixed issue #1557: Remove 'return' in void xdebug_build_fname - Fixed issue #1568: Can't debug object properties that have numeric keys + Improvements: - Fixed issue #1487: Re-enable IPv6 test on Travis 2018-01-29 2.6.0 2.6.0 stable stable BSD style Mon, Jan 29, 2018 - xdebug 2.6.0 = Fixed bugs: - Fixed issue #1522: Remote debugging test failures on s390 (Big Endian). 2018-01-23 2.6.0RC2 2.6.0RC2 beta beta BSD style Tue, Jan 23, 2018 - xdebug 2.6.0RC2 = Fixed bugs: - Fixed issue #1521: xdebug_gc_stats.* missing from 2.6.0RC1 tarball 2018-01-22 2.6.0RC1 2.6.0RC1 beta beta BSD style Mon, Jan 22, 2018 - xdebug 2.6.0RC1 + Added features: - Fixed issue #1506: Add garbage collection statistics feature (Benjamin Eberlei). - Fixed issue #1507: Add functions to access Zend Engine garbage collection metrics (Benjamin Eberlei). + Improvements: - Fixed issue #1510: Change switch/case "break intentionally missing" comments to use GCC 7's new "fallthrough" attribute. - Fixed issue #1511: Detect and use compiler flags through new configure option. = Fixed bugs: - Fixed issue #1335: Debugging with PhpStorm sometimes gives "can not get property". - Fixed issue #1454: Invalid memory read or segfaults from a __call() method. - Fixed issue #1508: Code coverage filter not checked in xdebug_common_assign_dim handler. - Fixed issue #1509: Code coverage missing for case inside switch with PHP 7.2. - Fixed issue #1512: Xdebug does not properly encode and escape properties with quotes and \0 characters. - Fixed issue #1514: Variable names with a NULL char are cut off at NULL char. - Fixed issue #1515: Object property names with a NULL char are cut off at NULL char. - Fixed issue #1516: Can't fetch variables or object properties which have \0 characters in them. - Fixed issue #1517: Notifications incorrectly specify the error type in "type_string" instead of "type". 2017-12-28 2.6.0beta1 2.6.0beta1 beta beta BSD style Thu, Dec 28, 2017 - xdebug 2.6.0beta1 + Added features: - Fixed issue #1059: Add filter capabilities to tracing, stack traces, and code coverage. - Fixed issue #1437: Add X-Profile-File-Name header when a profile file has been generated. + Improvements: - Fixed issue #1493: Run test suite in AppVeyor for Windows CI. - Fixed issue #1498: Use new ZEND_EXTENSION API in config.w32 build scripts. (Kalle) = Fixed bugs: - Fixed issue #702: Check whether variables tracing also works with =&. - Fixed issue #1501: Xdebug var dump tries casting properties. - Fixed issue #1502: SEND_REF lines are not marked as covered. 2017-12-02 2.6.0alpha1 2.6.0alpha1 beta beta BSD style Sat, Dec 2, 2017 - xdebug 2.6.0alpha1 + Added features: - Implemented issue #474: Added "memory" output to profiling files, to find out where memory is allocated. - Implemented issue #575: Dump super globals contents to error log upon errors, just like when this would happen for stack traces. - Implemented issue #964: Parse X-Forwarded-For for the first IP address when selecting the remote_connect_back host (Steve Easley). - Implemented issue #990: Add DBGp: notifications for notices and warnings to be shown in IDEs. - Implemented issue #1312: Implement extended_properties feature to remote debugging to support names and values with low ASCII values. - Implemented issue #1323: Added xdebug.filename_format setting to configure the formatting of filenames when tracing. - Implemented issue #1379: Added support for Unix domain sockets to xdebug.remote_host (Sara Golemon). - Implemented issue #1380: Added xdebug_is_debugger_active() that returns true when debugger is connected. - Implemented issue #1391: Added support for earlier stack frames through new argument for xdebug_call_* functions. - Implemented issue #1420: Handle PHP 7.2's new methods for switch/case - Implemented issue #1470: Added xdebug.remote_timeout to make connect timeout configurable. - Implemented issue #1495: Make var_dump() also use the new xdebug.filename_format when formatting filenames. + Improvements: - Implemented issue #847: Added support for "%s" specifier for xdebug.trace_output_name. - Implemented issue #1384: Compile warning on Ubuntu 16.04 with GCC 5.4.x. - Implemented issue #1401: Improved error message in case the connection breaks. - Implemented issue #1430: Change DBGp tests to use TEST_PHP_EXECUTABLE instead of hard coded 'php' - Implemented issue #1484: Use FD_CLOEXEC with debugging sockets to prevent FDs from leaking to forked processes (Chris Wright). - Improve the foldexpr in xt.vim to fold lines correctly (Donie Leigh). = Fixed bugs: - Fixed issue #1272: property_get doesn't return @attributes for SimpleXMLElement. - Fixed issue #1305: Property names with quotes can not be fetch while debugging. - Fixed issue #1431: Fix "use after free" with in add_name_attribute_or_element. - Fixed issue #1432: Fixed memory leak with xdebug_path_info_dtor. - Fixed issue #1449: Debugging breaks with array element keys containing low-ASCII variables. - Fixed issue #1471: Tracing crashes with return_assignments and ternairy operator. - Fixed issue #1474: Crashes due to variable resolving/reading mechanism not taking care of temporary hash tables correctly (Nikita Popov, Derick). - Fixed issue #1481: Fixed s390x and ppc64 builds (Remi Collet). - Fixed issue #1486: Crash on ZEND_SWITCH_LONG / ZEND_SWITCH_STRING with more than 32 cases. - Fixed issue #1496: Rewrite README.rst to be more clear on how to install and build Xdebug. ~ Changes: - Fixed issue #1411: Use Error (Throwable) instead of fatal error when maximum nesting level is reached. - Removed features: - Implemented issue #1377: Drop support for PHP 5.5 and 5.6, only PHP 7 is now supported 2017-06-21 2.5.5 2.5.5 stable stable BSD style = Fixed bugs: - Fixed issue #1439: TYPE_CHECK needs overloading due to smart branches - Fixed issue #1444: Code Coverage misses a variable in a multi-line function call - Fixed issue #1446: Code Coverage misses elseif if it uses an isset with a property 2017-05-15 2.5.4 2.5.4 stable stable BSD style Mon, May 15, 2017 - xdebug 2.5.4 = Fixed bugs: - Fixed issue #799: Function traces report base class instead of object name - Fixed issue #1421: Fix set_time_limit hanging on PHP 5.6 when pcntl_exec does not exist (Frode E. Moe) - Fixed issue #1429: Code coverage does not cover null coalesce - Fixed issue #1434: Code coverage segfaults on 32-bit arch 2017-04-18 2.5.3 2.5.3 stable stable BSD style Mon, Apr 18, 2017 - xdebug 2.5.3 = Fixed bugs: - Fixed issue #1421: Xdebug crashes when it is loaded without pcntl being present 2017-04-17 2.5.2 2.5.2 stable stable BSD style Mon, Apr 17, 2017 - xdebug 2.5.2 = Fixed bugs: - Fixed issue #701: Functions as array indexes show ??? in trace - Fixed issue #1403: Code coverage does not cover BIND_STATIC - Fixed issue #1404: Execution time is calculated incorrectly - Fixed issue #1413: Code coverage mishap with PHP 7.1.3 - Fixed issue #1414: Missing variable assignment in traces with OPcache loaded - Fixed issue #1415: Crash with multiple catch constructs with OPcache loaded - Fixed issue #1416: Trace files should not include the first result of a generator if it hasn't started yet - Fixed issue #1417: Fetching properties of static class contexts fails due to incorrect fetch mode - Fixed issue #1419: Summary not written when script ended with "pcntl_exec()" 2017-04-17 2.5.2 2.5.2 stable stable BSD style Sun, Feb 26, 2017 - xdebug 2.5.1 = Fixed bugs: - Fixed issue #1057: Add xdebug.ini of all settings to package - Fixed issue #1165: DBGp: step_out skips subsequent function calls - Fixed issue #1180: Code coverage crashes with non-standard start/stops - Fixed issue #1278: Xdebug with PHP 7 does not handle prefill-from-oparray for XDEBUG_CC_UNUSED - Fixed issue #1300: Xdebug functions are not exposing their signature to Reflection - Fixed issue #1313: Arguments to __call() trampoline picked from the wrong memory location - Fixed issue #1329: While printing out a stack with and function parameters, XDebug reads uninitialized zvals or free()d memory - Fixed issue #1381: Code Coverage misses line due to missing FETCH_DIM_W overload - Fixed issue #1385: can not fetch IS_INDIRECT properties - Fixed issue #1386: Executable code not shown as executed/executable - Fixed issue #1392: Unable to compile on FreeBSD due to missing struct definition - Fixed issue #1394: Code coverage does not cover instanceof (in elseif) 2016-12-04 2.5.0 2.5.0 stable stable BSD style Sun, Dec 4, 2016 - xdebug 2.5.0 + Added features: - Implemented issue #1232: add memory delta to HTML traces - Implemented issue #1365: Allow remote_connect_back to be set through XDEBUG_CONFIG = Fixed bugs: - Fixed issue #1168: Added defensive check to prevent infinite loop - Fixed issue #1242: Xdebug on Windows with Eclipse has issues with breakpoint IDs - Fixed issue #1343: Wrong values of numerical keys outside 32bit range - Fixed issue #1357: Function signature using variadics is reported as being not executed - Fixed issue #1361: Remote debugging connection issues with Windows (Anatol Belski) - Fixed issue #1373: Crash in zend_hash_apply_with_arguments when debugging, due to unset symbol table 2016-11-12 2.5.0RC1 2.5.0RC1 stable stable BSD style Sat, Nov 12, 2016 - xdebug 2.5.0RC1 + Added features: - Implemented issue #998: Added support for IPv6 (Thomas Vanhaniemi) - Implemented issue #1297: Initial PHP 7.1 support = Fixed bugs: - Fixed issue #1295: Apache crashes (SIGSEGV) when trying to establish connection when sockfd is large - Fixed issue #1303: POLLRDHUP is not supported outside of Gnu/Linux - Fixed issue #1331: Segfault in code coverage - Removed features: - Support for PHP versions lower than PHP 5.5 has been dropped 2016-08-02 2.4.1 2.4.1 stable stable BSD style Tue, Aug 02, 2016 - xdebug 2.4.1 = Fixed bugs: - Fixed issue #1106: A thrown Exception after a class with __debugInfo gives 2 errors - Fixed issue #1241: FAST_CALL/FAST_RET take #2 - Fixed issue #1246: Path and branch coverage should be initialised per request, not globally - Fixed issue #1263: Code coverage segmentation fault with opcache enabled - Fixed issue #1277: Crash when using a userland function from RSHUTDOWN with profiling enabled - Fixed issue #1282: var_dump() of integers > 32 bit is broken on Windows - Fixed issue #1288: Segfault when uncaught exception message does not contain " in " - Fixed issue #1291: Debugclient installation fails on Mac OS X - Fixed issue #1326: Tracing and generators crashes with PHP 7.x - Fixed issue #1333: Profiler accesses memory structures after freeing 2016-01-25 2.4.0RC4 2.4.0RC4 beta beta BSD style Mon, Jan 25, 2016 - xdebug 2.4.0RC4 = Fixed bugs: - Fixed issue #1220: Segmentation fault if var_dump() output is too large. - Fixed issue #1223: Xdebug crashes on PHP 7 when doing a DBGp eval command. - Fixed issue #1229: Issues with GCC 4.8, which in -O2 move removes some required code. - Fixed issue #1235: Xdebug does not compile against PHP 7.1-dev due to ZEND_FETCH_STATIC_PROP*. - Fixed issue #1236: Can't remove breakpoints with negative IDs. - Fixed issue #1238: Xdebug crashes with SIGSEGV while enumerating references in variables. - Fixed issue #1239: Crash due to changes with the CATCH opcode's jump mechanism in 7.1 - Fixed issue #1241: Xdebug doesn't handle FAST_RET and FAST_CALL opcodes for branch/dead code analysis, and path coverage. - Fixed issue #1245: xdebug_dump_superglobals dumps *uninitialized* with PHP 7. - Fixed issue #1250: Add PHP version descriptors to debugging log and profile files. 2016-03-03 2.4.0 2.4.0 stable stable BSD style Thu, Mar 03, 2016 - xdebug 2.4.0 = Fixed bugs: - Fixed issue #1258: Case in PHP 7.0 and code coverage - Fixed issue #1261: segmentation fault in xdebug.so with PHP 7.0 version of 'pkgtools' due to spl_autoload() - Fixed issue #1262: overload_var_dump=0 messes with xdebug_var_dump() - Fixed issue #1266: xdebug_dump_superglobals() always dumps empty stack on PHP 7 - Fixed issue #1267: AIX build issues - Fixed issue #1270: String parsing marked not covered with PHP 7 2015-12-12 2.4.0RC3 2.4.0RC3 beta beta BSD style Wed, Dec 12, 2015 - xdebug 2.4.0RC3 = Fixed bugs: - Fixed issue #1221: Sort out Windows x64 PHP 7 support - Fixed issue #1229: Detect GCC 4.8 and disable optimisations when it is found = Others: - Made the test suite work for Windows too. Finally, after 13 years. 2015-12-02 2.4.0RC2 2.4.0RC2 beta beta BSD style Wed, Dec 02, 2015 - xdebug 2.4.0RC2 = Fixed bugs: - Fixed issue #1181: Remote debugging does not handle exceptions after using zend_read_property - Fixed issue #1189: Remove address attribute from remote debugging responses - Fixed issue #1194: The error message is doubly HTML-encoded with assert() - Fixed issue #1210: Segfault with code coverage dead code analysis and foreach on PHP 7 - Fixed issue #1215: SIGSEGV if xdebug.trace_output_dir directory does not exist - Fixed issue #1217: xdebug.show_error_trace should not be enabled by default - Fixed issue #1218: Xdebug messes with the exception code, by casting it to int - Fixed issue #1219: Set default value for xdebug.overload_var_dump to 2 to include file / line numbers by default - Use long for PHP 5, and zend_long for PHP 7 for ini settings in the globals 2015-11-21 2.4.0RC1 2.4.0RC1 beta beta BSD style Sat, Nov 21, 2015 - xdebug 2.4.0RC1 = Fixed bugs: - Fixed issue #1195: Segfault with code coverage and foreach - Fixed issue #1200: Additional opcodes need to be overloaded for PHP 7 - Fixed issue #1202: Anonymous classes are not handled properly while remote debugging - Fixed issue #1203: Accessing static property of a class that has no static properties crashes while remote debugging - Fixed issue #1209: Segfault with building a function name for create_function - Restored Windows support (Includes patches by Jan Ehrhardt) 2015-11-05 2.4.0beta1 2.4.0beta1 beta beta BSD style Thu, Sep 05, 2015 - xdebug 2.4.0beta1 + Added features: - Implemented issue #1109: Added support for PHP 7. - Implemented issue #1153: Add function monitor functionality. - Implemented issue #1183: Add xdebug.show_error_trace setting to allow/disallow to show a stack trace for every Error (throwable) = Fixed bugs: - Fixed issue #1070: Too many open files error with php-fpm: connections not closed. (Patch by Sean Dubois) - Fixed issue #1123: With Xdebug 2.3.1, PHPUnit with coverage is exponentially slower than without - Fixed issue #1166: Using $this in __debugInfo() causes infinite recursion - Fixed issue #1173: Segmentation fault in xdebug_get_monitored_functions() - Fixed issue #1182: Using PHPStorm with PHP 7 RC1 and xdebug 2.4-dev break points are passed by including setting break point at start of script - Fixed issue #1192: Dead code analysis does not work for generators with 'return;' 2015-06-19 2.3.3 2.3.3 stable stable BSD style Fri, Jun 19, 2015 - xdebug 2.3.3 = Fixed bugs: - Fixed issue #1130: Escaping issues with docrefs and HTML characters in error messages - Fixed issue #1133: PDO exception code value type is changed - Fixed issue #1137: Windows does not support %zu formatting for sprintf - Fixed issue #1140: Tracing with __debugInfo() crashes Xdebug due to a stack overflow - Fixed issue #1148: Can't disable max_nesting_function - Fixed issue #1151: Crash when another extension calls call_user_function() during RINIT - Fixed crash with code coverage (Antony Dovgal) - Fixed usage of virtual_file_ex and STR_FREE (Remi Collet) - Reset overloaded opcodes at the end of each request (Eran Ifrah) = Improvements: - Fixed issue #686: Not possible to inspect SplObjectStorage instances with Xdebug - Fixed issue #864: No attributes are shown if an object extends ArrayIterator - Fixed issue #996: Can't evaluate property of class that extends ArrayObject - Fixed issue #1134: Allow introspection of ArrayObject implementation's internal storage - Get rid of setlocale hack, by using %F instead of %f (and speed up tracing by 15-20%) 2015-03-22 2.3.2 2.3.2 stable stable BSD style Sun, Mar 22, 2015 - xdebug 2.3.2 = Fixed bugs: - Fixed issue #1117: Path/branch coverage sometimes crashes - Fixed issue #1121: Segfaults with path/branch coverage 2015-02-24 2.3.1 2.3.1 stable stable BSD style Tue, Feb 24, 2015 - xdebug 2.3.1 = Fixed bugs: - Fixed issue #1112: Setting an invalid xdebug.trace_format causes Xdebug to crash - Fixed issue #1113: xdebug.*_trigger do no longer work, due to NULL not being an empty string 2015-02-22 2.3.0 2.3.0 stable stable BSD style Sun, Feb 22, 2015 - xdebug 2.3.0 = Fixed bugs: - Fixed bug #932: Added an error message in case the remote debug log couldn't be opened - Fixed bug #982: Incorrect file paths in exception stack trace - Fixed bug #1094: Segmentation fault when attempting to use branch/path coverage - Fixed bug #1101: Debugger is not triggered on xdebug_break() in JIT mode - Fixed bug #1102: Stop Xdebug from crashing when debugging PHP Code with "php -r". - Fixed bug #1103: XDEBUG_SESSION_STOP_NO_EXEC only stops first script executed with auto_prepend|append_files - Fixed bug #1104: One character non-public properties cause issues with debugging - Fixed bug #1105: Setting properties without specifying a type only works in topmost frame (Dominik del Bondio) - Fixed bug #1095: Crash when using a non-associate array key in GLOBALS - Fixed bug #1111: eval does not work when debugger is stopped in xdebug_throw_exception_hook (Dominik del Bondio) + Added features: - General - Implemented issue #304: File name and line number info for overloaded var_dump() - Implemented issue #310: Allow class vars and array keys with xdebug_debug_zval() - Implemented issue #722: Add stack trace limit setting. - Implemented issue #1003: Add option to xdebug_print_function_stack() to suppress filename and line number - Implemented issue #1004: Ability to halt on warning/notice - Implemented issue #1023: Add support for PHP 5.6 variadics - Implemented issue #1024: Add support for PHP 5.6's ASSIGN_POW - Debugging - Implemented issue #406: Added support for remote debugging user-defined constants - Implemented issue #495: Added support for the wildcard exception name '*' - Implemented issue #1066: Better error message for SELinux preventing debugging connections - Implemented issue #1084: Added support for extended classes to trigger exception breakpoints - Implemented issue #1084: Added exception code as extra element to debugger XML - Tracing - Implemented issue #341: Added the time index and memory usage for function returns in normal tracefiles - Implemented issue #644: Shared secret for profiler_enable_trigger and trace_enable_trigger with *_value option - Implemented issue #971: Added the trace file option "XDEBUG_TRACE_NAKED_FILENAME" to xdebug_start_trace() to prevent the ".xt" extension from being added - Implemented issue #1021: Added support for return values to computerized trace files - Implemented issue #1022: Added support for serialized variables as format in trace files in the form of option "5" for "xdebug.collect_params" - Code coverage - Implemented issue #380: Added xdebug_code_coverage_started() - Implemented issue #1034: Add collected path and branch information to xdebug_get_code_coverage() output - Profiling - Implement issue #1054: Support for filename and function name compression in cachegrind files + Changes: - Implemented issue #863: Support xdebug.overload_var_dump through ini_set() - Implemented issue #973: Use case-insensitive filename comparison on all systems (Galen Wright-Watson) - Implemented issue #1015: Added the xdebug.force_display_errors and xdebug.force_error_reporting php.ini-only settings to always override PHP's settings for display_errors and error_reporting - Implemented issue #1057: Removed trailing whitespace from example xdebug.ini - Implemented issue #1096: Improve performance improvement for handling breakpoints by ignoring locales (Daniel Sloof) - Implemented issue #1100: Raise default max_nesting_level to 256 - Removed features: - Support for PHP versions lower than PHP 5.4 have been dropped. 2015-01-21 2.2.7 2.2.7 stable stable BSD style Thu, Jan 22, 2014 - xdebug 2.2.7 = Fixed bugs: - Fixed bug #1083: Segfault when requesting a variable for a context that did not have them. - Fixed bug #1087: zend_execute_script or zend_eval_string in RINIT segfaults. - Fixed bug #1088: Xdebug won't show dead and not executed lines at the second time. - Fixed bug #1098: Xdebug doesn't make use of __debugInfo. - Fixed segfaults with ZTS on PHP 5.6. 2014-11-14 2.2.6 2.2.6 stable stable BSD style Fri, Nov 14, 2014 - xdebug 2.2.6 = Fixed bugs: - Fixed bug #1048: Can not get $GLOBAL variable by property_value on function context. - Fixed bug #1073 and #1075: Segmentation fault with internal functions calling internal functions. - Fixed bug #1085: Fixed the tracefile analyser as the format version had been bumbed. - Fixed memory leaks 2014-04-29 2.2.5 2.2.5 stable stable BSD style Tue, Apr 29, 2014 - xdebug 2.2.5 = Fixed bugs: - Fixed bug #1040: Fixed uninitialized sa value. - Fixed building on hurd-i386. 2014-02-28 2.2.4 2.2.4 stable stable BSD style Fri, Feb 28, 2014 - xdebug 2.2.4 = Fixed bugs: - Fixed bug #785: Profiler does not handle closures and call_user_func_array well. - Fixed bug #963: Xdebug waits too long for response from remote client - Fixed bug #976: XDebug crashes if current varibles scope contains COM object. - Fixed bug #978: Inspection of array with negative keys fails - Fixed bug #979: property_value -m 0 should mean all bytes, not 0 bytes - Fixed bug #987: Hidden property names not shown. 2013-05-22 2.2.3 2.2.3 stable stable BSD style Tue, May 21, 2013 - xdebug 2.2.3 + Added features: - Support for PHP 5.5. = Fixed bugs: - Fixed bug #923: Xdebug + Netbeans + ext/MongoDB crash on MongoCursor instance - Fixed bug #929: Directory name management in xdebug.profiler_output_dir - Fixed bug #931: xdebug_str_add does not check for NULL str before calling strlen on it - Fixed bug #935: Document the return value from xdebug_get_code_coverage() - Fixed bug #947: Newlines converted when html_errors = 0 2013-03-23 2.2.2 2.2.2 stable stable BSD style Sat, Mar 23, 2013 - xdebug 2.2.2 + Added features: - Support for PHP 5.5. = Fixed bugs: - Fixed bug #598: Use HTTP_X_FORWARDED_FOR to determine remote debugger. - Fixed bug #625: xdebug_get_headers() -> Headers are reset unexpectedly. - Fixed bug #811: PHP Documentation Link. - Fixed bug #818: Require a php script in the PHP_RINIT causes Xdebug to crash. - Fixed bug #903: xdebug_get_headers() returns replaced headers. - Fixed bug #905: Support PHP 5.5 and generators. - Fixed bug #920: AM_CONFIG_HEADER is depreciated. 2.2.1 2.2.1 stable stable 2012-07-14 BSD style = Fixed bugs: - Fixed bug #843: Text output depends on php locale. - Fixed bug #838/#839/#840: Debugging static properties crashes Xdebug. - Fixed bug #821: Variable assignments (beginning with =>) should be indented one more scope. - Fixed bug #811: PHP Documentation Link. - Fixed bug #800: var_dump(get_class(new foo\bar')) add an extra "\" in class name. 2.2.0 2.2.0 stable stable 2012-05-08 BSD style Tue, May 08, 2012 - xdebug 2.2.0 + Added features: - Support for PHP 5.4. - Added ANSI colour output for the shell. (Including patches by Michael Maclean) - Added var_dump() overloading on the command line (issue #457). - Added better support for closures in stack and function traces. - Added the size of arrays to the overloaded variable output, so that you know how many elements there are. - Added support for X-HTTP-FORWARDED-FOR before falling back to REMOTE_ADDR (issue #660). (Patch by Hannes Magnusson) - Added the method call type to xdebug_get_function_stack() (issue #695). - Added extra information to error printouts to tell that the error suppression operator has been ignored due to xdebug.scream. - Added a error-specific CSS class to stack traces. + New settings: - xdebug.cli_color for colouring output on the command line (Unix only). - Added xdebug.trace_enable_trigger to triger function traces through a GET/POST/COOKIE parameter (issue #517). (Patch by Patrick Allaert) - Added support for the 'U' format specifier for function trace and profiler filenames. + Changes: - Improved performance by lazy-initializing data structures. - Improved code coverage performance. (Including some patches by Taavi Burns) - Improved compatibility with KCacheGrind. - Improved logging of remote debugging connections, by added connection success/failure logging to the xdebug.remote_log functionality. = Fixed bugs: - Fixed bug #827: Enabling Xdebug causes phpt tests to fail because of var_dump() formatting issues. - Fixed bug #823: Single quotes are escaped in var_dumped string output. - Fixed issue #819: Xdebug 2.2.0RC2 can't stand on a breakpoint more than 30 seconds. - Fixed bug #801: Segfault with streamwrapper and unclosed $fp on destruction. - Fixed issue #797: Xdebug crashes when fetching static properties. - Fixed bug #794: Allow coloured output on Windows. - Fixed bug #784: Unlimited feature for var_display_max_data and var_display_max_depth is undocumented. - Fixed bug #774: Apache crashes on header() calls. - Fixed bug #764: Tailored Installation instructions do not work. - Fixed bug #758: php_value xdebug.idekey is ignored in .htaccess files - Fixed bug #728: Profiler reports __call() invocations confusingly/wrongly. - Fixed bug #687: Xdebug does not show dynamically defined variable. - Fixed bug #662: idekey is set to running user. - Fixed bug #627: Added the realpath check. 2.2.0RC2 2.2.0RC2 stable stable 2012-04-22 BSD style Tue, Apr 22, 2012 - xdebug 2.2.0rc2 = Fixed bugs: - Fixed bug #801: Segfault with streamwrapper and unclosed $fp on destruction. - Fixed bug #794: Allow coloured output on Windows. - Fixed bug #784: Unlimited feature for var_display_max_data and var_display_max_depth is undocumented. - Fixed bug #774: Apache crashes on header() calls. - Fixed bug #764: Tailored Installation instructions do not work. - Fixed bug #758: php_value xdebug.idekey is ignored in .htaccess files - Fixed bug #662: idekey is set to running user. 2.2.0RC1 2.2.0RC1 stable stable 2012-03-12 BSD style Tue, Mar 13, 2012 - xdebug 2.2.0rc1 + Added features: - Support for PHP 5.4. - Added ANSI colour output for the shell. (Including patches by Michael Maclean) - Added var_dump() overloading on the command line (issue #457). - Added better support for closures in stack and function traces. - Added the size of arrays to the overloaded variable output, so that you know how many elements there are. - Added support for X-HTTP-FORWARDED-FOR before falling back to REMOTE_ADDR (issue #660). (Patch by Hannes Magnusson) - Added the method call type to xdebug_get_function_stack() (issue #695). - Added extra information to error printouts to tell that the error suppression operator has been ignored due to xdebug.scream. - Added a error-specific CSS class to stack traces. + New settings: - xdebug.cli_color for colouring output on the command line (Unix only). - Added xdebug.trace_enable_trigger to triger function traces through a GET/POST/COOKIE parameter (issue #517). (Patch by Patrick Allaert) - Added support for the 'U' format specifier for function trace and profiler filenames. + Changes: - Improved performance by lazy-initializing data structures. - Improved code coverage performance. (Including some patches by Taavi Burns) - Improved compatibility with KCacheGrind. - Improved logging of remote debugging connections, by added connection success/failure logging to the xdebug.remote_log functionality. = Fixed bugs: - No additional bug fixes besides the ones from the 2.1 branch up til Xdebug 2.1.4. 2.1.4 2.1.4 stable stable 2012-03-12 BSD style = Fixed bugs: - Fixed bug #788: Collect errors eats fatal errors. - Fixed bug #787: Segmentation Fault with PHP header_remove(). - Fixed bug #778: Xdebug session in Eclipse crash whenever it run into simplexml_load_string call. - Fixed bug #756: Added support for ZEND_*_*_OBJ and self::*. - Fixed bug #747: Still problem with error message and soap client / soap server. - Fixed bug #744: new lines in a PHP file from Windows are displayed with an extra white line with var_dump(). - Fixed an issue with debugging and the eval command. - Fixed compilation with ZTS on PHP < 5.3 2.1.3 2.1.3 stable stable 2012-01-25 BSD style = Fixed bugs: - Fixed bug #725: EG(current_execute_data) is not checked in xdebug.c, xdebug_statement_call. - Fixed bug #723: xdebug is stricter than PHP regarding Exception property types. - Fixed bug #714: Cachegrind files have huge (wrong) numbers in some lines. - Fixed bug #709: Xdebug doesn't understand E_USER_DEPRECATED. - Fixed bug #698: Allow xdebug.remote_connect_back to be set in .htaccess. - Fixed bug #690: Function traces are not appended to file with xdebug_start_trace() and xdebug.trace_options=1. - Fixed bug #623: Static properties of a class can be evaluated only with difficulty. - Fixed bug #614/#619: Viewing private variables in base classes through the debugger. - Fixed bug #609: Xdebug and SOAP extension's error handlers conflict. - Fixed bug #606/#678/#688/#689/#704: crash after using eval on an unparsable, or un-executable statement. - Fixed bug #305: xdebug exception handler doesn't properly handle special chars. + Changes: - Changed xdebug_break() to hint to the statement execution trap instead of breaking forcefully adding an extra stackframe. - Prevent Xdebug 2.1.x to build with PHP 5.4. 2.1.2 2.1.2 stable stable 2011-07-28 BSD style = Fixed bugs: - Fixed bug #622: Working with eval() code is inconvenient and difficult. - Fixed bug #684: xdebug_var_dump - IE does not support &. - Fixed bug #693: Cachegrind files not written when filename is very long. - Fixed bug #697: Incorrect code coverage of function arguments when using XDEBUG_CC_UNUSED. - Fixed bug #699: Xdebug gets the filename wrong for the countable interface. - Fixed bug #703 by adding another opcode to the list that needs to be overridden. 2.1.2 2.1.2 stable stable 2011-07-28 BSD style = Fixed bugs: - Fixed bug #622: Working with eval() code is inconvenient and difficult. - Fixed bug #684: xdebug_var_dump - IE does not support &. - Fixed bug #693: Cachegrind files not written when filename is very long. - Fixed bug #697: Incorrect code coverage of function arguments when using XDEBUG_CC_UNUSED. - Fixed bug #699: Xdebug gets the filename wrong for the countable interface. - Fixed bug #703 by adding another opcode to the list that needs to be overridden. 2.1.1 2.1.1 stable stable 2011-03-28 BSD style Mon, Mar 28, 2011 - xdebug 2.1.1 = Fixed bugs: - Fixed ZTS compilation. 2.1.1RC1 2.1.1RC1 beta beta 2011-03-22 BSD style Tue, Mar 22, 2011 - xdebug 2.1.1rc1 = Fixed bugs: = Debugger - Fixed bug #518: Removed CLASSNAME pseudo-property optional. - Fixed bug #592: Xdebug crashes with run after detach. - Fixed bug #596: Call breakpoint never works with instance methods, only static methods. - Fixed JIT mode in the debugger so that it works for xdebug_break() too. = Profiler - Fixed bug #631: Summary not written when script ended with "exit()". - Fixed bug #639: Xdebug profiling: output not correct - missing 'cfl='. - Fixed bug #642: Fixed line numbers for offsetGet, offsetSet, __get/__set/__isset/__unset and __call in profile files and stack traces/function traces. - Fixed bug #643: Profiler gets line numbers wrong. - Fixed bug #653: XDebug profiler crashes with %H in file name and non standard port. = Others - Fixed bug #651: Incorrect code coverage after empty() in conditional. - Fixed bug #654: Xdebug hides error message in CLI. - Fixed bug #665: Xdebug does not respect display_errors=stderr. Patch by Ben Spencer <dangerous.ben@gmail.com> - Fixed bug #670: Xdebug crashes with broken "break x" code. 2.1.0 2.1.0 stable stable 2010-06-29 BSD style Tue, Jun 29, 2010 - xdebug 2.1.0 = Fixed bugs: - Fixed bug #562: Incorrect coverage information for closure function headers. - Fixed bug #566: Xdebug crashes when using conditional breakpoints. - Fixed bug #567: xdebug_debug_zval and xdebug_debug_zval_stdout don't work with PHP 5.3. (Patch by Endo Hiroaki). - Fixed bug #570: undefined symbol: zend_memrchr. 2.1.0RC1 2.1.0RC1 beta beta 2010-02-27 BSD style Thu, Apr 06, 2010 - xdebug 2.1.0rc1 = Fixed bugs: - Fixed bug #494: Private attributes of parent class unavailable when inheriting. - Fixed bug #400: Xdebug shows errors, even when PHP is request startup mode. - Fixed bug #421: xdebug sends back invalid characters in xml sometimes. - Fixed bug #475: Property names with null chars are not sent fully to the client. - Fixed bug #480: Issues with the reserved resource in multi threaded environments (Patch by Francis.Grolemund@netapp.com). - Fixed bug #558: PHP segfaults when running a nested eval. 2.1.0beta3 2.1.0beta3 beta beta 2010-02-27 BSD style Sat, Feb 27, 2010 - xdebug 2.1.0beta3 = Fixed bugs: - Fixed memory corruption issues. - Fixed a threading related issue for code-coverage. - Fixed bug #532: XDebug breaks header() function. - DBGP: Prevent Xdebug from returning properties when a too high page number has been requested. 2.1.0beta2 2.1.0beta2 beta beta 2010-02-03 BSD style Wed, Feb 03, 2010 - xdebug 2.1.0beta2 = Fixed bugs: - Fixed memory leak in breakpoint handling. - Fixed bug #528: Core dump generated with remote_connect_back option set and CLI usage. - Fixed bug #515: declare(ticks) statement confuses code coverage. - Fixed bug #512: DBGP: breakpoint_get doesn't return conditions in its response. - Possible fix for bug #507/#517: Crashes because of uninitalised header globals. - Fixed bug #501: Xdebug's variable tracing misses POST_INC and variants. 2.1.0beta1 2.1.0beta1 beta beta 2010-01-03 BSD style Sun, Jan 03, 2010 - xdebug 2.1.0beta1 + Added features: - Added error display collection and suppressions. - Added the recording of headers being set in scripts. - Added variable assignment tracing. - Added the ability to turn of the default overriding of var_dump(). - Added "Scream" support, which disables the @ operator. - Added a trace-file analysing script. - Added support for debugging into phars. - Added a default xdebug.ini. (Patch by Martin Schuhfu <martins@spot-media.de>) - Added function parameters in computerized function traces. - PHP 5.3 compatibility. - Improved code coverage accuracy. + New functions: - xdebug_get_formatted_function_stack(), which returns a formatted function stack instead of displaying it. - xdebug_get_headers(), which returns all headers that have been set in a script, both explicitly with things like header(), but also implicitly for things like setcookie(). - xdebug_start_error_collection(), xdebug_stop_error_collection() and xdebug_get_collected_errors(), which allow you to collect all notices, warnings and error messages that Xdebug generates from PHP's error_reporting functionality so that you can output them at a later point in your script by hand. + New settings: - xdebug.collect_assignments, which enables the emitting of variable assignments in function traces. - xdebug.file_line_format, to generate a link with a specific format for every filename that Xdebug outputs. - xdebug.overload_var_dump, which allows you to turn off Xdebug's version of var_dump(). - xdebug.remote_cookie_expire_time, that controls the length of a remote debugging session. (Patch by Rick Pannen <pannen@gmail.com>) - xdebug.scream, which makes the @ operator to be ignored. + Changes: - Added return values for xdebug_start_code_coverage() and xdebug_stop_code_coverage() to indicate whether the action was successful. xdebug_start_code_coverage() will return TRUE if the call enabled code coverage, and FALSE if it was already enabled. xdebug_stop_code_coverage() will return FALSE when code coverage wasn't started yet and TRUE if it was turned on. - Added an optional argument to xdebug_print_function_stack() to display your own message. (Patch by Mikko Koppanen). - All HTML output as generated by Xdebug now has a HTML "class" attribute for easy CSS formatting. - Removed features: - Support for PHP versions lower than PHP 5.1 have been dropped. - The PHP3 and GDB debugger engines have been removed. = Fixed bugs: - Fixed support for showing $this in remote debugging sessions. - Fixed bug in formatting the display of "Variables in the local scope". - Possible fix for a threading issue where the headers gathering function would create stack overflows. - Possible fix for #324: xdebug_dump_superglobals() only dumps superglobals that were accessed before, and #478: XDebug 2.0.x can't use %R in xdebug.profiler_output_name if register_long_arrays is off. - Fixed bug #505: %s in xdebug.trace_output_name breaks functions traces. - Fixed bug #494: Private attributes of parent class unavailable when inheriting. - Fixed bug #486: feature_get -n breakpoint_types returns out of date list. - Fixed bug #476: Xdebug doesn't support PHP 5.3's exception chaining. - Fixed bug #472: Dead Code Analysis for code coverage messed up after goto. - Fixed bug #470: Catch blocks marked as dead code unless executed. - Fixed bug #469: context_get for function variables always appear as "uninitialized". - Fixed bug #468: Property_get on $GLOBALS works only at top-level, by adding GLOBALS to the super globals context. - Fixed bug #453: Memory leaks. - Fixed bug #445: error_prepend_string and error_append_string are ignored by xdebug_error_cb. (Patch by Kent Davidson <kent@marketruler.com>) - Fixed bug #442: configure: error: "you have strange libedit". - Fixed bug #439: Xdebug crash in xdebug_header_handler. - Fixed bug #423: Conflicts with funcall. - Fixed bug #419: Make use of P_tmpdir if defined instead of hard coded '/tmp'. - Fixed bug #417: Response of context_get may lack page and pagesize attributes. - Fixed bug #411: Class/function breakpoint setting does not follow the specs. - Fixed bug #393: eval returns array data at the previous page request. - Fixed bug #391: Xdebug doesn't stop executing script on catchable fatal errors. - Fixed bug #389: Destructors called on fatal error. - Fixed bug #368: Xdebug's debugger bails out on a parse error with the eval command. - Fixed bug #356: Temporary breakpoints persist. - Fixed bug #355: Function numbers in trace files weren't unique. - Fixed bug #340: Segfault while throwing an Exception. - Fixed bug #328: Private properties are incorrectly enumerated in case of extended classes. - Fixed bug #249: Xdebug's error handler messes up with the SOAP extension's error handler. + DBGP: - Fixed cases where private properties where shown for objects, but not accessible. - Added a patch by Lucas Nealan (lucas@php.net) and Brian Shire (shire@php.net) of Facebook to allow connections to the initiating request's IP address for remote debugging. - Added the -p argument to the eval command as well, pending inclusion into DBGP. - Added the retrieval of a file's execution lines. I added a new un-official method called xcmd_get_executable_lines which requires the stack depth as argument (-d). You can only fetch this information for stack frames as it needs an available op-array which is only available when a function is executed. - Added a fake "CLASSNAME" property to objects that are returned in debug requests to facilitate deficiencies in IDEs that fail to show the "classname" XML attribute. 2.0.5 2.0.5 stable stable 2009-07-03 BSD style Fri, Jul 03, 2009 - xdebug 2.0.5 = Fixed bugs: - Fixed bug #425: memory leak (around 40MB for each request) when using xdebug_start_trace. - Fixed bug #422: Segfaults when using code coverage with a parse error in the script. - Fixed bug #418: compilation breaks with CodeWarrior for NetWare. - Fixed bug #403: 'call' and 'return' breakpoints triggers both on call and return for class method breakpoints. - Fixed TSRM issues for PHP 5.2 and PHP 5.3. (Original patch by Elizabeth M. Smith). - Fixed odd crash bugs, due to GCC 4 sensitivity. 2.0.4 2.0.4 stable stable 2008-12-30 BSD style Tue, Dec 30, 2008 - xdebug 2.0.4 = Fixed bugs: - Fixed for strange jump positions in path analysis. - Fixed issues with code coverage crashing on parse errors. - Fixed code code coverage by overriding more opcodes. - Fixed issues with Xdebug stalling/crashing when detaching from remote debugging. - Fixed crash on Vista where memory was freed with routines from a different standard-C library than it was allocated with. (Patch by Eric Promislow <ericp@activestate.com>). - Link against the correct CRT library. (Patch by Eric Promislow <ericp@activestate.com>). - Sort the symbol elements according to name. (Patch by Eric Promislow <ericp@activestate.com>). - Fixed support for mapped-drive UNC paths for Windows. (Patch by Eric Promislow <ericp@activestate.com>). - Fixed a segfault in interactive mode while including a file. - Fixed a crash in super global dumping in case somebody was strange enough to reassign them to a value type other than an Array. - Simplify version checking for libtool. (Patch by PGNet <pgnet.trash@gmail.com>). - Fixed display of unused returned variables from functions in PHP 5.3. - Include config.w32 in the packages as well. - Fixed .dsp for building with PHP 4. + Added features: - Support debugging into phars. - Basic PHP 5.3 support. 2.0.3 2.0.3 stable stable 2008-04-09 BSD style Wed, Apr 09, 2008 - xdebug 2.0.3 = Fixed bugs: - Fixed bug #338: Crash with: xdebug.remote_handler=req. - Fixed bug #334: Code Coverage Regressions. - Fixed abstract method detection for PHP 5.3. - Fixed code coverage dead-code detection. - Ignore ZEND_ADD_INTERFACE, which is on a different line in PHP >= 5.3 for some weird reason. + Changes: - Added a CSS-class for xdebug's var_dump(). - Added support for the new E_DEPRECATED. 2.0.2 2.0.2 stable stable 2007-11-11 BSD style Sun, Nov 11, 2007 - xdebug 2.0.2 = Fixed bugs: - Fixed bug #325: DBGP: "detach" stops further sessions being established from Apache. - Fixed bug #321: Code coverage crashes on empty PHP files. - Fixed bug #318: Segmentation Fault in code coverage analysis. - Fixed bug #315: Xdebug crashes when including a file that doesn't exist. - Fixed bug #314: PHP CLI Error Logging thwarted when XDebug Loaded. - Fixed bug #300: Direction of var_dump(). - Always set the transaction_id and command. (Related to bug #313). 2.0.1 2.0.1 stable stable 2007-10-29 BSD style Sat, Oct 20, 2007 - xdebug 2.0.1 + Changes: - Improved code coverage performance dramatically. - PHP 5.3 compatibility (no namespaces yet though). = Fixed bugs: - Fixed bug #301: Loading would cause SIGBUS on Solaris 10 SPARC. (Patch by Sean Chalmers) - Fixed bug #300: Xdebug does not force LTR rendering for its tables. - Fixed bug #299: Computerized traces don't have a newline for return entries if memory limit is not enabled. - Fixed bug #298: xdebug_var_dump() doesn't handle entity replacements correctly concerning string length. - Fixed a memory free error related to remote debugging conditions. (Related to bug #297). 2.0.0 2.0.0 stable stable 2007-07-18 BSD style Wed, Jul 18, 2007 - xdebug 2.0.0 + Changes: - Put back the disabling of stack traces - apperently people were relying on this. This brings back xdebug_enable(), xdebug_disable() and xdebug_is_enabled(). - xdebug.collect_params is no longer a boolean setting. Although it worked fine, phpinfo() showed only just On or Off here. - Fixed the Xdebug version of raw_url_encode to not encode : and \. This is not necessary according to the RFCs and it makes debug breakpoints work on Windows. = Fixed bugs: - Fixed bug #291: Tests that use SPL do not skip when SPL is not available. - Fixed bug #290: Function calls leak memory. - Fixed bug #289: Xdebug terminates connection when eval() is run in the init stage. - Fixed bug #284: Step_over on breakpointed line made Xdebug break twice. - Fixed bug #283: Xdebug always returns $this with the value of last stack frame. - Fixed bug #282: %s is not usable for xdebug.profiler_output_name on Windows in all stack frames. - Fixed bug #280: var_dump() doesn't display key of array as expected. - Fixed bug #278: Code Coverage Issue. - Fixed bug #273: Remote debugging: context_get does not return context id. - Fixed bug #270: Debugger aborts when PHP's eval() is encountered. - Fixed bug #265: XDebug breaks error_get_last() . - Fixed bug #261: Code coverage issues by overloading zend_assign_dim. + DBGP: - Added support for "breakpoint_languages". 2.0.0RC4 2.0.0RC4 beta beta 2007-05-17 BSD style Wed, May 17, 2007 - xdebug 2.0.0rc4 + Changes: - Use microseconds instead of a tenths of microseconds to avoid confusion in profile information. - Changed xdebug.profiler_output_name and xdebug.trace_output_name to use modifier tags: %c = crc32 of the current working directory %p = pid %r = random number %s = script name %t = timestamp (seconds) %u = timestamp (microseconds) %H = $_SERVER['HTTP_HOST'] %R = $_SERVER['REQUEST_URI'] %S = session_id (from $_COOKIE if set) %% = literal % = Fixed bugs: - Fixed bug #255: Call Stack Table doesn't show Location on Windows. - Fixed bug #251: Using the source command with an invalid filename returns unexpected result. - Fixed bug #243: show_exception_trace="0" ignored. - Fixed bug #241: Crash in xdebug_get_function_stack(). - Fixed bug #240: Crash with xdebug.remote_log on Windows. - Fixed a segfault in rendering stack traces to error logs. - Fixed a bug that prevented variable names from being recorded for remote debug session while xdebug.collect_vars was turned off. - Fixed xdebug_dump_superglobals() in case no super globals were configured. - Removed functions: - Removed support for Memory profiling as that didn't work properly. - Get rid of xdebug.default_enable setting and associated functions: xdebug_disable() and xdebug_enable(). + Added features: - Implemented support for four different xdebug.collect_params settings for stack traces and function traces. - Allow to trigger profiling by the XDEBUG_PROFILE cookie. + DBGP: - Correctly add namespace definitions to XML. - Added the xdebug namespace that adds extra information to breakpoints if available. - Stopped the use of >error> elements for exception breakpoints, as that violates the protocol. 2.0.0RC3 2.0.0RC3 beta beta 2007-01-31 BSD style Wed, Jan 31, 2007 - xdebug 2.0.0rc3 + Changes: - Removed the bogus "xdebug.allowed_clients" setting - it was not implemented. - Optimized used variable collection by switching to a linked list instead of a hash. This is about 30% faster, but it needed a quick conversion to hash in the case the information had to be shown to remove duplicate variable names. = Fixed bugs: - Fixed bug #232: PHP log_errors functionality lost after enabling xdebug error handler when CLI is used. - Fixed problems with opening files - the filename could cause double free issues. - Fixed memory tracking as memory_limit is always enabled in PHP 5.2.1 and later. - Fixed a segfault that occurred when creating printable stack traces and collect_params was turned off. 2.0.0RC2 2.0.0RC2 beta beta 2006-12-24 BSD style Sun, Dec 24, 2006 - xdebug 2.0.0rc2 + Added new features: - Implemented the "xdebug.var_display_max_children" setting. The default is set to 128 children. - Added types to fancy var dumping function. - Implemented FR #210: Add a way to stop the debug session without having to execute a script. The GET/POST parameter "XDEBUG_SESSION_STOP_NO_EXEC" works in the same way as XDEBUG_SESSION_STOP, except that the script will not be executed. - DBGP: Allow postmortem analysis. - DBGP: Added the non-standard function xcmd_profiler_name_get. + Changes: - Fixed the issue where xdebug_get_declared_vars() did not know about variables there are in the declared function header, but were not used in the code. Due to this change expected arguments that were not send to a function will now show up as ??? in stack and function traces in PHP 5.1 and PHP 5.2. - Allow xdebug.var_display_max_data and xdebug.var_display_max_depth settings of -1 which will unlimit those settings. - DBGP: Sort super globals in Globals overview. - DBGP: Fixed a bug where error messages where not added upon errors in the protocol. - DBGP: Change context 1 from globals (superglobals + vars in bottom most stack frame) to just superglobals. = Fixed bugs: - Fixed linking error on AIX by adding libm. - Fixed dead code analysis for THROW. - Fixed oparray prefill caching for code coverage. - Fixed the xdebug.remote_log feature work. - DBGP: Fixed a bug where $this did not appear in the local scoped context. - DBGP: Reimplemented property_set to use the same symbol fetching function as property_get. We now only use eval in case no type (-t) argument was given. - DBGP: Fixed some issues with finding out the classname, which is important for fetching private properties. - DBGP: Fixed usage of uninitialized memory that prevented looking up numerical array keys while fetching array elements not work properly. - Fixed bug #228: Binary safety for stream output and property fetches. - Fixed bug #227: The SESSION super global does not show up in the Globals scope. - Fixed bug #225: xdebug dumps core when protocol is GDB. - Fixed bug #224: Compile failure on Solaris. - Fixed bug #219: Memory usage delta in traces don't work on PHP 5.2.0. - Fixed bug #215: Cannot retrieve nested arrays when the array key is a numeric index. - Fixed bug #214: The depth level of arrays was incorrectly checked so it would show the first page of a level too deep as well. - Fixed bug #213: Dead code analysis doesn't take catches for throws into account. - Fixed bug #211: When starting a new session with a different idekey, the cookie is not updated. - Fixed bug #209: Additional remote debugging session started when triggering shutdown function. - Fixed bug #208: Socket connection attempted when XDEBUG_SESSION_STOP. - Fixed PECL bug #8989: Compile error with PHP 5 and GCC 2.95. 2.0.0rc1 2.0.0rc1 beta beta 2006-10-08 BSD style + Added new features: - Implemented FR #70: Provide optional depth on xdebug_call_* functions. - Partially implemented FR #50: Resource limiting for variable display. By default only two levels of nested variables and max string lengths of 512 are shown. This can be changed by setting the ini settings xdebug.var_display_max_depth and xdebug.var_display_max_data. - Implemented breakpoints for different types of PHP errors. You can now set an 'exception' breakpoint on "Fatal error", "Warning", "Notice" etc. This is related to bug #187. - Added the xdebug_print_function_trace() function to display a stack trace on demand. - Reintroduce HTML tracing by adding a new tracing option "XDEBUG_TRACE_HTML" (4). - Made xdebug_stop_trace() return the trace file name, so that the following works: <?php echo file_get_contents( xdebug_stop_trace() ); ?> - Added the xdebug.collect_vars setting to tell Xdebug to collect information about which variables are used in a scope. Now you don't need to show variables with xdebug.show_local_vars anymore for xdebug_get_declared_vars() to work. - Make the filename parameter to the xdebug_start_trace() function optional. If left empty it will use the same algorithm to pick a filename as when you are using the xdebug.auto_trace setting. + Changes: - Implemented dead code analysis during code coverage for: * abstract methods. * dead code after return, throw and exit. * implicit returns when a normal return is present. - Improved readability of stack traces. - Use PG(html_errors) instead of checking whether we run with CLI when deciding when to use HTML messages or plain text messages. = Fixed bugs: - Fixed bug #203: PHP errors with HTML content processed incorrectly. This patch backs out the change that was made to fix bug #182. - Fixed bug #198: Segfault when trying to use a non-existing debug handler. - Fixed bug #197: Race condition fixes created too many files. - Fixed bug #196: Profile timing on Windows does not work. - Fixed bug #195: CLI Error after debugging session. - Fixed bug #193: Compile problems with PHP 5.2. - Fixed bug #191: File/line breakpoints are case-sensitive on Windows. - Fixed bug #181: Xdebug doesn't handle uncaught exception output correctly. - Fixed bug #173: Coverage produces wrong coverage. - Fixed a typo that prevented the XDEBUG_CONFIG option "profiler_enable" from working. 2.0.0beta6 2.0.0beta6 beta beta 2006-06-30 BSD style + Added new features: - Implemented FR #137: feature_get for general commands doesn't have a text field. - Implemented FR #131: XDebug needs to implement paged child object requests. - Implemented FR #124: Add backtrace dumping information when exception thrown. - Implemented FR #70: Add feature_get breakpoint_types. - Added profiling aggregation functions (patch by Andrei Zmievski) - Implemented the "timestamp" option for the xdebug.trace_output_name and xdebug.profiler_output_name settings. - Added the xdebug.remote_log setting that allows you to log debugger communication to a log file for debugging. This can also be set through the "remote_log" element in the XDEBUG_CONFIG environment variable. - Added a "script" value to the profiler_output_name option. This will write the profiler output to a filename that consists of the script's full path (using underscores). ie: /var/www/index.php becomes var_www_index_php_cachegrind.out. (Patch by Brian Shire). - DBGp: Implemented support for hit conditions for breakpoints. - DBGp: Added support for conditions for file/line breakpoints. - DBGp: Added support for hit value checking to file/line breakpoints. - DBGp: Added support for "exception" breakpoints. + Performance improvements: - Added a cache that prevents the code coverage functionality from running a "which code is executable check" on every function call, even if they were executed multiple times. This should speed up code coverage a lot. - Speedup Xdebug but only gathering information about variables in scopes when either remote debugging is used, or show_local_vars is enabled. = Fixed bugs: - Fixed bug #184: problem with control chars in code traces - Fixed bug #183: property_get -n $this->somethingnonexistent crashes the debugger. - Fixed bug #182: Errors are not html escaped when being displayed. - Fixed bug #180: collected includes not shown in trace files. (Patch by Cristian Rodriguez) - Fixed bug #178: $php_errormsg and Track errors unavailable. - Fixed bug #177: debugclient fails to compile due to Bison. - Fixed bug #176: Segfault using SplTempFileObject. - Fixed bug #173: Xdebug segfaults using SPL ArrayIterator. - Fixed bug #171: set_time_limit stack overflow on 2nd request. - Fixed bug #168: Xdebug's DBGp crashes on an eval command where the result is an array. - Fixed bug #125: show_mem_delta does not calculate correct negative values on 64bit machines. - Fixed bug #121: property_get -n $r[2] returns the whole hash. - Fixed bug #111: xdebug does not ignore set_time_limit() function during debug session. - Fixed bug #87: Warning about headers when "register_shutdown_function" used. - Fixed PECL bug #6940 (XDebug ignores set_time_limit) - Fixed Komodo bug 45484: no member data for objects in PHP debugger. - Suppress NOP/EXT_NOP from being marked as executable code with Code Coverage. 2.0.0beta5 2.0.0beta5 beta beta 2005-12-31 BSD style + Added new features: - Implemented FR #161: var_dump doesn't show lengths for strings. - Implemented FR #158: Function calls from the {main} scope always have the line number 0. - Implemented FR #156: it's impossible to know the time taken by the last func call in xdebug trace mode 0. - Implemented FR #153: xdebug_get_declared_vars(). = Fixed bugs: - Fixed shutdown crash with ZTS on Win32 - Fixed bad memory leak when a E_ERROR of exceeding memory_limit was thrown. - Fixed bug #154: GCC 4.0.2 optimizes too much out with -O2. - Fixed bug #141: Remote context_get causes segfault. 2.0.0beta4 2.0.0beta4 beta beta 2005-09-24 BSD style + Added new features: - Added xdebug_debug_zval_stdout(). - Added xdebug_get_profile_filename() function which returns the current profiler dump file. - Updated for latest 5.1 and 6.0 CVS versions of PHP. - Added FR #148: Option to append to cachegrind files, instead of overwriting. - Implemented FR #114: Rename tests/*.php to tests/*.inc - Changed features: - Allow "xdebug.default_enable" to be set everywhere. = Fixed bugs: - DBGP: Xdebug should return "array" with property get, which is defined in the typemap to the common type "hash". - Fixed bug #142: xdebug crashes with implicit destructor calls. - Fixed bug #136: The "type" attribute is missing from stack_get returns. - Fixed bug #133: PHP scripts exits with 0 on PHP error. - Fixed bug #132: use of eval causes a segmentation fault. 2.0.0beta3 2.0.0beta3 beta beta 2005-05-12 BSD style + Added new features: - Added the possibility to trigger the profiler by setting "xdebug.profiler_enable_trigger" to 1 and using XDEBUG_PROFILE as a get parameter. = Fixed bugs: - Fixed a segfault for when an attribute value is NULL on XML string generation. - Fixed bug #118: Segfault with exception when remote debugging. - Fixed bug #117: var_dump dows not work with "private". - Fixed bug #109: DBGP's eval will abort the script when the eval statement is invalid. - Fixed bug #108: log_only still displays some text for errors in included files. - Fixed bug #107: Code Coverage only detects executable code in used functions and classes. - Fixed bug #103: crash when running the DBGp command 'eval' on a global variable - Fixed bug #95: Segfault when deinitializing Xdebug module. (Patch by Maxim Poltarak <demiurg@gmail.com>) 2.0.0beta2 2.0.0beta2 beta beta 2004-11-28 BSD style + Added new features: - DBGP: Added error messages to returned errors (in most cases) + Added new functions: - xdebug_debug_zval() to debug zvals by printing its refcounts and is_ref values. = Changed features: - xdebug_code_coverage_stop() will now clean up the code coverage array, unless you specify FALSE as parameter. - The proper Xdebug type is "hash" for associative arrays. - Extended the code-coverage functionality by returning lines with executable code on them, but where not executed with a count value of -1. = Fixed bugs: - DBGP: Make property_get and property_value finally work as they should, including retrieving information from different depths then the most top stack frame. - DBGP: Fix eval'ed $varnames in property_get. - DBGP: Support the -d option for property_get. - Fixed the exit handler hook to use the new "5.1" way of handling it; which fortunately also works with PHP 5.0. - Fixed bug #102: Problems with configure for automake 1.8. - Fixed bug #101: crash with set_exeception_handler() and uncatched exceptions. - Fixed bug #99: unset variables return the name as a string with property_get. - Fixed bug #98: 'longname' attribute not returned for uninitialized property in context_get request. - Fixed bug #94: xdebug_sprintf misbehaves with x86_64/glibc-2.3.3 - Fixed bug #93: Crash in lookup_hostname on x86_64 - Fixed bug #92: xdebug_disable() doesn't disable the exception handler. - Fixed bug #68: Summary not written when script ended with "exit()". 2.0.0beta1 2.0.0beta1 beta beta 2004-09-15 BSD style + Added new features: - Added support for the new DBGp protocol for communicating with the debug engine. - A computerized trace format for easier parsing by external programs. - The ability to set remote debugging features via the environment. This allows an IDE to emulate CGI and still pass the configuration through to the debugger. In CGI mode, PHP does not allow -d arguments. - Reimplementation of the tracing code, you can now only trace to file; this greatly enhances performance as no string representation of variables need to be kept in memory any more. - Re-implemented profiling support. Xdebug outputs information the same way that cachegrind does so it is possible to use Kcachegrind as front-end. - Xdebug emits warnings when it was not loaded as a Zend extension. - Added showing private, protected and public to the fancy var_dump() replacement function. - Added the setting of the TCP_NODELAY socket option to stop delays in transferring data to the remote debugger client. (Patch by Christof J. Reetz) + DebugClient: Added setting for port to listen on and implemented running the previous command when pressing just enter. + Added new functions: - xdebug_get_stack_depth() to return the current stack depth level. - xdebug_get_tracefile_name() to retrieve the name of the tracefile. This is useful in case auto trace is enabled and you want to clean the trace file. - xdebug_peak_memory_usage() which returns the peak memory used in a script. (Only works when --enable-memory-limit was enabled) + Added feature requests: - FR #5: xdebug_break() function which interupts the script for the debug engine. - FR #30: Dump current scope information in stack traces on error. - FR #88: Make the url parameter XDEBUG_SESSION_START optional. So it can be disabled and the user does not need to add it. + Added new php.ini settings: - xdebug.auto_trace_file: to configure a trace file to write to as addition to the xdebug.auto_trace setting which just turns on tracing. - xdebug.collect_includes: separates collecting names of include files from the xdebug.collect_params setting. - xdebug.collect_return: showing return values in traces. - xdebug.dump_global: with which you can turn off dumping of super globals even in you have that configured. - xdebug.extended_info: turns off the generation of extended opcodes that are needed for stepping and breakpoints for the remote debugger. This is useful incase you want to profile memory usage as the generation of this extended info increases memory usage of oparrrays by about 33%. - xdebug.profiler_output_dir: profiler output directory. - xdebug.profiler_enable: enable the profiler. - xdebug.show_local_vars: turn off the showing of local variables in the top most stack frame on errors. - xdebug.show_mem_delta: show differences between current and previous memory usage on a function call level. - xdebug.trace_options: to configure extra options for trace dumping: o XDEBUG_TRACE_APPEND option (1) = Changed features: - xdebug_start_trace() now returns the filename of the tracefile (.xt is added to the requested name). - Changed default debugging protocol to dbgp instead of gdb. - Changed default debugger port from 17869 to 9000. - Changed trace file naming: xdebug.trace_output_dir is now used to configure a directory to dump automatic traces; the trace file name now also includes the pid (xdebug.trace_output_name=pid) or a crc32 checksum of the current working dir (xdebug.trace_output_name=crc32) and traces are not being appended to an existing file anymore, but simply overwritten. - Removed $this and $GLOBALS from showing variables in the local scope. - Removed functions: - xdebug_get_function_trace/xdebug_dump_function_trace() because of the new idea of tracing. = Fixed bugs: - Fixed bug #89: var_dump shows empty strings garbled. - Fixed bug #85: Xdebug segfaults when no idekey is set. - Fixed bug #83: More than 32 parameters functions make xdebug crash. - Fixed bug #75: xdebug's var_dump implementation is not binary safe. - Fixed bug #73: komodo beta 4.3.7 crash. - Fixed bug #72: breakpoint_get returns wrong structure. - Fixed bug #69: Integer overflow in cachegrind summary. - Fixed bug #67: Filenames in Xdebug break URI RFC with spaces. - Fixed bug #64: Missing include of xdebug_compat.h. - Fixed bug #57: Crash with overloading functions. - Fixed bug #54: source command did not except missing -f parameter. - Fixed bug #53: Feature get misusing the supported attribute. - Fixed bug #51: Only start a debug session if XDEBUG_SESSION_START is passed as GET or POST parameter, or the DBGP_COOKIE is send to the server. Passing XDEBUG_SESSION_STOP as GET/POST parameter will end the debug session and removes the cookie again. The cookie is also passed to the remote handler backends; for DBGp it is added to the <init> packet. - Fixed bug #49: Included file's names should not be stored by address. - Fixed bug #44: Script time-outs should be disabled when debugging. = Fixed bug #36: GDB handler using print causes segfault with wrong syntax - Fixed bug #33: Implemented the use of the ZEND_POST_DEACTIVATE hook. Now we can handle destructors safely too. - Fixed bug #32: Unusual dynamic variables cause xdebug to crash. 1.3.1 1.3.1 stable stable 2004-04-06 BSD style = Fixed profiler to aggregate class/method calls correctly. (Robert Beenen) = Fixed debugclient to initialize socket structure correctly. (Brandon Philips and David Sklar) = GDB: Fixed bug where the source file wasn't closed after a "source" command. (Derick) 1.3.0 1.3.0 stable stable 2003-09-17 BSD style = Fixed segfault where a function name didn't exist in case of a "call_user_function". (Derick) = Fixed reading a filename in case of an callback to a PHP function from an internal function (like "array_map()"). (Derick) 1.3.0rc1 1.3.0rc1 beta beta 2003-09-17 BSD style = Fixed bug with wrong file names for functions called from call_user_*(). (Derick) + Added the option "dump_superglobals" to the remote debugger. If you set this option to 0 the "show-local" and similar commands will not return any data from superglobals anymore. (Derick) = Fixed bug #2: "pear package" triggers a segfault. (Derick) = Fixed crash bug when a function had sprintf style parameters (ie. strftime()). (Derick) + Added "id" attribute to <var /> elements in responses from the remove debugger when the response method is XML. This makes it possible to distinguish between unique elements by use of recursion for example. (Derick) = Improved performance greatly by doing lazy folding of variables outside trace mode. (Derick) = Fixed a bug with "quit", if it was used it disabled the extension for the current process. (Derick) + Added the "full" argument to the remote command "backtrace". When this argument is passed, the local variables will be returned to for each frame in the stack. (Derick) + Implemented xdebug_time_index() which returns the time passed since the start of the script. This change also changes the output of the tracing functions as the start time will no longer be the first function call, but the real start time of the script. (Derick) + Implemented the "show-local" command (shows all local variables in the current scope including all contents). (Derick) + Implemented conditions for breakpoints in the "break" command. (Derick) 1.2.0 1.2.0 stable stable 2003-04-21 BSD style = Fixed compilation on MacOSX. (Derick) 1.2.0rc2 1.2.0rc2 beta beta 2003-04-15 BSD style = Fixed handling Windows paths in the debugger. (Derick) = Fixed getting zvals out of Zend Engine 2. (Derick) 1.2.0rc1 1.2.0rc1 beta beta 2003-04-06 BSD style + Added code coverage functions to check which lines and how often they were touched during execution. (Derick) + Made Xdebug compatible with Zend Engine 2. (Derick) + Added dumping of super globals on errors. (Harald Radi) + Added XML protocol for the debugger client. (Derick) = Fixed handling of "continue" (so that it also continues with the script). (Derick) + Additions to the remote debugger: "eval" (evaluate any PHP code from the debugger client). (Derick) + Added profiling support to xdebug. This introduces 3 new functions, xdebug_start_profiling() that begins profiling process, xdebug_stop_profiling() that ends the profiling process and xdebug_dump_function_trace() that dumps the profiling data. (Ilia) + Implemented the "kill" (kills the running script) and "delete" (removes a breakpoint on a specified element) command. (Derick) 1.1.0 1.1.0 stable stable 2002-11-11 BSD style + Implemented the "list" (source listing), "print" (printing variable contents), "show" (show all variables in the scope), "step" (step through execution), "pwd" (print working directory), "next" (step over) and "finish" (step out) commands for the remote debugger. (Derick) = Fixed lots of small bugs, under them memory leaks and crash bugs. (Derick) 1.1.0pre2 1.1.0pre2 beta beta 2002-10-29 BSD style + Implemented class::method, object->method and file.ext:line style breakpoints. (Derick) + Added xdebug.collect_params setting. If this setting is on (the default) then Xdebug collects all parameters passed to functions, otherwise they are not collected at all. (Derick) + Implemented correct handling of include/require and eval. (Derick) 1.1.0pre1 1.1.0pre1 beta beta 2002-10-22 BSD style + Added automatic starting of function traces (xdebug.auto_trace, defaulting to "off"). (Derick) - Xdebug no longer supports PHP versions below PHP 4.3.0pre1. (Derick) + Added gdb compatible debugger handler with support for simple (function only) breakpoints. (Derick) = Implemented a new way to get class names and file names. (Derick, Thies C. Arntzen <thies@thieso.net>) + Added time-index and memory footprint to CLI dumps. (Derick) + Implemented remote debugger handler abstraction. (Derick) + Added a php3 compatible debugger handler. (Derick) 1.0.0rc1 1.0.0rc1 beta beta 2002-09-01 BSD style + Implemented gathering of parameters to internal functions (only available in combination with PHP 4.3.0-dev). (Derick) = Implemented a new way to get class names and file names. (Derick, Thies C. Arntzen >thies@thieso.net<) + Added support for error messages with stack trace in syslog. (Sergio Ballestrero >s.ballestrero@planetweb.it<) = Windows compilation fixes. (Derick) 0.9.0 0.9.0 beta beta 2002-06-16 BSD style = Fixed a memory leak in delayed included files. (Derick) - Added support for PHP 4.1.2. (Derick) = Rewrote xdebug_get_function_stack() and xdebug_get_function_trace() to return data in multidimensional arrays. (Derick) = Fixed compiling without memory limit enabled (Sander Roobol, Derick) - Add support for classnames, variable include files and variable function names. (Derick) - Implemented links to the PHP Manual in traces. (Derick) - Added timestamps and memory usage to function traces. (Derick) = Fixed crash when using an user defined session handler. (Derick) + Implemented variable function names ($a = 'foo'; $f();) for use in traces. (Derick) 0.8.0 0.8.0 beta beta 2002-05-26 BSD style + Implemented much better parameter tracing for user defined functions. (Derick) = Renamed xdebug_get_function_trace() to xdebug_dump_function_trace(). (Derick) = Implemented new xdebug_get_function_trace() to return the function trace in an array. (Derick) + Added a parameter to xdebug_start_trace(). When this parameter is used, xdebug will dump a function trace to the filename which this parameter speficies. (Derick) - Fix a problem with nested member functions. (Derick) = Make configure scripts work with PHP 4.2.x. (Derick) + Implemented handling single-dimensional constant arrays passed to a function. (Derick) = Fix function traces in windows. (Derick) + Implemented function traces, which you can start and stop with xdebug_start_trace() and xdebug_stop_trace(). You can view the trace by using the return array from xdebug_get_function_trace(). (Derick) = Fixed segfaults with xdebug_call_*(). (Derick) 0.7.0 0.7.0 beta beta 2002-05-08 BSD style + Implemented handling of static method calls (foo::bar). (Derick) + Added correct handling of include(_once)/require(_once) and eval(). (Derick) + Added ini setting to change the default setting for enabling showing enhanced error messages. (Defaults to "On"). (Derick) + Added the functions xdebug_enable() and xdebug_disable() to change the showing of stack traces from within your code. (Derick) = Fixed the extension to show all errors. (Derick) + Implemented xdebug_memory_usage() which returns the memory in use by PHPs engine. (Derick) xdebug-3.4.3/contrib/tracefile-analyser.php0000664000175000017500000001444115011062311020202 0ustar derickderick 4 ) { showUsage(); } $fileName = $argv[1]; $sortKey = 'time-own'; $elements = 25; if ( $argc > 2 ) { $sortKey = $argv[2]; if ( !in_array( $sortKey, array( 'calls', 'time-inclusive', 'memory-inclusive', 'time-own', 'memory-own' ) ) ) { showUsage(); } } if ( $argc > 3 ) { $elements = (int) $argv[3]; } $o = new drXdebugTraceFileParser( $argv[1] ); $o->parse(); $functions = $o->getFunctions( $sortKey ); // find longest function name $maxLen = 0; foreach( $functions as $name => $f ) { if ( strlen( $name ) > $maxLen ) { $maxLen = strlen( $name ); } } $maxLen = max( $maxLen, 8 ); echo "Showing the {$elements} most costly calls sorted by '{$sortKey}'.\n\n"; echo " ", str_repeat( ' ', $maxLen - 8 ), " Inclusive Own\n"; echo "function", str_repeat( ' ', $maxLen - 8 ), "#calls time memory time memory\n"; echo "--------", str_repeat( '-', $maxLen - 8 ), "----------------------------------------\n"; // display functions $c = 0; foreach( $functions as $name => $f ) { $c++; if ( $c > $elements ) { break; } printf( "%-{$maxLen}s %5d %3.4f %8d %3.4f %8d\n", $name, $f['calls'], $f['time-inclusive'], $f['memory-inclusive'], $f['time-own'], $f['memory-own'] ); } function showUsage() { echo "usage:\n\tphp run-cli tracefile [sortkey] [elements]\n\n"; echo "Allowed sortkeys:\n\tcalls, time-inclusive, memory-inclusive, time-own, memory-own\n"; die(); } class drXdebugTraceFileParser { protected $handle; /** * Stores the last function, time and memory for the entry point per * stack depth. int=>array(string, float, int). */ protected $stack; /** * Stores per function the total time and memory increases and calls * string=>array(float, int, int) */ protected $functions; /** * Stores which functions are on the stack */ protected $stackFunctions; public function __construct( $fileName ) { $this->handle = fopen( $fileName, 'r' ); if ( !$this->handle ) { throw new Exception( "Can't open '$fileName'" ); } $this->stack[-1] = array( '', 0, 0, 0, 0 ); $this->stack[ 0] = array( '', 0, 0, 0, 0 ); $this->stackFunctions = array(); $header1 = fgets( $this->handle ); $header2 = fgets( $this->handle ); if ( !preg_match( '@Version: [23].*@', $header1 ) || !preg_match( '@File format: [2-4]@', $header2 ) ) { echo "\nThis file is not an Xdebug trace file made with format option '1' and version 2 to 4.\n"; showUsage(); } } public function parse() { echo "\nparsing...\n"; $c = 0; $size = fstat( $this->handle ); $size = $size['size']; $read = 0; while ( !feof( $this->handle ) ) { $buffer = fgets( $this->handle, 4096 ); $read += strlen( $buffer ); $buffer = rtrim( $buffer, PHP_EOL ); $this->parseLine( $buffer ); $c++; if ( $c % 25000 === 0 ) { printf( " (%5.2f%%)\n", ( $read / $size ) * 100 ); } } echo "\nDone.\n\n"; } private function parseLine( $line ) { /* if ( preg_match( '@^Version: (.*)@', $line, $matches ) ) { } else if ( preg_match( '@^File format: (.*)@', $line, $matches ) ) { } else if ( preg_match( '@^TRACE.*@', $line, $matches ) ) { } else // assume a normal line */ { $parts = explode( "\t", $line ); if ( count( $parts ) < 5 ) { return; } $depth = $parts[0]; $funcNr = $parts[1]; $time = $parts[3]; $memory = $parts[4]; if ( $parts[2] == '0' ) // function entry { $funcName = $parts[5]; $intFunc = $parts[6]; $this->stack[$depth] = array( $funcName, $time, $memory, 0, 0 ); array_push( $this->stackFunctions, $funcName ); } else if ( $parts[2] == '1' ) // function exit { list( $funcName, $prevTime, $prevMem, $nestedTime, $nestedMemory ) = $this->stack[$depth]; // collapse data onto functions array $dTime = $time - $prevTime; $dMemory = $memory - $prevMem; if ( ! array_key_exists( $depth - 1, $this->stack ) ) { $this->stack[$depth - 1] = array( '', 0, 0, 0, 0 ); } $this->stack[$depth - 1][3] += $dTime; $this->stack[$depth - 1][4] += $dMemory; array_pop( $this->stackFunctions ); $this->addToFunction( $funcName, $dTime, $dMemory, $nestedTime, $nestedMemory ); } } } protected function addToFunction( $function, $time, $memory, $nestedTime, $nestedMemory ) { if ( !isset( $this->functions[$function] ) ) { $this->functions[$function] = array( 0, 0, 0, 0, 0 ); } $elem = &$this->functions[$function]; $elem[0]++; if ( !in_array( $function, $this->stackFunctions ) ) { $elem[1] += $time; $elem[2] += $memory; $elem[3] += $nestedTime; $elem[4] += $nestedMemory; } } public function getFunctions( $sortKey = null ) { $result = array(); foreach ( $this->functions as $name => $function ) { $result[$name] = array( 'calls' => $function[0], 'time-inclusive' => $function[1], 'memory-inclusive' => $function[2], 'time-children' => $function[3], 'memory-children' => $function[4], 'time-own' => $function[1] - $function[3], 'memory-own' => $function[2] - $function[4] ); } if ( $sortKey !== null ) { uasort( $result, function( $a, $b ) use ( $sortKey ) { return ( $a[$sortKey] > $b[$sortKey] ) ? -1 : ( $a[$sortKey] < $b[$sortKey] ? 1 : 0 ); } ); } return $result; } } ?> xdebug-3.4.3/contrib/xt.vim0000664000175000017500000000427615011062311015074 0ustar derickderick" Vim syntax file " Language: Xdebug trace files (context or unified) " Maintainer: Derick Rethans " Last Change: 2010 Jun 06 " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded if version < 600 syntax clear elseif exists("b:current_syntax") finish endif syn match begin "^TRACE START" syn match end "^TRACE END" syn match date "\[.*\]" syn match min_memory "+\d\+" syn match pls_memory "-\d\+" syn match nll_memory "+0" syn match level "->" syn match lineno ":\d\+$" syn match result ">=>.\+" syn match assignment "=> \$[a-z]\+\(->[a-z]\+\)*\ .*=" syn match assignment "=> \$[a-z]\+\['\([a-z]\+\)*'\]\ .*=" syn match methodcall "\k\+->" syn match staticcall "\k\+::" syn match functionb "\k\+(" syn match functione ") " syn match main "{main}()" syn match include "include\(_once\)\=('.\+')" " Define the default highlighting. " For version 5.7 and earlier: only when not done already " For version 5.8 and later: only when an item doesn't have highlighting yet if version >= 508 || !exists("did_diff_syntax_inits") if version < 508 let did_diff_syntax_inits = 1 command -nargs=+ HiLink hi link else command -nargs=+ HiLink hi def link endif HiLink begin Label HiLink end Label HiLink date Label HiLink assignment Label HiLink level SpecialChar HiLink result Constant HiLink min_memory Constant HiLink pls_memory Structure HiLink nll_memory Comment HiLink main Structure HiLink include Structure HiLink lineno Delimiter HiLink methodcall Function HiLink staticcall Function HiLink functionb Function HiLink functione Function delcommand HiLink endif let b:current_syntax = "xt" :set foldmethod=expr :set foldlevel=9999 " check whether xdebug.show_mem_delta=1 is set let s:startColumn = getline(2)[24:25]=='->' ? 22 : 31 fu! TraceFoldLevel(ln) for i in range(10, 1, -1) if eval('getline(a:ln)['.s:startColumn.':]')=~'^[ ]\{'.(2*i).'\}[^\\s]' return i endfor return 0 endfu :set foldexpr=TraceFoldLevel(v:lnum) " vim: ts=8 sw=2 xdebug-3.4.3/m4/clocks.m40000664000175000017500000000210415011062311014310 0ustar derickderickAC_DEFUN([AC_XDEBUG_CLOCK], [ have_clock_gettime=no AC_MSG_CHECKING([for clock_gettime]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);]])], [ have_clock_gettime=yes AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) if test "$have_clock_gettime" = "no"; then AC_MSG_CHECKING([for clock_gettime in -lrt]) SAVED_LIBS="$LIBS" LIBS="$LIBS -lrt" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);]])], [ have_clock_gettime=yes AC_MSG_RESULT([yes]) ], [ LIBS="$SAVED_LIBS" AC_MSG_RESULT([no]) ]) fi if test "$have_clock_gettime" = "yes"; then AC_DEFINE([HAVE_XDEBUG_CLOCK_GETTIME], 1, [do we have clock_gettime?]) fi AC_CHECK_FUNC(clock_gettime_nsec_np, [AC_DEFINE([HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP], 1, [do we have clock_gettime_nsec_np?])], [AC_DEFINE([HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP], 0, [do we have clock_gettime_nsec_np?])] ) AC_CHECK_FUNCS(gettimeofday) ]) xdebug-3.4.3/m4/pkg.m40000664000175000017500000002401015011062311013613 0ustar derickderick# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurrence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR xdebug-3.4.3/src/base/base.c0000664000175000017500000014226115011062311015040 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "TSRM.h" #include "php_globals.h" #include "zend_closures.h" #include "zend_exceptions.h" #if PHP_VERSION_ID >= 80200 # include "zend_attributes.h" #endif #include "zend_interfaces.h" #if PHP_VERSION_ID >= 80100 # include "base_private.h" # include "Zend/zend_fibers.h" # include "Zend/zend_observer.h" #endif #include "php_xdebug.h" #include "php_xdebug_arginfo.h" #include "base.h" #include "filter.h" #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT # include "ctrl_socket.h" #endif #include "develop/develop.h" #include "develop/stack.h" #include "gcstats/gc_stats.h" #include "lib/lib_private.h" #include "lib/log.h" #include "lib/var_export_line.h" #include "lib/var.h" #include "profiler/profiler.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) /* True globals for overloaded functions */ zif_handler orig_error_reporting_func = NULL; zif_handler orig_set_time_limit_func = NULL; zif_handler orig_pcntl_exec_func = NULL; zif_handler orig_pcntl_fork_func = NULL; zif_handler orig_exit_func = NULL; #if PHP_VERSION_ID >= 80100 void (*xdebug_old_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); void (*xdebug_new_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); static void xdebug_error_cb(int orig_type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); #else void (*xdebug_old_error_cb)(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message); void (*xdebug_new_error_cb)(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message); static void xdebug_error_cb(int orig_type, const char *error_filename, const uint32_t error_lineno, zend_string *message); #endif /* execution redirection functions */ zend_op_array* (*old_compile_file)(zend_file_handle* file_handle, int type); static void (*xdebug_old_execute_ex)(zend_execute_data *execute_data); static void (*xdebug_old_execute_internal)(zend_execute_data *current_execute_data, zval *return_value); /* error_cb and execption hook overrides */ void xdebug_base_use_original_error_cb(void); void xdebug_base_use_xdebug_error_cb(void); void xdebug_base_use_xdebug_throw_exception_hook(void); /* Forward declarations for function overides */ PHP_FUNCTION(xdebug_set_time_limit); PHP_FUNCTION(xdebug_error_reporting); PHP_FUNCTION(xdebug_pcntl_exec); PHP_FUNCTION(xdebug_pcntl_fork); PHP_FUNCTION(xdebug_exit); /* {{{ zend_op_array xdebug_compile_file (file_handle, type) * This function provides a hook for the execution of bananas */ static zend_op_array *xdebug_compile_file(zend_file_handle *file_handle, int type) { zend_op_array *op_array; op_array = old_compile_file(file_handle, type); if (!op_array) { return NULL; } xdebug_coverage_compile_file(op_array); xdebug_debugger_compile_file(op_array); return op_array; } /* }}} */ /* I don't like this API, but the function_stack_entry does not keep this as a * pointer, and hence we need two APIs for freeing :-S */ void xdebug_func_dtor_by_ref(xdebug_func *elem) { if (elem->function) { zend_string_release(elem->function); } if (elem->object_class) { zend_string_release(elem->object_class); } if (elem->scope_class) { zend_string_release(elem->scope_class); } if (elem->include_filename) { zend_string_release(elem->include_filename); } } void xdebug_func_dtor(xdebug_func *elem) { xdebug_func_dtor_by_ref(elem); xdfree(elem); } static void function_stack_entry_dtor(void *elem) { unsigned int i; function_stack_entry *e = elem; xdebug_func_dtor_by_ref(&e->function); if (e->filename) { zend_string_release(e->filename); } if (e->var) { for (i = 0; i < e->varc; i++) { if (e->var[i].name) { zend_string_release(e->var[i].name); } zval_ptr_dtor(&(e->var[i].data)); } xdfree(e->var); } if (e->declared_vars) { xdebug_llist_destroy(e->declared_vars, NULL); e->declared_vars = NULL; } if (e->profile.call_list) { xdebug_llist_destroy(e->profile.call_list, NULL); e->profile.call_list = NULL; } } int xdebug_include_or_eval_handler(XDEBUG_OPCODE_HANDLER_ARGS) { const zend_op *opline = execute_data->opline; zval *inc_filename; zval tmp_inc_filename; if (opline->extended_value != ZEND_EVAL) { return xdebug_call_original_opcode_handler_if_set(opline->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } inc_filename = xdebug_get_zval(execute_data, opline->op1_type, &opline->op1); /* If there is no inc_filename, we're just bailing out instead */ if (!inc_filename) { return xdebug_call_original_opcode_handler_if_set(opline->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } if (Z_TYPE_P(inc_filename) != IS_STRING) { tmp_inc_filename = *inc_filename; zval_copy_ctor(&tmp_inc_filename); convert_to_string(&tmp_inc_filename); inc_filename = &tmp_inc_filename; } /* Now let's store this info */ if (XG_BASE(last_eval_statement)) { zend_string_release(XG_BASE(last_eval_statement)); } XG_BASE(last_eval_statement) = zend_string_init(Z_STRVAL_P(inc_filename), Z_STRLEN_P(inc_filename), 0); if (inc_filename == &tmp_inc_filename) { zval_dtor(&tmp_inc_filename); } return xdebug_call_original_opcode_handler_if_set(opline->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } static int find_line_number_for_current_execute_point(zend_execute_data *edata) { zend_execute_data *ptr = edata; while (ptr && (!ptr->func || !ZEND_USER_CODE(ptr->func->type))) { ptr = ptr->prev_execute_data; } if (ptr && ptr->opline) { return ptr->opline->lineno; } return 0; } void xdebug_build_fname(xdebug_func *tmp, zend_execute_data *edata) { memset(tmp, 0, sizeof(xdebug_func)); if (edata && edata->func && edata->func == (zend_function*) &zend_pass_function) { tmp->type = XFUNC_ZEND_PASS; tmp->function = ZSTR_INIT_LITERAL("{zend_pass}", false); } else if (edata && edata->func) { tmp->type = XFUNC_NORMAL; if ((Z_TYPE(edata->This)) == IS_OBJECT) { tmp->type = XFUNC_MEMBER; if (edata->func->common.scope && strstr(edata->func->common.scope->name->val, "@anonymous") != NULL) { char *tmp_object_class = xdebug_sprintf( "{anonymous-class:%s:%d-%d}", edata->func->common.scope->info.user.filename->val, edata->func->common.scope->info.user.line_start, edata->func->common.scope->info.user.line_end ); tmp->object_class = zend_string_init(tmp_object_class, strlen(tmp_object_class), 0); xdfree(tmp_object_class); } else { if (edata->func->common.scope) { tmp->scope_class = zend_string_copy(edata->func->common.scope->name); } tmp->object_class = zend_string_copy(edata->This.value.obj->ce->name); } } else { if (edata->func->common.scope) { tmp->type = XFUNC_STATIC_MEMBER; tmp->object_class = zend_string_copy(edata->func->common.scope->name); } } if (edata->func->common.function_name) { if (edata->func->common.fn_flags & ZEND_ACC_CLOSURE) { tmp->function = xdebug_wrap_closure_location_around_function_name(&edata->func->op_array, edata->func->common.function_name); } else if (strncmp(ZSTR_VAL(edata->func->common.function_name), "call_user_func", 14) == 0) { zend_string *fname = NULL; int lineno = 0; if (edata->prev_execute_data && edata->prev_execute_data->func && edata->prev_execute_data->func->type == ZEND_USER_FUNCTION) { fname = edata->prev_execute_data->func->op_array.filename; } if (!fname) { function_stack_entry *tmp_fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); if (tmp_fse->filename) { fname = tmp_fse->filename; } } if (!fname) { /* It wasn't a special call_user_func after all */ goto normal_after_all; } lineno = find_line_number_for_current_execute_point(edata); tmp->function = zend_strpprintf( 0, "%s:{%s:%d}", ZSTR_VAL(edata->func->common.function_name), ZSTR_VAL(fname), lineno ); } else { normal_after_all: tmp->function = zend_string_copy(edata->func->common.function_name); } } else if ( edata && edata->func && edata->func->type == ZEND_EVAL_CODE && edata->prev_execute_data && edata->prev_execute_data->func && edata->prev_execute_data->func->common.function_name && ( (strncmp(edata->prev_execute_data->func->common.function_name->val, "assert", 6) == 0) || (strncmp(edata->prev_execute_data->func->common.function_name->val, "create_function", 15) == 0) ) ) { tmp->type = XFUNC_NORMAL; tmp->function = ZSTR_INIT_LITERAL("{internal eval}", false); } else if ( edata && edata->prev_execute_data && edata->prev_execute_data->func && edata->prev_execute_data->func->type == ZEND_USER_FUNCTION && edata->prev_execute_data->opline && edata->prev_execute_data->opline->opcode == ZEND_INCLUDE_OR_EVAL ) { switch (edata->prev_execute_data->opline->extended_value) { case ZEND_EVAL: tmp->type = XFUNC_EVAL; break; case ZEND_INCLUDE: tmp->type = XFUNC_INCLUDE; break; case ZEND_REQUIRE: tmp->type = XFUNC_REQUIRE; break; case ZEND_INCLUDE_ONCE: tmp->type = XFUNC_INCLUDE_ONCE; break; case ZEND_REQUIRE_ONCE: tmp->type = XFUNC_REQUIRE_ONCE; break; default: tmp->type = XFUNC_UNKNOWN; break; } } else if ( edata && edata->prev_execute_data ) { xdebug_build_fname(tmp, edata->prev_execute_data); } else { tmp->type = XFUNC_UNKNOWN; } } } // TODO: Remove #define XINI_DEV(v) (XG(settings.develop.v)) #define NO_VARIADIC INT_MAX #define DEBUG 0 static void collect_params_internal(function_stack_entry *fse, zend_execute_data *zdata, zend_op_array *op_array) { int i; int is_variadic = !!(zdata->func->common.fn_flags & ZEND_ACC_VARIADIC); int is_trampoline = !!(zdata->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE); int variadic_at_pos = NO_VARIADIC; int variadic_sensitive = 0; int names_expected = 0; int arguments_sent = 0; int arguments_storage = 0; arguments_sent = ZEND_CALL_NUM_ARGS(zdata); if (arguments_sent > USHRT_MAX) { return; } names_expected = zdata->func->internal_function.num_args; if (names_expected > arguments_sent) { names_expected = arguments_sent; } #if DEBUG fprintf(stderr, "\nF: %s\n - CALL_NUM_ARGS: %d, op_array->num_args: %d, is_variadic: %d, trampoline: %d\n", fse->function.function, ZEND_CALL_NUM_ARGS(zdata), op_array->num_args, is_variadic, is_trampoline); #endif /* If this function is variadic, we have an extra name field in arg_info, and also an extra * argument sent to the function. */ if (is_variadic && !is_trampoline) { names_expected++; } /* Pick the highest of "expected arguments" and "arguments given" (also * taking into account the extra one for variadics */ if (names_expected > arguments_sent) { arguments_storage = names_expected; } else { arguments_storage = arguments_sent; } fse->varc = arguments_storage; fse->var = xdmalloc(fse->varc * sizeof(xdebug_var_name)); #if DEBUG fprintf(stderr, " - names_expected: %d, arguments_sent: %d, arguments_storage: %d, fse->varc: %d\n", names_expected, arguments_sent, arguments_storage, fse->varc); #endif /* Initialise everything in storage */ for (i = 0; i < fse->varc; i++) { fse->var[i].name = NULL; ZVAL_UNDEF(&fse->var[i].data); fse->var[i].is_variadic = 0; } /* Collect Names */ for (i = 0; i < names_expected; i++) { if (op_array->arg_info[i].name) { fse->var[i].name = zend_string_init( zdata->func->internal_function.arg_info[i].name, strlen(zdata->func->internal_function.arg_info[i].name), 0 ); /* If an argument is a variadic, then we mark that on this 'name', * and also remember which position the variadic started */ if (ZEND_ARG_IS_VARIADIC(&op_array->arg_info[i]) && variadic_at_pos == NO_VARIADIC) { fse->var[i].is_variadic = 1; variadic_at_pos = i; } } } /* Collect Arguments */ for (i = 0; i < arguments_sent; i++) { #if PHP_VERSION_ID >= 80200 zend_attribute *attribute; #else void *attribute = NULL; #endif /* The index in ZEND_CALL_ARG is 1-based */ #if DEBUG fprintf(stderr, "Copying argument %d\n", i); #endif #if PHP_VERSION_ID >= 80200 attribute = zend_get_parameter_attribute_str( zdata->func->common.attributes, "sensitiveparameter", sizeof("sensitiveparameter") - 1, i ); #endif if (attribute && fse->var[i].is_variadic) { variadic_sensitive = 1; } # if DEBUG fprintf(stderr, "SENSTIVIVE %d ", attribute != NULL); # endif if ((variadic_sensitive || attribute != NULL) && !fse->var[i].is_variadic) { ZVAL_STRING(&(fse->var[i].data), "[Sensitive Parameter]"); } else { ZVAL_COPY(&(fse->var[i].data), ZEND_CALL_ARG(zdata, i + 1)); } #if DEBUG fprintf(stderr, "OK\n"); #endif } if (ZEND_CALL_INFO(zdata) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { zend_string *name; zval *param; int i = fse->varc; fse->varc += zend_hash_num_elements(zdata->extra_named_params); fse->var = xdrealloc(fse->var, fse->varc * sizeof(xdebug_var_name)); ZEND_HASH_FOREACH_STR_KEY_VAL(zdata->extra_named_params, name, param) { fse->var[i].name = zend_string_copy(name); ZVAL_COPY(&(fse->var[i].data), param); fse->var[i].is_variadic = 0; i++; } ZEND_HASH_FOREACH_END(); } #if DEBUG for (i = 0; i < fse->varc; i++) { fprintf(stderr, "%2d %-20s %c %s\n", i, fse->var[i].name ? ZSTR_VAL(fse->var[i].name) : "---", fse->var[i].is_variadic ? 'V' : ' ', xdebug_get_zval_value_line(&fse->var[i].data, 0, NULL)->d); } #endif } static void collect_params(function_stack_entry *fse, zend_execute_data *zdata, zend_op_array *op_array) { int i; int is_variadic = !!(zdata->func->common.fn_flags & ZEND_ACC_VARIADIC); int is_trampoline = !!(zdata->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE); int variadic_at_pos = NO_VARIADIC; int variadic_sensitive = 0; int names_expected = 0; int arguments_sent = 0; int arguments_storage = 0; arguments_sent = ZEND_CALL_NUM_ARGS(zdata); /* The op_array contains the number of * (named) arguments. */ names_expected = op_array->num_args; #if DEBUG fprintf(stderr, "\nF: %s\n - CALL_NUM_ARGS: %d, op_array->num_args: %d, is_variadic: %d, trampoline: %d\n", fse->function.function, ZEND_CALL_NUM_ARGS(zdata), op_array->num_args, is_variadic, is_trampoline); #endif /* If this function is variadic, we have an extra name field in arg_info, and also an extra * argument sent to the function. */ if (is_variadic && !is_trampoline) { names_expected++; arguments_sent++; } /* Pick the highest of "expected arguments" and "arguments given" (also * taking into account the extra one for variadics */ if (names_expected > arguments_sent) { arguments_storage = names_expected; } else { arguments_storage = arguments_sent; } fse->varc = arguments_storage; fse->var = xdmalloc(fse->varc * sizeof(xdebug_var_name)); #if DEBUG fprintf(stderr, " - names_expected: %d, arguments_sent: %d, arguments_storage: %d, fse->varc: %d\n", names_expected, arguments_sent, arguments_storage, fse->varc); #endif /* Initialise everything in storage */ for (i = 0; i < fse->varc; i++) { fse->var[i].name = NULL; ZVAL_UNDEF(&fse->var[i].data); fse->var[i].is_variadic = 0; } /* Collect Names */ for (i = 0; i < names_expected; i++) { if (op_array->arg_info[i].name) { fse->var[i].name = zend_string_copy(op_array->arg_info[i].name); } /* If an argument is a variadic, then we mark that on this 'name', * and also remember which position the variadic started */ if (ZEND_ARG_IS_VARIADIC(&op_array->arg_info[i]) && variadic_at_pos == NO_VARIADIC) { fse->var[i].is_variadic = 1; variadic_at_pos = i; } } /* Collect Arguments */ for (i = 0; i < fse->varc; i++) { #if PHP_VERSION_ID >= 80200 zend_attribute *attribute; #else void *attribute = NULL; #endif /* The index in ZEND_CALL_ARG is 1-based */ #if DEBUG fprintf(stderr, "Copying argument %d: ", i); #endif if (i < names_expected || is_trampoline) { #if DEBUG fprintf(stderr, "ARG "); #endif #if PHP_VERSION_ID >= 80200 attribute = zend_get_parameter_attribute_str( zdata->func->common.attributes, "sensitiveparameter", sizeof("sensitiveparameter") - 1, i ); #endif if (attribute && fse->var[i].is_variadic) { variadic_sensitive = 1; } # if DEBUG fprintf(stderr, "SENSITIVE %d ", attribute != NULL); # endif if ((variadic_sensitive || attribute != NULL) && !fse->var[i].is_variadic) { ZVAL_STRING(&(fse->var[i].data), "[Sensitive Parameter]"); } else { ZVAL_COPY(&(fse->var[i].data), ZEND_CALL_ARG(zdata, i + 1)); } } else { #if DEBUG fprintf(stderr, "VAR_NUM "); #endif if (variadic_sensitive) { ZVAL_STRING(&(fse->var[i].data), "[Sensitive Parameter]"); } else { ZVAL_COPY(&(fse->var[i].data), ZEND_CALL_VAR_NUM(zdata, zdata->func->op_array.last_var + zdata->func->op_array.T + i - names_expected)); } } #if DEBUG fprintf(stderr, "OK\n"); #endif } if (ZEND_CALL_INFO(zdata) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { zend_string *name; zval *param; int i = fse->varc; fse->varc += zend_hash_num_elements(zdata->extra_named_params); fse->var = xdrealloc(fse->var, fse->varc * sizeof(xdebug_var_name)); ZEND_HASH_FOREACH_STR_KEY_VAL(zdata->extra_named_params, name, param) { fse->var[i].name = zend_string_copy(name); ZVAL_COPY(&(fse->var[i].data), param); fse->var[i].is_variadic = 0; i++; } ZEND_HASH_FOREACH_END(); } #if DEBUG for (i = 0; i < fse->varc; i++) { fprintf(stderr, "%2d %-20s %c %s\n", i, fse->var[i].name ? ZSTR_VAL(fse->var[i].name) : "---", fse->var[i].is_variadic ? 'V' : ' ', xdebug_get_zval_value_line(&fse->var[i].data, 0, NULL)->d); } #endif } function_stack_entry *xdebug_add_stack_frame(zend_execute_data *zdata, zend_op_array *op_array, int type) { zend_execute_data *edata; zend_op **opline_ptr = NULL; function_stack_entry *tmp; zend_op *cur_opcode; if (type == XDEBUG_USER_DEFINED) { edata = EG(current_execute_data)->prev_execute_data; if (edata) { opline_ptr = (zend_op**) &edata->opline; } } else { edata = EG(current_execute_data); opline_ptr = (zend_op**) &EG(current_execute_data)->opline; } zdata = EG(current_execute_data); tmp = (function_stack_entry*) xdebug_vector_push(XG_BASE(stack)); tmp->level = XDEBUG_VECTOR_COUNT(XG_BASE(stack)); tmp->user_defined = type; tmp->op_array = op_array; XG_BASE(function_count)++; tmp->function_nr = XG_BASE(function_count); { zend_execute_data *ptr = edata; while (ptr && (!ptr->func || !ZEND_USER_CODE(ptr->func->type))) { ptr = ptr->prev_execute_data; } if (ptr) { tmp->filename = zend_string_copy(ptr->func->op_array.filename); } } if (!tmp->filename) { /* Includes/main script etc */ tmp->filename = (type == XDEBUG_USER_DEFINED && op_array && op_array->filename) ? zend_string_copy(op_array->filename) : NULL; } /* Call user function locations */ if (!tmp->filename && XG_BASE(stack)) { function_stack_entry *tail_fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); if (tail_fse->filename) { tmp->filename = zend_string_copy(tail_fse->filename); } } if (!tmp->filename) { tmp->filename = zend_string_init("Unknown", sizeof("Unknown") - 1, 0); } tmp->lineno = 0; tmp->prev_memory = XG_BASE(prev_memory); tmp->memory = zend_memory_usage(0); XG_BASE(prev_memory) = tmp->memory; /* Only get the time when it is actually going to be used. Profiling is not * included, because it has its own points when it reads the current time. * */ if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING) || XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { tmp->nanotime = xdebug_get_nanotime(); } else { tmp->nanotime = 0; } xdebug_build_fname(&(tmp->function), zdata); if (!tmp->function.type) { tmp->function.function = ZSTR_INIT_LITERAL("{main}", false); tmp->function.object_class = NULL; tmp->function.scope_class = NULL; tmp->function.type = XFUNC_MAIN; } else if (tmp->function.type & XFUNC_INCLUDES) { tmp->lineno = 0; if (opline_ptr) { cur_opcode = *opline_ptr; if (cur_opcode) { tmp->lineno = cur_opcode->lineno; } } if (tmp->function.type == XFUNC_EVAL && XG_BASE(last_eval_statement)) { tmp->function.include_filename = zend_string_copy(XG_BASE(last_eval_statement)); } else { tmp->function.include_filename = zend_string_copy(zend_get_executed_filename_ex()); } } else { tmp->lineno = find_line_number_for_current_execute_point(edata); tmp->is_variadic = !!(zdata->func->common.fn_flags & ZEND_ACC_VARIADIC); tmp->is_trampoline = !!(zdata->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE); if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING) || XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { if (ZEND_USER_CODE(zdata->func->type)) { collect_params(tmp, zdata, op_array); } else { collect_params_internal(tmp, zdata, op_array); } } } /* Now we have location and name, we can run the filter (for stack and tracing)*/ xdebug_filter_run(tmp); /* Count code coverage line for call */ xdebug_coverage_count_line_if_branch_check_active(op_array, tmp->filename, tmp->lineno); return tmp; } /** Function interceptors and dispatchers to modules ***********************/ static void xdebug_execute_user_code_begin(zend_execute_data *execute_data) { zend_op_array *op_array = &(execute_data->func->op_array); zend_execute_data *edata = execute_data->prev_execute_data; function_stack_entry *fse; /* For PHP 7, we need to reset the opline to the start, so that all opcode * handlers are being hit. But not for generators, as that would make an * endless loop. TODO: Fix RECV handling with generators. */ if (!(EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR)) { EX(opline) = EX(func)->op_array.opcodes; } if (XG_BASE(in_execution) && XDEBUG_VECTOR_COUNT(XG_BASE(stack)) == 0 && ((EG(flags) & EG_FLAGS_IN_SHUTDOWN) == 0)) { if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_debugger_set_program_name(op_array->filename); xdebug_debug_init_if_requested_at_startup(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_GCSTATS)) { xdebug_gcstats_init_if_requested(op_array); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_init_if_requested(op_array); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_tracing_init_if_requested(op_array); } } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP) && (signed long) XDEBUG_VECTOR_COUNT(XG_BASE(stack)) >= XINI_BASE(max_nesting_level) && (XINI_BASE(max_nesting_level) != -1)) { zend_throw_exception_ex(zend_ce_error, 0, "Xdebug has detected a possible infinite loop, and aborted your script with a stack depth of '" ZEND_LONG_FMT "' frames", XINI_BASE(max_nesting_level)); } fse = xdebug_add_stack_frame(edata, op_array, XDEBUG_USER_DEFINED); fse->function.internal = 0; /* A hack to make __call work with profiles. The function *is* user defined after all. */ if (fse && xdebug_vector_element_is_valid(XG_BASE(stack), fse - 1) && fse->function.function && zend_string_equals_literal(fse->function.function, "__call")) { (fse - 1)->user_defined = XDEBUG_USER_DEFINED; } #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT xdebug_control_socket_dispatch(); #endif if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_monitor_handler(fse); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_tracing_execute_ex(fse); } fse->execute_data = EG(current_execute_data)->prev_execute_data; if (ZEND_CALL_INFO(EG(current_execute_data)) & ZEND_CALL_HAS_SYMBOL_TABLE) { fse->symbol_table = EG(current_execute_data)->symbol_table; } if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { fse->code_coverage_init = xdebug_coverage_execute_ex(fse, op_array, &fse->code_coverage_filename, &fse->code_coverage_function_name); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { /* If we're in an eval, we need to create an ID for it. */ if (fse->function.type == XFUNC_EVAL) { xdebug_debugger_register_eval(fse); } /* Check for entry breakpoints */ xdebug_debugger_handle_breakpoints(fse, XDEBUG_BREAKPOINT_TYPE_CALL|XDEBUG_BREAKPOINT_TYPE_EXTERNAL, NULL); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_execute_ex(fse, op_array); } } static void xdebug_execute_user_code_end(zend_execute_data *execute_data, zval *retval) { zend_op_array *op_array = &(execute_data->func->op_array); function_stack_entry *fse; fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_execute_ex_end(fse); } if (fse->code_coverage_init) { xdebug_coverage_execute_ex_end(fse, op_array, fse->code_coverage_filename, fse->code_coverage_function_name); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_tracing_execute_ex_end(fse, execute_data, retval); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { zval *return_value = NULL; if (!fse->is_trampoline && retval && !(op_array->fn_flags & ZEND_ACC_GENERATOR)) { return_value = execute_data->return_value; } /* Check for return breakpoints */ xdebug_debugger_handle_breakpoints(fse, XDEBUG_BREAKPOINT_TYPE_RETURN|XDEBUG_BREAKPOINT_TYPE_EXTERNAL, return_value); } if (XG_BASE(stack)) { xdebug_vector_pop(XG_BASE(stack)); } } static bool should_run_user_handler(zend_execute_data *execute_data) { zend_op_array *op_array = &(execute_data->func->op_array); zend_execute_data *prev_edata = execute_data->prev_execute_data; if (!ZEND_USER_CODE(op_array->type)) { return false; } /* If we're evaluating for the debugger's eval capability, just bail out */ if (op_array && ZEND_USER_CODE(op_array->type) && op_array->filename && op_array->filename && strcmp("xdebug://debug-eval", STR_NAME_VAL(op_array->filename)) == 0) { return false; } /* if we're in a ZEND_EXT_STMT, we ignore this function call as it's likely that it's just being called to check for breakpoints with conditions */ if (prev_edata && prev_edata->func && ZEND_USER_CODE(prev_edata->func->type) && prev_edata->opline && prev_edata->opline->opcode == ZEND_EXT_STMT) { return false; } return true; } /* This is confusing. On PHP 8.1 we flip the logic, as normal user functions * are handled through the Observer API. Once PHP 8.0 support is dropped, the * negation should be **added** to the usage below in xdebug_execute_ex. */ static bool should_run_user_handler_wrapper(zend_execute_data *execute_data) { /* If the stack vector hasn't been initialised yet, we should abort immediately */ if (!XG_BASE(stack)) { return false; } #if PHP_VERSION_ID >= 80100 return !should_run_user_handler(execute_data); #else return should_run_user_handler(execute_data); #endif } /* We still need this to do "include", "require", and "eval" */ static void xdebug_execute_ex(zend_execute_data *execute_data) { bool run_user_handler = should_run_user_handler_wrapper(execute_data); if (run_user_handler) { xdebug_execute_user_code_begin(execute_data); } xdebug_old_execute_ex(execute_data); if (run_user_handler) { xdebug_execute_user_code_end(execute_data, execute_data->return_value); } } static int check_soap_call(function_stack_entry *fse, zend_execute_data *execute_data) { if ( fse->function.object_class && Z_OBJ(EX(This)) && Z_TYPE(EX(This)) == IS_OBJECT && (zend_hash_str_find_ptr(&module_registry, "soap", sizeof("soap") - 1) != NULL) ) { zend_class_entry *soap_server_ce, *soap_client_ce; soap_server_ce = zend_hash_str_find_ptr(CG(class_table), "soapserver", 10); soap_client_ce = zend_hash_str_find_ptr(CG(class_table), "soapclient", 10); if (!soap_server_ce || !soap_client_ce) { return 0; } if ( (instanceof_function(Z_OBJCE(EX(This)), soap_server_ce)) || (instanceof_function(Z_OBJCE(EX(This)), soap_client_ce)) ) { return 1; } } return 0; } static bool should_run_internal_handler(zend_execute_data *execute_data) { /* If the stack vector hasn't been initialised yet, we should abort immediately */ if (!XG_BASE(stack)) { return false; } if (!execute_data || !execute_data->func || ZEND_USER_CODE(execute_data->func->type)) { return false; } return true; } static void xdebug_execute_internal_begin(zend_execute_data *current_execute_data) { zend_execute_data *edata = EG(current_execute_data); function_stack_entry *fse; if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP) && (signed long) XDEBUG_VECTOR_COUNT(XG_BASE(stack)) >= XINI_BASE(max_nesting_level) && (XINI_BASE(max_nesting_level) != -1)) { zend_throw_exception_ex(zend_ce_error, 0, "Xdebug has detected a possible infinite loop, and aborted your script with a stack depth of '" ZEND_LONG_FMT "' frames", XINI_BASE(max_nesting_level)); } fse = xdebug_add_stack_frame(edata, &edata->func->op_array, XDEBUG_BUILT_IN); fse->function.internal = 1; if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_monitor_handler(fse); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { fse->function_call_traced = xdebug_tracing_execute_internal(fse); } fse->execute_data = EG(current_execute_data)->prev_execute_data; if (ZEND_CALL_INFO(EG(current_execute_data)) & ZEND_CALL_HAS_SYMBOL_TABLE) { fse->symbol_table = EG(current_execute_data)->symbol_table; } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { /* Check for entry breakpoints */ xdebug_debugger_handle_breakpoints(fse, XDEBUG_BREAKPOINT_TYPE_CALL, NULL); } /* Check for SOAP */ if (check_soap_call(fse, current_execute_data)) { fse->soap_error_cb = zend_error_cb; xdebug_base_use_original_error_cb(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_execute_internal(fse); } } static void xdebug_execute_internal_end(zend_execute_data *current_execute_data, zval *return_value) { function_stack_entry *fse; /* Re-acquire the tail as nested calls through * xdebug_old_execute_internal() might have reallocated the vector */ fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_execute_internal_end(fse); } /* Restore SOAP situation if needed */ if (fse->soap_error_cb) { zend_error_cb = fse->soap_error_cb; } /* We only call the function_exit handler and return value handler if the * function call was also traced. Otherwise we end up with return trace * lines without a corresponding function call line. */ if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING) && fse->function_call_traced) { xdebug_tracing_execute_internal_end(fse, return_value); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { /* Check for return breakpoints */ xdebug_debugger_handle_breakpoints(fse, XDEBUG_BREAKPOINT_TYPE_RETURN, return_value); } if (XG_BASE(stack)) { xdebug_vector_pop(XG_BASE(stack)); } } #if PHP_VERSION_ID < 80200 static void xdebug_execute_internal(zend_execute_data *current_execute_data, zval *return_value) { bool run_internal_handler = should_run_internal_handler(current_execute_data); if (run_internal_handler) { xdebug_execute_internal_begin(current_execute_data); } if (xdebug_old_execute_internal) { xdebug_old_execute_internal(current_execute_data, return_value); } else { execute_internal(current_execute_data, return_value); } if (run_internal_handler) { xdebug_execute_internal_end(current_execute_data, return_value); } } #endif #if PHP_VERSION_ID >= 80100 static void xdebug_execute_begin(zend_execute_data *execute_data) { /* If the stack vector hasn't been initialised yet, we should abort immediately */ if (!XG_BASE(stack)) { return; } if (should_run_user_handler(execute_data)) { xdebug_execute_user_code_begin(execute_data); } #if PHP_VERSION_ID >= 80200 if (should_run_internal_handler(execute_data)) { xdebug_execute_internal_begin(execute_data); } #endif } static void xdebug_execute_end(zend_execute_data *execute_data, zval *retval) { /* If the stack vector hasn't been initialised yet, we should abort immediately */ if (!XG_BASE(stack)) { return; } if (should_run_user_handler(execute_data)) { xdebug_execute_user_code_end(execute_data, retval); } #if PHP_VERSION_ID >= 80200 if (should_run_internal_handler(execute_data)) { xdebug_execute_internal_end(execute_data, retval); } #endif } static zend_observer_fcall_handlers xdebug_observer_init(zend_execute_data *execute_data) { return (zend_observer_fcall_handlers){xdebug_execute_begin, xdebug_execute_end}; } #endif /***************************************************************************/ static void xdebug_base_overloaded_functions_setup(void) { zend_function *orig; /* Override set_time_limit with our own function to prevent timing out while debugging */ orig = zend_hash_str_find_ptr(CG(function_table), "set_time_limit", sizeof("set_time_limit") - 1); if (orig) { orig_set_time_limit_func = orig->internal_function.handler; orig->internal_function.handler = zif_xdebug_set_time_limit; } /* Override error_reporting with our own function, to be able to give right answer during DBGp's * 'eval' commands */ orig = zend_hash_str_find_ptr(CG(function_table), "error_reporting", sizeof("error_reporting") - 1); if (orig) { orig_error_reporting_func = orig->internal_function.handler; orig->internal_function.handler = zif_xdebug_error_reporting; } /* Override pcntl_exec with our own function to be able to write profiling summary */ orig = zend_hash_str_find_ptr(CG(function_table), "pcntl_exec", sizeof("pcntl_exec") - 1); if (orig) { orig_pcntl_exec_func = orig->internal_function.handler; orig->internal_function.handler = zif_xdebug_pcntl_exec; } /* Override pcntl_fork with our own function to be able * to start the debugger for the forked process */ orig = zend_hash_str_find_ptr(CG(function_table), "pcntl_fork", sizeof("pcntl_fork") - 1); if (orig) { orig_pcntl_fork_func = orig->internal_function.handler; orig->internal_function.handler = zif_xdebug_pcntl_fork; } /* Override exit with our own function to be able to write profiling summary */ orig = zend_hash_str_find_ptr(CG(function_table), "exit", sizeof("exit") - 1); if (orig) { orig_exit_func = orig->internal_function.handler; orig->internal_function.handler = zif_xdebug_exit; } } static int xdebug_closure_serialize_deny_wrapper(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data) { zend_class_entry *ce = Z_OBJCE_P(object); if (!XG_BASE(in_var_serialisation)) { zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed", STR_NAME_VAL(ce->name)); } return FAILURE; } #if PHP_VERSION_ID >= 80100 /** Handling fibers ********************************************************/ static struct xdebug_fiber_entry* xdebug_fiber_entry_ctor(xdebug_vector *stack) { struct xdebug_fiber_entry *tmp = xdmalloc(sizeof(struct xdebug_fiber_entry)); tmp->stack = stack; return tmp; } static void xdebug_fiber_entry_dtor(struct xdebug_fiber_entry *entry) { xdebug_vector_destroy(entry->stack); xdfree(entry); } static zend_string *create_key_for_fiber(zend_fiber_context *fiber) { return zend_strpprintf(0, "{fiber:%0" PRIXPTR "}", ((uintptr_t) fiber)); } static void add_fiber_main(zend_fiber_context *fiber) { function_stack_entry *tmp = (function_stack_entry*) xdebug_vector_push(XG_BASE(stack)); tmp->level = XDEBUG_VECTOR_COUNT(XG_BASE(stack)); tmp->user_defined = XDEBUG_BUILT_IN; tmp->function.type = XFUNC_FIBER; tmp->function.object_class = NULL; tmp->function.scope_class = NULL; tmp->function.function = create_key_for_fiber(fiber); tmp->filename = zend_string_copy(zend_get_executed_filename_ex()); tmp->lineno = zend_get_executed_lineno(); tmp->prev_memory = XG_BASE(prev_memory); tmp->memory = zend_memory_usage(0); XG_BASE(prev_memory) = tmp->memory; tmp->nanotime = xdebug_get_nanotime(); } static xdebug_vector* create_stack_for_fiber(zend_fiber_context *fiber) { xdebug_vector *tmp_stack = xdebug_vector_alloc(sizeof(function_stack_entry), function_stack_entry_dtor); zend_string *key = create_key_for_fiber(fiber); struct xdebug_fiber_entry *entry = xdebug_fiber_entry_ctor(tmp_stack); xdebug_hash_add(XG_BASE(fiber_stacks), ZSTR_VAL(key), ZSTR_LEN(key), entry); zend_string_release(key); return tmp_stack; } static void remove_stack_for_fiber(zend_fiber_context *fiber) { zend_string *key = create_key_for_fiber(fiber); xdebug_hash_delete(XG_BASE(fiber_stacks), ZSTR_VAL(key), ZSTR_LEN(key)); zend_string_release(key); } static xdebug_vector *find_stack_for_fiber(zend_fiber_context *fiber) { struct xdebug_fiber_entry *entry = NULL; zend_string *key = create_key_for_fiber(fiber); xdebug_hash_find(XG_BASE(fiber_stacks), ZSTR_VAL(key), ZSTR_LEN(key), (void*) &entry); zend_string_release(key); return entry->stack; } static void xdebug_fiber_switch_observer(zend_fiber_context *from, zend_fiber_context *to) { xdebug_vector *current_stack; if (from->status == ZEND_FIBER_STATUS_DEAD) { if (XG_DBG(context).next_stack == find_stack_for_fiber(from)) { XG_DBG(context).next_stack = NULL; } remove_stack_for_fiber(from); } if (to->status == ZEND_FIBER_STATUS_INIT) { current_stack = create_stack_for_fiber(to); } else { current_stack = find_stack_for_fiber(to); } XG_BASE(stack) = current_stack; if (to->status == ZEND_FIBER_STATUS_INIT) { add_fiber_main(to); } } /***************************************************************************/ #endif #ifdef __linux__ int read_systemd_private_tmp_directory(char **private_tmp) { pid_t current_pid; char *mountinfo_fn; FILE *mountinfo_fd; size_t bytes_read; char buffer[8192] = { 0 }; xdebug_arg *lines; int i; int retval = 0; /* Open right file in /proc */ current_pid = getpid(); mountinfo_fn = xdebug_sprintf("/proc/%ld/mountinfo", current_pid); mountinfo_fd = fopen(mountinfo_fn, "r"); xdfree(mountinfo_fn); if (!mountinfo_fd) { return retval; } /* Read contents and split in lines */ bytes_read = fread(buffer, 1, sizeof(buffer), mountinfo_fd); if (!bytes_read) { fclose(mountinfo_fd); return retval; } lines = xdebug_arg_ctor(); xdebug_explode("\n", buffer, lines, -1); /* Check whether each line has /tmp/systemd-private, and parse accordingly. * There is a " " in front as there is often also a /var/tmp/systemd-private * entry that we need to ignore. */ for (i = 0; i < lines->c; i++) { const char *mountpoint; const char *slash; mountpoint = strstr(lines->args[i], " /tmp/systemd-private"); if (mountpoint == NULL) { continue; } mountpoint++; slash = strchr(mountpoint + 1, '/'); if (!slash) { continue; } slash = strchr(slash + 1, '/'); if (!slash) { continue; } *private_tmp = xdstrndup(mountpoint, slash - mountpoint); retval = 1; break; } /* Clean up and return */ xdebug_arg_dtor(lines); fclose(mountinfo_fd); return retval; } #endif void xdebug_base_minit(INIT_FUNC_ARGS) { /* Record Zend and Xdebug error callbacks, the actual setting is done in * base on RINIT */ xdebug_old_error_cb = zend_error_cb; xdebug_new_error_cb = xdebug_error_cb; #if PHP_VERSION_ID >= 80100 /* User Code Functions */ zend_observer_fcall_register(xdebug_observer_init); #endif /* Include, Require, Eval */ xdebug_old_execute_ex = zend_execute_ex; zend_execute_ex = xdebug_execute_ex; #if PHP_VERSION_ID < 80200 /* Internal Functions, since 8.2 they're also observed */ xdebug_old_execute_internal = zend_execute_internal; zend_execute_internal = xdebug_execute_internal; #endif XG_BASE(error_reporting_override) = 0; XG_BASE(error_reporting_overridden) = 0; XG_BASE(output_is_tty) = OUTPUT_NOT_CHECKED; #if PHP_VERSION_ID >= 80100 zend_observer_fiber_switch_register(xdebug_fiber_switch_observer); #endif XG_BASE(private_tmp) = NULL; #ifdef __linux__ read_systemd_private_tmp_directory(&XG_BASE(private_tmp)); #endif #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT XG_BASE(control_socket_path) = NULL; XG_BASE(control_socket_fd) = 0; XG_BASE(control_socket_last_trigger) = 0; #endif xdebug_base_overloaded_functions_setup(); } void xdebug_base_mshutdown() { /* Reset compile, execute and error callbacks */ zend_compile_file = old_compile_file; zend_execute_ex = xdebug_old_execute_ex; zend_execute_internal = xdebug_old_execute_internal; zend_error_cb = xdebug_old_error_cb; #ifdef __linux__ if (XG_BASE(private_tmp)) { xdfree(XG_BASE(private_tmp)); } #endif } void xdebug_base_post_startup() { old_compile_file = zend_compile_file; zend_compile_file = xdebug_compile_file; } void xdebug_base_rinit() { /* Hack: We check for a soap header here, if that's existing, we don't use * Xdebug's error handler to keep soap fault from fucking up. */ if ( (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP) || XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) && (zend_hash_str_find(Z_ARR(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_SOAPACTION", sizeof("HTTP_SOAPACTION") - 1) == NULL) ) { xdebug_base_use_xdebug_error_cb(); xdebug_base_use_xdebug_throw_exception_hook(); } #if PHP_VERSION_ID >= 80100 XG_BASE(fiber_stacks) = xdebug_hash_alloc(64, (xdebug_hash_dtor_t) xdebug_fiber_entry_dtor); XG_BASE(stack) = create_stack_for_fiber(EG(main_fiber_context)); #else XG_BASE(stack) = xdebug_vector_alloc(sizeof(function_stack_entry), function_stack_entry_dtor); #endif XG_BASE(in_debug_info) = 0; XG_BASE(prev_memory) = 0; XG_BASE(function_count) = -1; XG_BASE(last_eval_statement) = NULL; XG_BASE(last_exception_trace) = NULL; /* Initialize start time */ XG_BASE(start_nanotime) = xdebug_get_nanotime(); XG_BASE(in_var_serialisation) = 0; zend_ce_closure->serialize = xdebug_closure_serialize_deny_wrapper; #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT /* Set-up Control Socket */ # if HAVE_XDEBUG_CLOCK_GETTIME /* Check whether we have a broken TSC clock, and adjust if needed */ if (!XG_BASE(working_tsc_clock)) { if (XINI_BASE(control_socket_granularity) == XDEBUG_CONTROL_SOCKET_DEFAULT) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "TSC-NO", "Not setting up control socket with default value due to unavailable 'tsc' clock"); XINI_BASE(control_socket_granularity) = XDEBUG_CONTROL_SOCKET_OFF; } if (XINI_BASE(control_socket_granularity) == XDEBUG_CONTROL_SOCKET_TIME) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "TSC-INFREQ", "Due to unavailable TSC clock, setting poll granularity to 100ms instead of 25ms"); XINI_BASE(control_socket_threshold_ms) = 100; } } # endif if (XINI_BASE(control_socket_granularity) != XDEBUG_CONTROL_SOCKET_OFF) { xdebug_control_socket_setup(); } #endif /* Signal that we're in a request now */ XG_BASE(in_execution) = 1; /* filters */ XG_BASE(filter_type_code_coverage) = XDEBUG_FILTER_NONE; XG_BASE(filter_type_stack) = XDEBUG_FILTER_NONE; XG_BASE(filter_type_tracing) = XDEBUG_FILTER_NONE; XG_BASE(filters_code_coverage) = xdebug_llist_alloc(xdebug_llist_string_dtor); XG_BASE(filters_stack) = xdebug_llist_alloc(xdebug_llist_string_dtor); XG_BASE(filters_tracing) = xdebug_llist_alloc(xdebug_llist_string_dtor); if (XG_BASE(private_tmp)) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_INFO, "PRIVTMP", "Systemd Private Temp Directory is enabled (%s)", XG_BASE(private_tmp)); } } void xdebug_base_post_deactivate() { #if PHP_VERSION_ID >= 80100 xdebug_hash_destroy(XG_BASE(fiber_stacks)); XG_BASE(fiber_stacks) = NULL; #else xdebug_vector_destroy(XG_BASE(stack)); #endif XG_BASE(stack) = NULL; XG_BASE(in_debug_info) = 0; if (XG_BASE(last_eval_statement)) { zend_string_release(XG_BASE(last_eval_statement)); XG_BASE(last_eval_statement) = NULL; } if (XG_BASE(last_exception_trace)) { xdfree(XG_BASE(last_exception_trace)); XG_BASE(last_exception_trace) = NULL; } /* filters */ xdebug_llist_destroy(XG_BASE(filters_code_coverage), NULL); xdebug_llist_destroy(XG_BASE(filters_stack), NULL); xdebug_llist_destroy(XG_BASE(filters_tracing), NULL); XG_BASE(filters_tracing) = NULL; XG_BASE(filters_code_coverage) = NULL; #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT /* Close Down Control Socket */ xdebug_control_socket_teardown(); #endif } void xdebug_base_rshutdown() { /* Signal that we're no longer in a request */ XG_BASE(in_execution) = 0; } /* Error callback for formatting stack traces */ #if PHP_VERSION_ID >= 80100 static void xdebug_error_cb(int orig_type, zend_string *error_filename, const unsigned int error_lineno, zend_string *message) { if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { int type = orig_type & E_ALL; char *error_type_str = xdebug_error_type(type); xdebug_debugger_error_cb(error_filename, error_lineno, type, error_type_str, ZSTR_VAL(message)); xdfree(error_type_str); } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_develop_error_cb(orig_type, error_filename, error_lineno, message); } else { xdebug_old_error_cb(orig_type, error_filename, error_lineno, message); } } #else static void xdebug_error_cb(int orig_type, const char *error_filename, const unsigned int error_lineno, zend_string *message) { if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { int type = orig_type & E_ALL; char *error_type_str = xdebug_error_type(type); zend_string *tmp_error_filename = zend_string_init(error_filename, strlen(error_filename), 0); xdebug_debugger_error_cb(tmp_error_filename, error_lineno, type, error_type_str, ZSTR_VAL(message)); zend_string_release(tmp_error_filename); xdfree(error_type_str); } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_develop_error_cb(orig_type, error_filename, error_lineno, message); } else { xdebug_old_error_cb(orig_type, error_filename, error_lineno, message); } } #endif void xdebug_base_use_original_error_cb(void) { zend_error_cb = xdebug_old_error_cb; } void xdebug_base_use_xdebug_error_cb(void) { zend_error_cb = xdebug_new_error_cb; } static void xdebug_throw_exception_hook(zend_object *exception) { zval *code, *message, *file, *line; zend_class_entry *exception_ce; char *code_str = NULL; zval dummy; if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP) && !XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { return; } if (!exception) { return; } if (zend_is_unwind_exit(exception)) { return; } #if PHP_VERSION_ID >= 80100 if (zend_is_graceful_exit(exception)) { return; } #endif exception_ce = exception->ce; code = zend_read_property(exception_ce, exception, "code", sizeof("code")-1, 0, &dummy); message = zend_read_property(exception_ce, exception, "message", sizeof("message")-1, 0, &dummy); file = zend_read_property(exception_ce, exception, "file", sizeof("file")-1, 0, &dummy); line = zend_read_property(exception_ce, exception, "line", sizeof("line")-1, 0, &dummy); if (Z_TYPE_P(code) == IS_LONG) { if (Z_LVAL_P(code) != 0) { code_str = xdebug_sprintf("%lu", Z_LVAL_P(code)); } } else if (Z_TYPE_P(code) != IS_STRING) { code_str = xdstrdup(""); } if (Z_TYPE_P(message) != IS_STRING) { message = NULL; } convert_to_string_ex(file); convert_to_long_ex(line); if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_develop_throw_exception_hook(exception, file, line, code, code_str, message); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_debugger_throw_exception_hook(exception, file, line, code, code_str, message); } /* Free code_str if necessary */ if (code_str) { xdfree(code_str); } } void xdebug_base_use_xdebug_throw_exception_hook(void) { zend_throw_exception_hook = xdebug_throw_exception_hook; } /* {{{ proto void xdebug_set_time_limit(void) Dummy function to prevent time limit from being set within the script */ PHP_FUNCTION(xdebug_set_time_limit) { if (!xdebug_is_debug_connection_active()) { orig_set_time_limit_func(INTERNAL_FUNCTION_PARAM_PASSTHRU); } RETURN_FALSE; } /* }}} */ /* {{{ proto int xdebug_error_reporting(void) Dummy function to return original error reporting level when 'eval' has turned it into 0 */ PHP_FUNCTION(xdebug_error_reporting) { if (ZEND_NUM_ARGS() == 0 && XG_BASE(error_reporting_overridden) && xdebug_is_debug_connection_active()) { RETURN_LONG(XG_BASE(error_reporting_override)); } orig_error_reporting_func(INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ proto void xdebug_pcntl_exec(void) Dummy function to stop profiling when we run pcntl_exec */ PHP_FUNCTION(xdebug_pcntl_exec) { /* We need to stop the profiler and trace files here */ xdebug_profiler_pcntl_exec_handler(); orig_pcntl_exec_func(INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ proto void xdebug_exit(void) Dummy function to stop profiling when we run exit */ PHP_FUNCTION(xdebug_exit) { orig_exit_func(INTERNAL_FUNCTION_PARAM_PASSTHRU); /* We need to stop the profiler and trace files here */ xdebug_profiler_exit_function_handler(); } /* }}} */ /* {{{ proto int xdebug_pcntl_fork(void) Dummy function to set a new connection when forking a process */ PHP_FUNCTION(xdebug_pcntl_fork) { orig_pcntl_fork_func(INTERNAL_FUNCTION_PARAM_PASSTHRU); xdebug_debugger_restart_if_pid_changed(); } /* }}} */ xdebug-3.4.3/src/base/base.h0000664000175000017500000000272715011062311015047 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_BASE_H__ #define __XDEBUG_BASE_H__ void xdebug_base_minit(INIT_FUNC_ARGS); void xdebug_base_mshutdown(); void xdebug_base_post_startup(); void xdebug_base_rinit(); void xdebug_base_post_deactivate(); void xdebug_base_rshutdown(); void xdebug_func_dtor_by_ref(xdebug_func *elem); /* TODO: Remove this API */ void xdebug_func_dtor(xdebug_func *elem); void xdebug_build_fname(xdebug_func *tmp, zend_execute_data *edata); void xdebug_print_info(void); #endif // __XDEBUG_BASE_H__ xdebug-3.4.3/src/base/base_globals.h0000664000175000017500000000600315011062311016541 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_BASE_GLOBALS_H__ #define __XDEBUG_BASE_GLOBALS_H__ #include "lib/hash.h" #include "lib/llist.h" #include "lib/vector.h" #if PHP_WIN32 typedef void (WINAPI *WIN_PRECISE_TIME_FUNC)(LPFILETIME); #endif typedef struct _xdebug_nanotime_context { uint64_t start_abs; uint64_t last_abs; #if PHP_WIN32 | HAVE_XDEBUG_CLOCK_GETTIME | HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP uint64_t start_rel; uint64_t last_rel; int use_rel_time; #endif #if PHP_WIN32 WIN_PRECISE_TIME_FUNC win_precise_time_func; uint64_t win_freq; #endif } xdebug_nanotime_context; typedef struct _xdebug_base_globals_t { xdebug_vector *stack; #if PHP_VERSION_ID >= 80100 xdebug_hash *fiber_stacks; #endif xdebug_nanotime_context nanotime_context; uint64_t start_nanotime; unsigned int working_tsc_clock; /* -1 = unknown, 0 = not available, 1 = available */ unsigned int prev_memory; int output_is_tty; zend_bool in_debug_info; zend_long error_reporting_override; zend_bool error_reporting_overridden; unsigned int function_count; zend_string *last_eval_statement; char *last_exception_trace; /* in-execution checking */ zend_bool in_execution; zend_bool in_var_serialisation; /* Systemd Private Temp */ char *private_tmp; #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT /* Control Socket */ char *control_socket_path; int control_socket_fd; zend_long control_socket_last_trigger; #endif /* filters */ zend_long filter_type_code_coverage; zend_long filter_type_stack; zend_long filter_type_tracing; xdebug_llist *filters_code_coverage; xdebug_llist *filters_stack; xdebug_llist *filters_tracing; /* PHP versions */ const char *php_version_compile_time; const char *php_version_run_time; } xdebug_base_globals_t; typedef struct _xdebug_base_settings_t { #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT int control_socket_granularity; zend_long control_socket_threshold_ms; #endif zend_long max_nesting_level; } xdebug_base_settings_t; #endif // __XDEBUG_BASE_GLOBALS_H__ xdebug-3.4.3/src/base/base_private.h0000664000175000017500000000223515011062311016573 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_BASE_PRIVATE_H__ #define __XDEBUG_BASE_PRIVATE_H__ #include "php_xdebug.h" struct xdebug_fiber_entry { xdebug_vector *stack; }; #endif // __XDEBUG_BASE_PRIVATE_H__ xdebug-3.4.3/src/base/filter.c0000664000175000017500000002154015011062311015407 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "filter.h" #include "lib/lib.h" #include "lib/log.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) int xdebug_is_stack_frame_filtered(int filter_type, function_stack_entry *fse) { switch (filter_type) { case XDEBUG_FILTER_CODE_COVERAGE: return fse->filtered_code_coverage; case XDEBUG_FILTER_STACK: return fse->filtered_stack; case XDEBUG_FILTER_TRACING: return fse->filtered_tracing; } return 0; } int xdebug_is_top_stack_frame_filtered(int filter_type) { function_stack_entry *fse; fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); return xdebug_is_stack_frame_filtered(filter_type, fse); } void xdebug_filter_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("XDEBUG_FILTER_CODE_COVERAGE", XDEBUG_FILTER_CODE_COVERAGE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_FILTER_STACK", XDEBUG_FILTER_STACK, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_FILTER_TRACING", XDEBUG_FILTER_TRACING, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_FILTER_NONE", XDEBUG_FILTER_NONE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_PATH_INCLUDE", XDEBUG_PATH_INCLUDE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_PATH_EXCLUDE", XDEBUG_PATH_EXCLUDE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_NAMESPACE_INCLUDE", XDEBUG_NAMESPACE_INCLUDE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_NAMESPACE_EXCLUDE", XDEBUG_NAMESPACE_EXCLUDE, CONST_CS | CONST_PERSISTENT); } static int xdebug_filter_match_path_include(function_stack_entry *fse, unsigned char *filtered_flag, char *filter) { if (fse->filename && strncasecmp(filter, ZSTR_VAL(fse->filename), strlen(filter)) == 0) { *filtered_flag = 0; return 1; } return 0; } static int xdebug_filter_match_path_exclude(function_stack_entry *fse, unsigned char *filtered_flag, char *filter) { if (fse->filename && strncasecmp(filter, ZSTR_VAL(fse->filename), strlen(filter)) == 0) { *filtered_flag = 1; return 1; } return 0; } static int xdebug_filter_match_namespace_include(function_stack_entry *fse, unsigned char *filtered_flag, char *filter) { if (!fse->function.object_class && filter[0] == '\0') { *filtered_flag = 0; return 1; } if (fse->function.object_class && filter[0] != '\0' && strncasecmp(filter, ZSTR_VAL(fse->function.object_class), strlen(filter)) == 0) { *filtered_flag = 0; return 1; } return 0; } static int xdebug_filter_match_namespace_exclude(function_stack_entry *fse, unsigned char *filtered_flag, char *filter) { if (!fse->function.object_class && filter[0] == '\0') { *filtered_flag = 1; return 1; } if (fse->function.object_class && filter[0] != '\0' && strncasecmp(filter, ZSTR_VAL(fse->function.object_class), strlen(filter)) == 0) { *filtered_flag = 1; return 1; } return 0; } void xdebug_filter_run_internal(function_stack_entry *fse, int group, unsigned char *filtered_flag, int type, xdebug_llist *filters) { xdebug_llist_element *le; unsigned int k; function_stack_entry tmp_fse; int (*filter_to_run)(function_stack_entry *fse, unsigned char *filtered_flag, char *filter); le = XDEBUG_LLIST_HEAD(filters); switch (type) { case XDEBUG_PATH_INCLUDE: *filtered_flag = 1; if (group == XDEBUG_FILTER_CODE_COVERAGE && fse->function.type & XFUNC_INCLUDES) { tmp_fse.filename = fse->function.include_filename; fse = &tmp_fse; } filter_to_run = xdebug_filter_match_path_include; break; case XDEBUG_PATH_EXCLUDE: *filtered_flag = 0; if (group == XDEBUG_FILTER_CODE_COVERAGE && fse->function.type & XFUNC_INCLUDES) { tmp_fse.filename = fse->function.include_filename; fse = &tmp_fse; } filter_to_run = xdebug_filter_match_path_exclude; break; case XDEBUG_NAMESPACE_INCLUDE: *filtered_flag = 1; filter_to_run = xdebug_filter_match_namespace_include; break; case XDEBUG_NAMESPACE_EXCLUDE: *filtered_flag = 0; filter_to_run = xdebug_filter_match_namespace_exclude; break; default: /* Logically can't happen, but compilers can't detect that */ return; } for (k = 0; k < filters->size; k++, le = XDEBUG_LLIST_NEXT(le)) { char *filter = XDEBUG_LLIST_VALP(le); /* If the filter matched once, we're done */ if (filter_to_run(fse, filtered_flag, filter)) { break; } } } void xdebug_filter_run(function_stack_entry *fse) { fse->filtered_stack = 0; fse->filtered_tracing = 0; if (XG_BASE(filter_type_stack) != XDEBUG_FILTER_NONE) { xdebug_filter_run_internal(fse, XDEBUG_FILTER_STACK, &fse->filtered_stack, XG_BASE(filter_type_stack), XG_BASE(filters_stack)); } if (XG_BASE(filter_type_tracing) != XDEBUG_FILTER_NONE) { xdebug_filter_run_internal(fse, XDEBUG_FILTER_TRACING, &fse->filtered_tracing, XG_BASE(filter_type_tracing), XG_BASE(filters_tracing)); } } /* {{{ proto void xdebug_set_filter(int group, int type, array filters) This function configures filters for tracing and code coverage */ PHP_FUNCTION(xdebug_set_filter) { zend_long filter_group; zend_long filter_type; xdebug_llist **filter_list; zval *filters, *item; if (zend_parse_parameters(ZEND_NUM_ARGS(), "lla", &filter_group, &filter_type, &filters) == FAILURE) { return; } switch (filter_group) { case XDEBUG_FILTER_CODE_COVERAGE: if (!XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { xdebug_log_ex(XLOG_CHAN_BASE, XLOG_WARN, "COV-FILTER", "Can not set a filter for code coverage, because Xdebug mode does not include 'coverage'"); return; } filter_list = &XG_BASE(filters_code_coverage); XG_BASE(filter_type_code_coverage) = XDEBUG_FILTER_NONE; if (filter_type == XDEBUG_NAMESPACE_INCLUDE || filter_type == XDEBUG_NAMESPACE_EXCLUDE) { php_error(E_WARNING, "The code coverage filter (XDEBUG_FILTER_CODE_COVERAGE) only supports the XDEBUG_PATH_INCLUDE, XDEBUG_PATH_EXCLUDE, and XDEBUG_FILTER_NONE filter types"); return; } break; case XDEBUG_FILTER_STACK: if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_log_ex(XLOG_CHAN_BASE, XLOG_WARN, "DEV-FILTER", "Can not set a stack filter, because Xdebug mode does not include 'develop'"); return; } filter_list = &XG_BASE(filters_stack); XG_BASE(filter_type_stack) = XDEBUG_FILTER_NONE; break; case XDEBUG_FILTER_TRACING: if (!XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_log_ex(XLOG_CHAN_BASE, XLOG_WARN, "TRACE-FILTER", "Can not set a filter for tracing, because Xdebug mode does not include 'trace'"); return; } filter_list = &XG_BASE(filters_tracing); XG_BASE(filter_type_tracing) = XDEBUG_FILTER_NONE; break; default: php_error(E_WARNING, "Filter group needs to be one of XDEBUG_FILTER_CODE_COVERAGE, XDEBUG_FILTER_STACK, or XDEBUG_FILTER_TRACING"); return; } if ( filter_type == XDEBUG_PATH_INCLUDE || filter_type == XDEBUG_PATH_EXCLUDE || filter_type == XDEBUG_NAMESPACE_INCLUDE || filter_type == XDEBUG_NAMESPACE_EXCLUDE || filter_type == XDEBUG_FILTER_NONE ) { switch (filter_group) { case XDEBUG_FILTER_CODE_COVERAGE: XG_BASE(filter_type_code_coverage) = filter_type; break; case XDEBUG_FILTER_STACK: XG_BASE(filter_type_stack) = filter_type; break; case XDEBUG_FILTER_TRACING: XG_BASE(filter_type_tracing) = filter_type; break; } } else { php_error(E_WARNING, "Filter type needs to be one of XDEBUG_PATH_INCLUDE, XDEBUG_PATH_EXCLUDE, XDEBUG_NAMESPACE_INCLUDE, XDEBUG_NAMESPACE_EXCLUDE, or XDEBUG_FILTER_NONE"); return; } xdebug_llist_empty(*filter_list, NULL); if (filter_type == XDEBUG_FILTER_NONE) { return; } ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(filters), item) { zend_string *str = zval_get_string(item); char *filter = ZSTR_VAL(str); /* If we are a namespace filter, and the filter name starts with \, we * need to strip the \ from the matcher */ xdebug_llist_insert_next(*filter_list, XDEBUG_LLIST_TAIL(*filter_list), xdstrdup(filter[0] == '\\' ? &filter[1] : filter)); zend_string_release(str); } ZEND_HASH_FOREACH_END(); } /* }}} */ xdebug-3.4.3/src/base/filter.h0000664000175000017500000000357215011062311015421 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef XDEBUG_FILTER_H #define XDEBUG_FILTER_H #include "lib/php-header.h" #include "php_xdebug.h" int xdebug_is_stack_frame_filtered(int filter_type, function_stack_entry *fse); int xdebug_is_top_stack_frame_filtered(int filter_type); void xdebug_filter_register_constants(INIT_FUNC_ARGS); void xdebug_filter_run(function_stack_entry *fse); void xdebug_filter_run_code_coverage(zend_op_array *op_array); void xdebug_filter_run_internal(function_stack_entry *fse, int group, unsigned char *filtered_flag, int type, xdebug_llist *filters); #define XDEBUG_FILTER_NONE 0x000 #define XDEBUG_FILTER_CODE_COVERAGE 0x100 #define XDEBUG_FILTER_STACK 0x200 #define XDEBUG_FILTER_TRACING 0x300 #define XDEBUG_PATH_INCLUDE 0x01 #define XDEBUG_PATH_EXCLUDE 0x02 #define XDEBUG_NAMESPACE_INCLUDE 0x11 #define XDEBUG_NAMESPACE_EXCLUDE 0x12 PHP_FUNCTION(xdebug_set_filter); #endif xdebug-3.4.3/src/base/ctrl_socket.c0000664000175000017500000002523215011062311016440 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include #include #include #include #include #include #include #include "php_xdebug.h" #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT #include "ctrl_socket.h" #include "lib/cmd_parser.h" #include "lib/log.h" #include "lib/xml.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) typedef struct { int code; const char *message; } xdebug_error_entry; static xdebug_error_entry xdebug_error_codes[24] = { { 0, "no error" }, { 1, "parse error in command" }, { 2, "duplicate arguments in command" }, { 3, "invalid or missing options" }, { 4, "unimplemented command" }, { 5, "command is not available" }, { 400, "step debugger is not enabled" }, { 401, "step debugger did not activate" }, { -1, NULL } }; static const char *error_message_from_code(int code) { xdebug_error_entry *error_entry = &xdebug_error_codes[0]; while (error_entry->message) { if (code == error_entry->code) { return error_entry->message; } error_entry++; } return NULL; } #define ADD_REASON_MESSAGE(c) { \ xdebug_xml_node *message = xdebug_xml_node_init("message"); \ xdebug_xml_add_text(message, xdstrdup(error_message_from_code(c))); \ xdebug_xml_add_child(error, message); \ } #define CTRL_FUNC_PARAMETERS xdebug_xml_node **retval, xdebug_dbgp_arg *args #define CTRL_FUNC(name) static void xdebug_ctrl_handle_##name(CTRL_FUNC_PARAMETERS) #define CTRL_FUNC_ENTRY(name) { #name, xdebug_ctrl_handle_##name }, typedef struct xdebug_ctrl_cmd { const char *name; void (*handler)(CTRL_FUNC_PARAMETERS); int flags; } xdebug_ctrl_cmd; /* Command definition list */ CTRL_FUNC(ps); CTRL_FUNC(pause); static xdebug_ctrl_cmd ctrl_commands[] = { CTRL_FUNC_ENTRY(ps) CTRL_FUNC_ENTRY(pause) { NULL, NULL } }; /* Command handler selection */ static xdebug_ctrl_cmd* lookup_cmd(char *cmd) { xdebug_ctrl_cmd *ptr = ctrl_commands; while (ptr->name) { if (strcmp(ptr->name, cmd) == 0) { return ptr; } ptr++; } return NULL; } static xdebug_str *make_message(xdebug_xml_node *message) { xdebug_str xml_message = XDEBUG_STR_INITIALIZER; xdebug_str *ret = xdebug_str_new(); xdebug_xml_return_node(message, &xml_message); xdebug_str_add_literal(ret, "\n"); xdebug_str_add(ret, xml_message.d, 0); xdebug_str_addc(ret, '\0'); xdebug_str_destroy(&xml_message); return ret; } static void handle_command(int fd, const char *line) { char *cmd = NULL; xdebug_dbgp_arg *args; int res = 0; xdebug_ctrl_cmd *command; xdebug_str *message; xdebug_xml_node *retval; res = xdebug_cmd_parse(line, (char**) &cmd, (xdebug_dbgp_arg**) &args); retval = xdebug_xml_node_init("ctrl-response"); xdebug_xml_add_attribute(retval, "xmlns:xdebug-ctrl", "https://xdebug.org/ctrl/xdebug"); command = lookup_cmd(cmd); if (command) { command->handler(&retval, args); } else { xdebug_xml_node *error; error = xdebug_xml_node_init("error"); xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%lu", XDEBUG_ERROR_COMMAND_UNAVAILABLE), 0, 1); ADD_REASON_MESSAGE(XDEBUG_ERROR_COMMAND_UNAVAILABLE); xdebug_xml_add_child(retval, error); } message = make_message(retval); write(fd, message->d, message->l); xdfree(cmd); xdebug_cmd_arg_dtor(args); } CTRL_FUNC(ps) { xdebug_xml_node *response, *engine, *file, *pid, *time, *memory; char *pid_str, *time_str, *memory_str; function_stack_entry *fse = XDEBUG_VECTOR_HEAD(XG_BASE(stack)); double time_elapsed = XDEBUG_SECONDS_SINCE_START(xdebug_get_nanotime()); response = xdebug_xml_node_init("ps"); xdebug_xml_add_attribute(response, "success", "1"); engine = xdebug_xml_node_init("engine"); xdebug_xml_add_attribute(engine, "version", XDEBUG_VERSION); xdebug_xml_add_text(engine, xdstrdup(XDEBUG_NAME)); xdebug_xml_add_child(response, engine); file = xdebug_xml_node_init("fileuri"); xdebug_xml_add_text(file, ZSTR_VAL(fse->filename)); xdebug_xml_add_child(response, file); pid = xdebug_xml_node_init("pid"); pid_str = xdebug_sprintf("%lu", xdebug_get_pid()); xdebug_xml_add_text(pid, pid_str); xdebug_xml_add_child(response, pid); time = xdebug_xml_node_init("time"); time_str = xdebug_sprintf("%f", time_elapsed); xdebug_xml_add_text(time, time_str); xdebug_xml_add_child(response, time); memory = xdebug_xml_node_init("memory"); memory_str = xdebug_sprintf("%ld", zend_memory_usage(0) / 1024); xdebug_xml_add_text(memory, memory_str); xdebug_xml_add_child(response, memory); xdebug_xml_add_child(*retval, response); } CTRL_FUNC(pause) { xdebug_xml_node *response, *pid, *action; char *pid_str; response = xdebug_xml_node_init("pause"); xdebug_xml_add_attribute(response, "success", "1"); pid = xdebug_xml_node_init("pid"); pid_str = xdebug_sprintf("%lu", xdebug_get_pid()); xdebug_xml_add_text(pid, pid_str); xdebug_xml_add_child(response, pid); if (!XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_xml_node *error; error = xdebug_xml_node_init("error"); xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%lu", XDEBUG_ERROR_STEP_DEBUG_MODE_NOT_ENABLED), 0, 1); ADD_REASON_MESSAGE(XDEBUG_ERROR_STEP_DEBUG_MODE_NOT_ENABLED); xdebug_xml_add_child(*retval, error); xdebug_xml_add_child(*retval, response); return; } if (!xdebug_is_debug_connection_active()) { action = xdebug_xml_node_init("action"); xdebug_xml_add_text(action, xdstrdup("IDE Connection Signalled")); XG_DBG(context).do_connect_to_client = 1; } else { action = xdebug_xml_node_init("action"); xdebug_xml_add_text(action, xdstrdup("Breakpoint Signalled")); XG_DBG(context).do_break = 1; } xdebug_xml_add_child(response, action); xdebug_xml_add_child(*retval, response); } static void xdebug_control_socket_handle(void) { char buffer[256]; int bytes_read; int rc; struct timeval timeout; fd_set master_set, working_set; /* Update last trigger */ XG_BASE(control_socket_last_trigger) = xdebug_get_nanotime(); /* Initialize the master fd_set */ FD_ZERO(&master_set); FD_SET(XG_BASE(control_socket_fd), &master_set); /* non blocking */ timeout.tv_sec = 0; timeout.tv_usec = 0; memcpy(&working_set, &master_set, sizeof(master_set)); rc = select(XG_BASE(control_socket_fd) + 1, &working_set, NULL, NULL, &timeout); if (rc < 0) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "CTRL-SELECT", "Select failed: %s", strerror(errno)); return; } if (rc == 0) { return; } if (FD_ISSET(XG_BASE(control_socket_fd), &working_set)) { int new_sd = accept(XG_BASE(control_socket_fd), NULL, NULL); if (new_sd < 0) { if (errno != EWOULDBLOCK) { fprintf(stdout, " accept() failed: %d: %s", errno, strerror(errno)); } return; } memset(buffer, 0, sizeof(buffer)); bytes_read = read(new_sd, buffer, sizeof(buffer)); if (bytes_read == -1) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "CTRL-RECV", "Can't receive from socket: %s", strerror(errno)); } else { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_INFO, "CTRL-RECV", "Received: '%s'", buffer); handle_command(new_sd, buffer); } close(new_sd); } } void xdebug_control_socket_dispatch(void) { if (!XG_BASE(control_socket_path)) { return; } switch (XINI_BASE(control_socket_granularity)) { case XDEBUG_CONTROL_SOCKET_OFF: return; case XDEBUG_CONTROL_SOCKET_DEFAULT: case XDEBUG_CONTROL_SOCKET_TIME: if (xdebug_get_nanotime() < (XG_BASE(control_socket_last_trigger) + (XINI_BASE(control_socket_threshold_ms) * 1000000))) { return; } break; } xdebug_control_socket_handle(); } void xdebug_control_socket_setup(void) { struct sockaddr_un *servaddr = NULL; /* Initialise control socket globals */ XG_BASE(control_socket_fd) = -1; XG_BASE(control_socket_path) = NULL; XG_BASE(control_socket_last_trigger) = xdebug_get_nanotime(); /* Part 1 – create the socket */ if (0 > (XG_BASE(control_socket_fd) = socket(AF_UNIX, SOCK_STREAM, 0))) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "CTRL-SOCKET", "Can't create control socket"); return; } XG_BASE(control_socket_path) = xdebug_sprintf("xdebug-ctrl." ZEND_ULONG_FMT, xdebug_get_pid()); /* Part 2b — Configure socket */ servaddr = (struct sockaddr_un *)xdmalloc(sizeof(struct sockaddr_un)); if (servaddr == NULL) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "CTRL-ALLOC", "Can't allocate memory"); xdfree(XG_BASE(control_socket_path)); XG_BASE(control_socket_path) = NULL; close(XG_BASE(control_socket_fd)); return; } memset(servaddr, 'x', sizeof(struct sockaddr_un)); servaddr->sun_family = AF_UNIX; snprintf(servaddr->sun_path + 1, strlen(XG_BASE(control_socket_path)) + 1, "%s", XG_BASE(control_socket_path)); servaddr->sun_path[0] = '\0'; servaddr->sun_path[strlen(XG_BASE(control_socket_path)) + 1] = 'y'; if (0 != (bind(XG_BASE(control_socket_fd), (struct sockaddr *)servaddr, sizeof(struct sockaddr_un)))) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "CTRL-BIND", "Can't bind control socket"); xdfree(servaddr); xdfree(XG_BASE(control_socket_path)); XG_BASE(control_socket_path) = NULL; close(XG_BASE(control_socket_fd)); return; } /* Part 3 — Listen */ if (listen(XG_BASE(control_socket_fd), 32) < 0) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "CTRL-LISTEN", "Listen failed: %s", strerror(errno)); xdfree(servaddr); xdfree(XG_BASE(control_socket_path)); XG_BASE(control_socket_path) = NULL; close(XG_BASE(control_socket_fd)); return; } xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_INFO, "CTRL-OK", "Control socket set up successfully: '@%s'", XG_BASE(control_socket_path)); xdfree(servaddr); } void xdebug_control_socket_teardown(void) { if (XG_BASE(control_socket_path)) { close(XG_BASE(control_socket_fd)); xdfree(XG_BASE(control_socket_path)); XG_BASE(control_socket_path) = NULL; } } #endif xdebug-3.4.3/src/base/ctrl_socket.h0000664000175000017500000000230415011062311016440 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_CTRL_SOCKET__ #define __XDEBUG_CTRL_SOCKET__ void xdebug_control_socket_setup(void); void xdebug_control_socket_teardown(void); void xdebug_control_socket_dispatch(void); #endif // __XDEBUG_CTRL_SOCKET__ xdebug-3.4.3/src/lib/usefulstuff.c0000664000175000017500000004461415011062311016340 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include #ifndef WIN32 # include # include # include # include # include #else # define PATH_MAX MAX_PATH # include # include # include #endif #include "php_xdebug.h" #include "mm.h" #include "crc32.h" #include "str.h" #include "lib_private.h" #include "usefulstuff.h" #include "log.h" #if PHP_VERSION_ID >= 80200 # include "ext/random/php_random.h" #else # include "ext/standard/php_lcg.h" #endif #include "ext/standard/flock_compat.h" #include "main/php_ini.h" #ifndef NAME_MAX # ifdef _AIX # include # define NAME_MAX pathconf("/dev/null",_PC_NAME_MAX) # else # define NAME_MAX (MAXNAMELEN-1) # endif #endif ZEND_EXTERN_MODULE_GLOBALS(xdebug) xdebug_arg *xdebug_arg_ctor(void) { xdebug_arg *tmp = /*(xdebug_arg*)*/ xdmalloc(sizeof(xdebug_arg)); tmp->args = NULL; tmp->c = 0; return tmp; } void xdebug_arg_dtor(xdebug_arg *arg) { int i; for (i = 0; i < arg->c; i++) { xdfree(arg->args[i]); } if (arg->args) { xdfree(arg->args); } xdfree(arg); } xdebug_str* xdebug_join(const char *delim, xdebug_arg *args, int begin, int end) { int i; xdebug_str *ret = xdebug_str_new(); if (begin < 0) { begin = 0; } if (end > args->c - 1) { end = args->c - 1; } for (i = begin; i < end; i++) { xdebug_str_add(ret, args->args[i], 0); xdebug_str_add(ret, delim, 0); } xdebug_str_add(ret, args->args[end], 0); return ret; } void xdebug_explode(const char *delim, const char *str, xdebug_arg *args, int limit) { const char *p1, *p2, *endp; endp = str + strlen(str); p1 = str; p2 = xdebug_memnstr(str, delim, strlen(delim), endp); if (p2 == NULL) { args->c++; args->args = (char**) xdrealloc(args->args, sizeof(char*) * args->c); args->args[args->c - 1] = (char*) xdmalloc(strlen(str) + 1); memcpy(args->args[args->c - 1], p1, strlen(str)); args->args[args->c - 1][strlen(str)] = '\0'; } else { do { args->c++; args->args = (char**) xdrealloc(args->args, sizeof(char*) * args->c); args->args[args->c - 1] = (char*) xdmalloc(p2 - p1 + 1); memcpy(args->args[args->c - 1], p1, p2 - p1); args->args[args->c - 1][p2 - p1] = '\0'; p1 = p2 + strlen(delim); } while ((p2 = xdebug_memnstr(p1, delim, strlen(delim), endp)) != NULL && (limit == -1 || --limit > 1)); if (p1 <= endp) { args->c++; args->args = (char**) xdrealloc(args->args, sizeof(char*) * args->c); args->args[args->c - 1] = (char*) xdmalloc(endp - p1 + 1); memcpy(args->args[args->c - 1], p1, endp - p1); args->args[args->c - 1][endp - p1] = '\0'; } } } const char* xdebug_memnstr(const char *haystack, const char *needle, int needle_len, const char *end) { const char *p = haystack; char first = *needle; /* let end point to the last character where needle may start */ end -= needle_len; while (p <= end) { while (*p != first) if (++p > end) return NULL; if (memcmp(p, needle, needle_len) == 0) return p; p++; } return NULL; } char* xdebug_strrstr(const char* haystack, const char* needle) { char *loc = NULL; char *found = NULL; size_t pos = 0; while ((found = strstr(haystack + pos, needle)) != 0) { loc = found; pos = (found - haystack) + 1; } return loc; } char *xdebug_trim(const char *str) { char *trimmed = NULL, *begin = (char *) str, *end = NULL; /* trim leading space */ while (isspace((unsigned char) *begin)) { ++begin; } /* All spaces */ if (*begin == '\0') { return xdstrdup(""); } /* trim trailing space */ end = begin + strlen(begin) - 1; while (end > begin && isspace((unsigned char) *end)) { --end; } end++; trimmed = xdmalloc(end - begin + 1); memcpy(trimmed, begin, (end - begin)); trimmed[(end - begin)] = '\0'; return trimmed; } /* not all versions of php export this */ static int xdebug_htoi(char *s) { int value; int c; c = s[0]; if (isupper(c)) { c = tolower(c); } value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16; c = s[1]; if (isupper(c)) { c = tolower(c); } value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; return value; } /* not all versions of php export this */ int xdebug_raw_url_decode(char *str, int len) { char *dest = str; char *data = str; while (len--) { if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) { *dest = (char) xdebug_htoi(data + 1); data += 2; len -= 2; } else { *dest = *data; } data++; dest++; } *dest = '\0'; return dest - str; } static unsigned char hexchars[] = "0123456789ABCDEF"; char *xdebug_raw_url_encode(char const *s, int len, int *new_length, int skip_slash) { register int x, y; unsigned char *str; str = (unsigned char *) xdmalloc(3 * len + 1); for (x = 0, y = 0; len--; x++, y++) { str[y] = (unsigned char) s[x]; if ((str[y] < '0' && str[y] != '-' && str[y] != '.' && (str[y] != '/' || !skip_slash)) || (str[y] < 'A' && str[y] > '9' && str[y] != ':') || (str[y] > 'Z' && str[y] < 'a' && str[y] != '_' && (str[y] != '\\' || !skip_slash)) || (str[y] > 'z')) { str[y++] = '%'; str[y++] = hexchars[(unsigned char) s[x] >> 4]; str[y] = hexchars[(unsigned char) s[x] & 15]; } } str[y] = '\0'; if (new_length) { *new_length = y; } return ((char *) str); } /* fake URI's per IETF RFC 1738 and 2396 format */ char *xdebug_path_from_url(zend_string *fileurl) { /* deal with file: url's */ char *dfp = NULL; const char *fp = NULL, *efp = ZSTR_VAL(fileurl); #ifdef PHP_WIN32 int l = 0; int i; #endif char *tmp = NULL, *ret = NULL; dfp = xdstrdup(efp); fp = dfp; xdebug_raw_url_decode(dfp, strlen(dfp)); tmp = strstr(fp, "file://"); if (tmp) { fp = tmp + 7; if (fp[0] == '/' && fp[2] == ':') { fp++; } ret = xdstrdup(fp); #ifdef PHP_WIN32 l = strlen(ret); /* convert '/' to '\' */ for (i = 0; i < l; i++) { if (ret[i] == '/') { ret[i] = '\\'; } } #endif } else { ret = xdstrdup(ZSTR_VAL(fileurl)); } free(dfp); return ret; } /* fake URI's per IETF RFC 1738 and 2396 format */ char *xdebug_path_to_url(zend_string *fileurl) { int l, i, new_len; char *tmp = NULL; char *encoded_fileurl; /* encode the url */ encoded_fileurl = xdebug_raw_url_encode(ZSTR_VAL(fileurl), ZSTR_LEN(fileurl), &new_len, 1); if (strstr(ZSTR_VAL(fileurl), "://") != NULL && strstr(ZSTR_VAL(fileurl), "://") < strstr(ZSTR_VAL(fileurl), "/")) { /* ignore, some form of stream wrapper scheme */ tmp = xdstrdup(ZSTR_VAL(fileurl)); } else if (ZSTR_VAL(fileurl)[0] != '/' && ZSTR_VAL(fileurl)[0] != '\\' && ZSTR_VAL(fileurl)[1] != ':') { /* convert relative paths */ cwd_state new_state; char cwd[MAXPATHLEN]; char *result; result = VCWD_GETCWD(cwd, MAXPATHLEN); if (!result) { cwd[0] = '\0'; } new_state.cwd = estrdup(cwd); new_state.cwd_length = strlen(cwd); if (!virtual_file_ex(&new_state, ZSTR_VAL(fileurl), NULL, 1)) { char *s = estrndup(new_state.cwd, new_state.cwd_length); tmp = xdebug_sprintf("file://%s",s); efree(s); } efree(new_state.cwd); } else if (ZSTR_VAL(fileurl)[1] == '/' || ZSTR_VAL(fileurl)[1] == '\\') { /* convert UNC paths (eg. \\server\sharepath) */ /* See https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows */ tmp = xdebug_sprintf("file:%s", encoded_fileurl); } else if (ZSTR_VAL(fileurl)[0] == '/' || ZSTR_VAL(fileurl)[0] == '\\') { /* convert *nix paths (eg. /path) */ tmp = xdebug_sprintf("file://%s", encoded_fileurl); } else if (ZSTR_VAL(fileurl)[1] == ':') { /* convert windows drive paths (eg. c:\path) */ tmp = xdebug_sprintf("file:///%s", encoded_fileurl); } else { /* no clue about it, use it raw */ tmp = xdstrdup(encoded_fileurl); } l = strlen(tmp); /* convert '\' to '/' */ for (i = 0; i < l; i++) { if (tmp[i] == '\\') { tmp[i]='/'; } } xdfree(encoded_fileurl); return tmp; } #ifndef PHP_WIN32 static FILE *xdebug_open_file(char *fname, const char *mode, const char *extension, char **new_fname) { FILE *fh; char *tmp_fname; if (extension) { tmp_fname = xdebug_sprintf("%s.%s", fname, extension); } else { tmp_fname = xdstrdup(fname); } fh = fopen(tmp_fname, mode); if (fh && new_fname) { *new_fname = tmp_fname; } else { xdfree(tmp_fname); } return fh; } static FILE *xdebug_open_file_with_random_ext(char *fname, const char *mode, const char *extension, char **new_fname) { FILE *fh; char *tmp_fname; if (extension) { tmp_fname = xdebug_sprintf("%s.%06x.%s", fname, (long) (1000000 * php_combined_lcg()), extension); } else { tmp_fname = xdebug_sprintf("%s.%06x", fname, (long) (1000000 * php_combined_lcg()), extension); } fh = fopen(tmp_fname, mode); if (fh && new_fname) { *new_fname = tmp_fname; } else { xdfree(tmp_fname); } return fh; } FILE *xdebug_fopen(char *fname, const char *mode, const char *extension, char **new_fname) { int r; FILE *fh; struct stat buf; char *tmp_fname = NULL; int filename_len = 0; /* We're not doing any tricks for append mode... as that has atomic writes * anyway. And we ignore read mode as well. */ if (mode[0] == 'a' || mode[0] == 'r') { return xdebug_open_file(fname, mode, extension, new_fname); } /* Make sure we don't open a file with a path that's too long */ filename_len += (fname ? strlen(fname) : 0); /* filename */ filename_len += (extension ? strlen(extension) : 0) + 1; /* extension (+ ".") */ filename_len += 8; /* possible random extension (+ ".") */ if (filename_len > NAME_MAX) { fname[NAME_MAX - (extension ? strlen(extension) : 0 )] = '\0'; } /* In write mode however we do have to do some stuff. */ /* 1. Check if the file exists */ if (extension) { tmp_fname = xdebug_sprintf("%s.%s", fname, extension); } else { tmp_fname = xdstrdup(fname); } r = stat(tmp_fname, &buf); /* We're not freeing "tmp_fname" as that is used in the freopen as well. */ if (r == -1) { /* 2. Cool, the file doesn't exist so we can open it without probs now. */ fh = xdebug_open_file(fname, "w", extension, new_fname); goto lock; } /* 3. It exists, check if we can open it. */ fh = xdebug_open_file(fname, "r+", extension, new_fname); if (!fh) { /* 4. If fh == null we couldn't even open the file, so open a new one with a new name */ fh = xdebug_open_file_with_random_ext(fname, "w", extension, new_fname); goto lock; } /* 5. It exists and we can open it, check if we can exclusively lock it. */ r = flock(fileno(fh), LOCK_EX | LOCK_NB); if (r == -1) { if (errno == EWOULDBLOCK) { fclose(fh); /* 6. The file is in use, so we open one with a new name. */ fh = xdebug_open_file_with_random_ext(fname, "w", extension, new_fname); goto lock; } } /* 7. We established a lock, now we truncate and return the handle */ fh = freopen(tmp_fname, "w", fh); lock: /* Yes yes, an evil goto label here!!! */ if (fh) { /* 8. We have to lock again after the reopen as that basically closes * the file and opens it again. There is a small race condition here... */ flock(fileno(fh), LOCK_EX | LOCK_NB); } xdfree(tmp_fname); return fh; } #else FILE *xdebug_fopen(char *fname, const char *mode, const char *extension, char **new_fname) { char *tmp_fname; FILE *ret; if (extension) { tmp_fname = xdebug_sprintf("%s.%s", fname, extension); } else { tmp_fname = xdstrdup(fname); } ret = fopen(tmp_fname, mode); if (new_fname) { *new_fname = tmp_fname; } else { xdfree(tmp_fname); } return ret; } #endif int xdebug_format_output_filename(char **filename, char *format, char *script_name) { xdebug_str fname = XDEBUG_STR_INITIALIZER; char cwd[MAXPATHLEN]; while (*format) { if (*format != '%') { xdebug_str_addc(&fname, *format); } else { format++; switch (*format) { case 'c': /* crc32 of the current working directory */ if (VCWD_GETCWD(cwd, MAXPATHLEN - 1)) { xdebug_str_add_fmt(&fname, "%lu", xdebug_crc32(cwd, strlen(cwd))); } break; case 'p': /* pid */ xdebug_str_add_fmt(&fname, ZEND_ULONG_FMT, xdebug_get_pid()); break; case 'r': /* random number */ xdebug_str_add_fmt(&fname, "%06x", (long) (1000000 * php_combined_lcg())); break; case 's': { /* script fname */ char *char_ptr, *script_name_tmp; /* we do not always have script_name available, so if we * don't have it and this format specifier is used then we * simple do nothing for this specifier */ if (!script_name) { break; } /* create a copy to work on */ script_name_tmp = xdstrdup(script_name); /* replace slashes, whitespace and colons with underscores */ while ((char_ptr = strpbrk(script_name_tmp, "/\\: ")) != NULL) { char_ptr[0] = '_'; } /* replace .php with _php */ char_ptr = strrchr(script_name_tmp, '.'); if (char_ptr) { char_ptr[0] = '_'; } xdebug_str_add(&fname, script_name_tmp, 0); xdfree(script_name_tmp); } break; case 't': { /* timestamp (in seconds) */ xdebug_str_add_fmt(&fname, "%lu", xdebug_get_nanotime() / NANOS_IN_SEC); } break; case 'u': { /* timestamp (in microseconds) */ uint64_t nanotime = xdebug_get_nanotime(); xdebug_str_add_fmt(&fname, "%lu.%06d", nanotime / NANOS_IN_SEC, (nanotime % NANOS_IN_SEC) / NANOS_IN_MICROSEC ); } break; case 'H': /* $_SERVER['HTTP_HOST'] */ case 'U': /* $_SERVER['UNIQUE_ID'] */ case 'R': { /* $_SERVER['REQUEST_URI'] */ char *char_ptr, *strval; zval *data = NULL; if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY) { switch (*format) { case 'H': data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_HOST", sizeof("HTTP_HOST") - 1); break; case 'R': data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI") - 1); break; case 'U': data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "UNIQUE_ID", sizeof("UNIQUE_ID") - 1); break; } if (data) { strval = estrdup(Z_STRVAL_P(data)); /* replace slashes, dots, question marks, plus * signs, ampersands, spaces and other evil chars * with underscores */ while ((char_ptr = strpbrk(strval, "/\\.?&+:*\"<>| ")) != NULL) { char_ptr[0] = '_'; } xdebug_str_add(&fname, strval, 0); efree(strval); } } } break; case 'S': { /* session id */ zval *data; char *char_ptr, *strval; char *sess_name; sess_name = zend_ini_string((char*) "session.name", sizeof("session.name"), 0); if (sess_name && Z_TYPE(PG(http_globals)[TRACK_VARS_COOKIE]) == IS_ARRAY && ((data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]), sess_name, strlen(sess_name))) != NULL) && Z_STRLEN_P(data) < 100 /* Prevent any unrealistically long data being set as filename */ ) { strval = estrdup(Z_STRVAL_P(data)); /* replace slashes, dots, question marks, plus signs, * ampersands and spaces with underscores */ while ((char_ptr = strpbrk(strval, "/\\.?&+ ")) != NULL) { char_ptr[0] = '_'; } xdebug_str_add(&fname, strval, 0); efree(strval); } } break; case '%': /* literal % */ xdebug_str_addc(&fname, '%'); break; } } format++; } *filename = fname.d; return fname.l; } int xdebug_format_file_link(char **filename, const char *error_filename, int error_lineno) { xdebug_str fname = XDEBUG_STR_INITIALIZER; char *format = XINI_LIB(file_link_format); while (*format) { if (*format != '%') { xdebug_str_addc(&fname, *format); } else { format++; switch (*format) { case 'f': /* filename */ xdebug_str_add(&fname, error_filename, 0); break; case 'l': /* line number */ xdebug_str_add_fmt(&fname, "%d", error_lineno); break; case '%': /* literal % */ xdebug_str_addc(&fname, '%'); break; } } format++; } *filename = fname.d; return fname.l; } int xdebug_format_filename(char **formatted_name, const char *default_fmt, zend_string *filename) { xdebug_str fname = XDEBUG_STR_INITIALIZER; char *name; xdebug_str *parent, *ancester; const char *full_filename = ZSTR_VAL(filename); xdebug_arg *parts; char *slash = xdebug_sprintf("%c", DEFAULT_SLASH); char *fmt = XINI_LIB(filename_format); const char *format = fmt && fmt[0] ? fmt : default_fmt; /* If the format is empty, we use the default */ /* Create pointers for the format chars */ parts = xdebug_arg_ctor(); xdebug_explode(slash, full_filename, parts, -1); name = parts->args[parts->c - 1]; parent = parts->c > 1 ? xdebug_join(slash, parts, parts->c - 2, parts->c - 1) : xdebug_str_create_from_char(name); ancester = parts->c > 2 ? xdebug_join(slash, parts, parts->c - 3, parts->c - 1) : xdebug_str_copy(parent); while (*format) { if (*format != '%') { xdebug_str_addc(&fname, *format); } else { format++; switch (*format) { case 'n': /* filename */ xdebug_str_add(&fname, name, 0); break; case 'p': /* parent */ xdebug_str_add_str(&fname, parent); break; case 'a': /* ancester */ xdebug_str_add_str(&fname, ancester); break; case 'f': /* full path */ xdebug_str_add(&fname, full_filename, 0); break; case 's': /* slash */ xdebug_str_addc(&fname, DEFAULT_SLASH); break; case '%': /* literal % */ xdebug_str_addc(&fname, '%'); break; } } format++; } xdfree(slash); xdebug_str_free(ancester); xdebug_str_free(parent); xdebug_arg_dtor(parts); *formatted_name = fname.d; return fname.l; } xdebug-3.4.3/src/lib/usefulstuff.h0000664000175000017500000000406715011062311016343 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_USEFULSTUFF_H__ #define __HAVE_USEFULSTUFF_H__ #include "php_xdebug.h" #include "src/lib/compat.h" typedef struct xdebug_arg { int c; char **args; } xdebug_arg; xdebug_arg *xdebug_arg_ctor(void); void xdebug_arg_dtor(xdebug_arg *arg); xdebug_str* xdebug_join(const char *delim, xdebug_arg *args, int begin, int end); void xdebug_explode(const char *delim, const char *str, xdebug_arg *args, int limit); const char* xdebug_memnstr(const char *haystack, const char *needle, int needle_len, const char *end); char* xdebug_strrstr(const char* haystack, const char* needle); char *xdebug_trim(const char *str); char *xdebug_path_to_url(zend_string *fileurl); char *xdebug_path_from_url(zend_string *fileurl); FILE *xdebug_fopen(char *fname, const char *mode, const char *extension, char **new_fname); int xdebug_format_output_filename(char **filename, char *format, char *script_name); int xdebug_format_file_link(char **filename, const char *error_filename, int error_lineno); int xdebug_format_filename(char **formatted_name, const char *default_format, zend_string *filename); #endif xdebug-3.4.3/src/lib/cmd_parser.c0000664000175000017500000001067215011062311016101 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "usefulstuff.h" #include "cmd_parser.h" /* {{{ Constants for state machine */ #define STATE_NORMAL 0 #define STATE_QUOTED 1 #define STATE_OPT_FOLLOWS 2 #define STATE_SEP_FOLLOWS 3 #define STATE_VALUE_FOLLOWS_FIRST_CHAR 4 #define STATE_VALUE_FOLLOWS 5 #define STATE_SKIP_CHAR 6 #define STATE_ESCAPED_CHAR_FOLLOWS 7 /* }}} */ void xdebug_cmd_arg_dtor(xdebug_dbgp_arg *arg) { int i; for (i = 0; i < 27; i++) { if (arg->value[i]) { xdebug_str_free(arg->value[i]); } } xdfree(arg); } int xdebug_cmd_parse(const char *line, char **cmd, xdebug_dbgp_arg **ret_args) { xdebug_dbgp_arg *args = NULL; char *ptr; int state; int charescaped = 0; char opt = ' ', *value_begin = NULL; args = xdmalloc(sizeof (xdebug_dbgp_arg)); memset(args->value, 0, sizeof(args->value)); *cmd = NULL; /* Find the end of the command, this is always on the first space */ ptr = strchr(line, ' '); if (!ptr) { /* No space found. If the line is not empty, return the line * and assume it only consists of the command name. If the line * is 0 chars long, we return a failure. */ if (strlen(line)) { *cmd = strdup(line); *ret_args = args; return XDEBUG_ERROR_OK; } else { goto parse_error; } } else { /* A space was found, so we copy everything before it * into the cmd parameter. */ *cmd = xdcalloc(1, ptr - line + 1); memcpy(*cmd, line, ptr - line); } /* Now we loop until we find the end of the string, which is the \0 * character */ state = STATE_NORMAL; do { ptr++; switch (state) { case STATE_NORMAL: if (*ptr != '-') { goto parse_error; } else { state = STATE_OPT_FOLLOWS; } break; case STATE_OPT_FOLLOWS: opt = *ptr; state = STATE_SEP_FOLLOWS; break; case STATE_SEP_FOLLOWS: if (*ptr != ' ') { goto parse_error; } else { state = STATE_VALUE_FOLLOWS_FIRST_CHAR; value_begin = ptr + 1; } break; case STATE_VALUE_FOLLOWS_FIRST_CHAR: if (*ptr == '"' && opt != '-') { value_begin = ptr + 1; state = STATE_QUOTED; } else { state = STATE_VALUE_FOLLOWS; } break; case STATE_VALUE_FOLLOWS: if ((*ptr == ' ' && opt != '-') || *ptr == '\0') { int opt_index = opt - 'a'; if (opt == '-') { opt_index = 26; } if (!args->value[opt_index]) { args->value[opt_index] = xdebug_str_create(value_begin, ptr - value_begin); state = STATE_NORMAL; } else { goto duplicate_opts; } } break; case STATE_QUOTED: if (*ptr == '\\') { state = STATE_ESCAPED_CHAR_FOLLOWS; } else if (*ptr == '"') { int opt_index = opt - 'a'; if (charescaped) { charescaped = 0; break; } if (opt == '-') { opt_index = 26; } if (!args->value[opt_index]) { int len = ptr - value_begin; args->value[opt_index] = xdebug_str_create(value_begin, len); xdebug_stripcslashes(args->value[opt_index]->d, &len); args->value[opt_index]->l = len; state = STATE_SKIP_CHAR; } else { goto duplicate_opts; } } break; case STATE_SKIP_CHAR: state = STATE_NORMAL; break; case STATE_ESCAPED_CHAR_FOLLOWS: state = STATE_QUOTED; break; } } while (*ptr); *ret_args = args; return XDEBUG_ERROR_OK; parse_error: *ret_args = args; return XDEBUG_ERROR_PARSE; duplicate_opts: *ret_args = args; return XDEBUG_ERROR_DUP_ARG; } xdebug-3.4.3/src/lib/cmd_parser.h0000664000175000017500000000423615011062311016105 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_CMD_PARSER_H__ #define __HAVE_CMD_PARSER_H__ #include "php_xdebug.h" #include "src/lib/usefulstuff.h" /* Argument structure */ typedef struct xdebug_dbgp_arg { xdebug_str *value[27]; /* one extra for - */ } xdebug_dbgp_arg; #define CMD_OPTION_SET(opt) (!!(opt == '-' ? args->value[26] : args->value[(opt) - 'a'])) #define CMD_OPTION_CHAR(opt) (opt == '-' ? args->value[26]->d : args->value[(opt) - 'a']->d) #define CMD_OPTION_LEN(opt) (opt == '-' ? args->value[26]->l : args->value[(opt) - 'a']->l) #define CMD_OPTION_XDEBUG_STR(opt) (opt == '-' ? args->value[26] : args->value[(opt) - 'a']) void xdebug_cmd_arg_dtor(xdebug_dbgp_arg *arg); int xdebug_cmd_parse(const char *line, char **cmd, xdebug_dbgp_arg **ret_args); #define XDEBUG_ERROR_OK 0 #define XDEBUG_ERROR_PARSE 1 #define XDEBUG_ERROR_DUP_ARG 2 #define XDEBUG_ERROR_INVALID_ARGS 3 #define XDEBUG_ERROR_UNIMPLEMENTED 4 #define XDEBUG_ERROR_COMMAND_UNAVAILABLE 5 #define XDEBUG_ERROR_STEP_DEBUG_MODE_NOT_ENABLED 400 #define XDEBUG_ERROR_STEP_DEBUG_NOT_STARTED 401 #endif xdebug-3.4.3/src/lib/compat.c0000664000175000017500000003126215011062311015243 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | | (c) 1997-2004 Jim Winstead | | (c) 1998-2004 Andi Gutmans and | | Zeev Suraski | +----------------------------------------------------------------------+ | This source file is subject to the following Modified BSD license: | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions | | are met: | | | | 1. Redistributions of source code must retain the above copyright | | notice, this list of conditions and the following disclaimer. | | | | 2. Redistributions in binary form must reproduce the above | | copyright notice, this list of conditions and the following | | disclaimer in the documentation and/or other materials provided | | with the distribution. | | | | 3. The name of the author may not be used to endorse or promote | | products derived from this software without specific prior | | written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN | | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "main/php_version.h" #include "compat.h" #include "zend_extensions.h" #include "zend_compile.h" #include "ext/standard/base64.h" #include "ext/standard/php_string.h" char *xdebug_str_to_str(char *haystack, size_t length, const char *needle, size_t needle_len, const char *str, size_t str_len, size_t *new_len) { zend_string *new_str; char *retval; new_str = php_str_to_str(haystack, length, (char*) needle, needle_len, (char*) str, str_len); *new_len = new_str->len; retval = estrndup(new_str->val, new_str->len); zend_string_release(new_str); return retval; } /* {{{ base64 tables */ static const char base64_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' }; static const char base64_pad = '='; static const short base64_reverse_table[256] = { -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2, -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }; /* }}} */ static unsigned char *xdebug_base64_encode_impl(const unsigned char *in, size_t inl, unsigned char *out) /* {{{ */ { while (inl > 2) { /* keep going until we have less than 24 bits */ *out++ = base64_table[in[0] >> 2]; *out++ = base64_table[((in[0] & 0x03) << 4) + (in[1] >> 4)]; *out++ = base64_table[((in[1] & 0x0f) << 2) + (in[2] >> 6)]; *out++ = base64_table[in[2] & 0x3f]; in += 3; inl -= 3; /* we just handle 3 octets of data */ } /* now deal with the tail end of things */ if (inl != 0) { *out++ = base64_table[in[0] >> 2]; if (inl > 1) { *out++ = base64_table[((in[0] & 0x03) << 4) + (in[1] >> 4)]; *out++ = base64_table[(in[1] & 0x0f) << 2]; *out++ = base64_pad; } else { *out++ = base64_table[(in[0] & 0x03) << 4]; *out++ = base64_pad; *out++ = base64_pad; } } *out = '\0'; return out; } /* }}} */ static int xdebug_base64_decode_impl(const unsigned char *in, size_t inl, unsigned char *out, size_t *outl, zend_bool strict) /* {{{ */ { int ch; size_t i = 0, padding = 0, j = *outl; /* run through the whole string, converting as we go */ while (inl-- > 0) { ch = *in++; if (ch == base64_pad) { padding++; continue; } ch = base64_reverse_table[ch]; if (!strict) { /* skip unknown characters and whitespace */ if (ch < 0) { continue; } } else { /* skip whitespace */ if (ch == -1) { continue; } /* fail on bad characters or if any data follows padding */ if (ch == -2 || padding) { goto fail; } } switch (i % 4) { case 0: out[j] = ch << 2; break; case 1: out[j++] |= ch >> 4; out[j] = (ch & 0x0f) << 4; break; case 2: out[j++] |= ch >>2; out[j] = (ch & 0x03) << 6; break; case 3: out[j++] |= ch; break; } i++; } /* fail if the input is truncated (only one char in last group) */ if (strict && i % 4 == 1) { goto fail; } /* fail if the padding length is wrong (not VV==, VVV=), but accept zero padding * RFC 4648: "In some circumstances, the use of padding [--] is not required" */ if (strict && padding && (padding > 2 || (i + padding) % 4 != 0)) { goto fail; } *outl = j; out[j] = '\0'; return 1; fail: return 0; } /* }}} */ unsigned char *xdebug_base64_encode(unsigned char *data, size_t data_len, size_t *new_len) { unsigned char *retval = xdmalloc((((data_len + 2) / 3) + 1) * (4 * sizeof(char))); unsigned char *end; end = xdebug_base64_encode_impl(data, data_len, retval); *new_len = end - retval; return retval; } unsigned char *xdebug_base64_decode(unsigned char *data, size_t data_len, size_t *new_len) { unsigned char *retval = xdmalloc(data_len + 1); xdebug_base64_decode_impl(data, data_len, retval, new_len, 0); return retval; } zend_string *xdebug_addslashes(zend_string *str) { /* maximum string length, worst case situation */ char *target; const char *source, *end; size_t offset; zend_string *new_str; if (!str) { return ZSTR_EMPTY_ALLOC(); } source = ZSTR_VAL(str); end = source + ZSTR_LEN(str); while (source < end) { switch (*source) { case '\0': case '\'': case '\"': case '\\': goto do_escape; default: source++; break; } } return zend_string_copy(str); do_escape: offset = source - (char *)ZSTR_VAL(str); new_str = zend_string_safe_alloc(2, ZSTR_LEN(str) - offset, offset, 0); memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), offset); target = ZSTR_VAL(new_str) + offset; while (source < end) { switch (*source) { case '\0': *target++ = '\\'; *target++ = '0'; break; case '\'': case '\"': case '\\': *target++ = '\\'; XDEBUG_BREAK_INTENTIONALLY_MISSING default: *target++ = *source; break; } source++; } *target = '\0'; if (ZSTR_LEN(new_str) - (target - ZSTR_VAL(new_str)) > 16) { new_str = zend_string_truncate(new_str, target - ZSTR_VAL(new_str), 0); } else { ZSTR_LEN(new_str) = target - ZSTR_VAL(new_str); } return new_str; } void xdebug_stripcslashes(char *str, int *len) { char *source, *target, *end; int nlen = *len, i; char numtmp[4]; for (source=str, end=str+nlen, target=str; source < end; source++) { if (*source == '\\' && source+1 < end) { source++; switch (*source) { case 'n': *target++='\n'; nlen--; break; case 'r': *target++='\r'; nlen--; break; case 'a': *target++='\a'; nlen--; break; case 't': *target++='\t'; nlen--; break; case 'v': *target++='\v'; nlen--; break; case 'b': *target++='\b'; nlen--; break; case 'f': *target++='\f'; nlen--; break; case '\\': *target++='\\'; nlen--; break; case 'x': if (source+1 < end && isxdigit((int)(*(source+1)))) { numtmp[0] = *++source; if (source+1 < end && isxdigit((int)(*(source+1)))) { numtmp[1] = *++source; numtmp[2] = '\0'; nlen-=3; } else { numtmp[1] = '\0'; nlen-=2; } *target++=(char)strtol(numtmp, NULL, 16); break; } XDEBUG_BREAK_INTENTIONALLY_MISSING default: i=0; while (source < end && *source >= '0' && *source <= '7' && i<3) { numtmp[i++] = *source++; } if (i) { numtmp[i]='\0'; *target++=(char)strtol(numtmp, NULL, 8); nlen-=i; source--; } else { *target++=*source; nlen--; } } } else { *target++=*source; } } if (nlen != 0) { *target='\0'; } *len = nlen; } zend_ulong xdebug_get_pid(void) { #ifndef ZTS return (zend_ulong) getpid(); #else # ifdef _WIN32 return (zend_ulong) GetCurrentThreadId(); # else return (zend_ulong) pthread_self(); # endif #endif } void xdebug_setcookie(const char *name, int name_len, char *value, int value_len, time_t expires, const char *path, int path_len, const char *domain, int domain_len, int secure, int url_encode, int httponly) { zend_string *name_s = name ? zend_string_init(name, name_len, 0) : NULL; zend_string *value_s = value ? zend_string_init(value, value_len, 0) : NULL; zend_string *path_s = path ? zend_string_init(path, path_len, 0) : NULL; zend_string *domain_s = domain ? zend_string_init(domain, domain_len, 0) : NULL; zend_string *samesite_s = zend_string_init("Lax", sizeof("Lax") - 1, 0); php_setcookie(name_s, value_s, expires, path_s, domain_s, secure, httponly, samesite_s, url_encode); zend_string_release(samesite_s); name ? zend_string_release(name_s) : 0; value ? zend_string_release(value_s) : 0; path ? zend_string_release(path_s) : 0; domain ? zend_string_release(domain_s) : 0; } char *xdebug_get_compiled_variable_name(zend_op_array *op_array, uint32_t var, int *cv_len) { zend_string *cv = NULL; cv = zend_get_compiled_variable_name(op_array, var); *cv_len = cv->len; return cv->val; } #ifdef ZEND_HASH_GET_APPLY_COUNT /* PHP 7.2 or earlier recursion protection */ zend_bool xdebug_zend_hash_is_recursive(HashTable* ht) { return (ZEND_HASH_GET_APPLY_COUNT(ht) > 0); } zend_bool xdebug_zend_hash_apply_protection_begin(HashTable* ht) { if (!ht) { return 1; } if (ZEND_HASH_GET_APPLY_COUNT(ht) > 0) { return 0; } if (ZEND_HASH_APPLY_PROTECTION(ht)) { ZEND_HASH_INC_APPLY_COUNT(ht); } return 1; } zend_bool xdebug_zend_hash_apply_protection_end(HashTable* ht) { if (!ht) { return 1; } if (ZEND_HASH_GET_APPLY_COUNT(ht) == 0) { return 0; } if (ZEND_HASH_APPLY_PROTECTION(ht)) { ZEND_HASH_DEC_APPLY_COUNT(ht); } return 1; } #else /* PHP 7.3 or later */ zend_bool xdebug_zend_hash_is_recursive(HashTable* ht) { return GC_IS_RECURSIVE(ht); } zend_bool xdebug_zend_hash_apply_protection_begin(zend_array* ht) { if (GC_IS_RECURSIVE(ht)) { return 0; } if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { GC_PROTECT_RECURSION(ht); } return 1; } zend_bool xdebug_zend_hash_apply_protection_end(zend_array* ht) { if (!GC_IS_RECURSIVE(ht)) { return 0; } if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { GC_UNPROTECT_RECURSION(ht); } return 1; } #endif xdebug-3.4.3/src/lib/compat.h0000664000175000017500000000667315011062311015260 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_COMPAT_H__ #define __HAVE_XDEBUG_COMPAT_H__ #include "lib/php-header.h" #include "str.h" #include "ext/standard/head.h" #include "ext/standard/php_var.h" #define xdebug_php_var_dump php_var_dump char *xdebug_str_to_str(char *haystack, size_t length, const char *needle, size_t needle_len, const char *str, size_t str_len, size_t *new_len); unsigned char *xdebug_base64_encode(unsigned char *data, size_t data_len, size_t *new_len); unsigned char *xdebug_base64_decode(unsigned char *data, size_t data_len, size_t *new_len); zend_string *xdebug_addslashes(zend_string *str); void xdebug_stripcslashes(char *string, int *new_len); zend_ulong xdebug_get_pid(void); void xdebug_setcookie(const char *name, int name_len, char *value, int value_len, time_t expires, const char *path, int path_len, const char *domain, int domain_len, int secure, int url_encode, int httponly); char *xdebug_get_compiled_variable_name(zend_op_array *op_array, uint32_t var, int *cv_len); /* Recursion protection is done differently from PHP 7.3 onwards */ zend_bool xdebug_zend_hash_is_recursive(HashTable* ht); zend_bool xdebug_zend_hash_apply_protection_begin(HashTable* ht); zend_bool xdebug_zend_hash_apply_protection_end(HashTable* ht); #ifndef ZSTR_INIT_LITERAL # define ZSTR_INIT_LITERAL(s, persistent) (zend_string_init((s), strlen(s), (persistent))) #endif # define XDEBUG_MAKE_STD_ZVAL(zv) \ zv = ecalloc(1, sizeof(zval)); # define HASH_KEY_SIZEOF(k) (sizeof(k) - 1) # define HASH_KEY_STRLEN(k) (strlen(k)) # define HASH_KEY_IS_NUMERIC(k) ((k) == NULL) # define HASH_APPLY_KEY_VAL(k) (k)->val # define HASH_APPLY_KEY_LEN(k) (k)->len + 1 # define STR_NAME_VAL(k) (k)->val # define STR_NAME_LEN(k) (k)->len # if defined (__GNUC__) # define XDEBUG_GNUC_CHECK_VERSION(major, minor) \ ((__GNUC__ > (major)) || \ ((__GNUC__ == (major)) && (__GNUC_MINOR__ >= (minor)))) # else # define XDEBUG_GNUC_CHECK_VERSION(major, minor) 0 # endif # if XDEBUG_GNUC_CHECK_VERSION(7, 0) # define XDEBUG_BREAK_INTENTIONALLY_MISSING __attribute__ ((fallthrough)); # else # define XDEBUG_BREAK_INTENTIONALLY_MISSING # endif # if XDEBUG_GNUC_CHECK_VERSION(2, 7) || __has_attribute(format) # define XDEBUG_ATTRIBUTE_FORMAT(type, idx, first) __attribute__ ((format(type, idx, first))) # else # define XDEBUG_ATTRIBUTE_FORMAT(type, idx, first) # endif #define XDEBUG_OPCODE_HANDLER_ARGS zend_execute_data *execute_data #define XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU execute_data #endif xdebug-3.4.3/src/lib/crc32.c0000664000175000017500000001057215011062311014675 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #define XDEBUG_CRC32(crc, ch) (crc = (crc >> 8) ^ xdebug_crc32tab[(crc ^ (ch)) & 0xff]) static const unsigned int xdebug_crc32tab[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; long xdebug_crc32(const char *string, int str_len) { unsigned int crc = ~0; int len; len = 0 ; for (len += str_len; str_len--; ++string) { XDEBUG_CRC32(crc, *string); } return ~crc; } xdebug-3.4.3/src/lib/crc32.h0000664000175000017500000000213315011062311014674 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_LIB_CRC32_H__ #define __HAVE_LIB_CRC32_H__ long xdebug_crc32(const char *string, int str_len); #endif xdebug-3.4.3/src/lib/file.c0000664000175000017500000001222415011062311014674 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "lib_private.h" #include "log.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) #include "file.h" void xdebug_file_init(xdebug_file *xf) { xf->type = XDEBUG_FILE_TYPE_NULL; xf->fp.normal = NULL; #if HAVE_XDEBUG_ZLIB xf->fp.gz = NULL; #endif xf->name = NULL; } xdebug_file *xdebug_file_ctor(void) { xdebug_file *tmp = xdmalloc(sizeof(xdebug_file)); xdebug_file_init(tmp); return tmp; } void xdebug_file_deinit(xdebug_file *xf) { xf->type = XDEBUG_FILE_TYPE_NULL; xf->fp.normal = NULL; #if HAVE_XDEBUG_ZLIB xf->fp.gz = NULL; #endif xdfree(xf->name); } void xdebug_file_dtor(xdebug_file *xf) { xdebug_file_deinit(xf); xdfree(xf); } int xdebug_file_open(xdebug_file *file, const char *filename, const char *extension, const char *mode) { if (XINI_LIB(use_compression)) { #ifdef HAVE_XDEBUG_ZLIB FILE *tmp_file; char *combined_extension; /* Can't use profiler append with compression */ if (strcmp(mode, "ab") == 0) { xdebug_log_ex( XLOG_CHAN_CONFIG, XLOG_WARN, "ZLIB-A", "Cannot append to profiling file while file compression is turned on. Falling back to creating an uncompressed file" ); goto uncompressed; } combined_extension = extension ? xdebug_sprintf("%s.gz", extension) : xdstrdup("gz"); tmp_file = xdebug_fopen((char*) filename, mode, combined_extension, &(file->name)); xdfree(combined_extension); if (!tmp_file) { return 0; } file->type = XDEBUG_FILE_TYPE_GZ; file->fp.normal = tmp_file; file->fp.gz = gzdopen(fileno(tmp_file), mode); if (!file->fp.gz) { fclose(tmp_file); return 0; } return 1; #else char *combined_extension; combined_extension = extension ? xdebug_sprintf("%s.gz", extension) : xdstrdup("gz"); xdebug_log_ex( XLOG_CHAN_CONFIG, XLOG_WARN, "NOZLIB", "Cannot create the compressed file '%s.%s', because support for zlib has not been compiled in. Falling back to '%s%s%s'", filename, combined_extension, filename, extension ? "." : "", extension ? extension : "" ); xdfree(combined_extension); #endif } #ifdef HAVE_XDEBUG_ZLIB uncompressed: #endif file->type = XDEBUG_FILE_TYPE_NORMAL; file->fp.normal = xdebug_fopen((char*) filename, mode, extension, &(file->name)); if (!file->fp.normal) { return 0; } return 1; } int XDEBUG_ATTRIBUTE_FORMAT(printf, 2, 3) xdebug_file_printf(xdebug_file *file, const char *fmt, ...) { va_list argv; switch (file->type) { case XDEBUG_FILE_TYPE_NORMAL: va_start(argv, fmt); vfprintf(file->fp.normal, fmt, argv); va_end(argv); break; #if HAVE_XDEBUG_ZLIB case XDEBUG_FILE_TYPE_GZ: { xdebug_str formatted_string = XDEBUG_STR_INITIALIZER; va_start(argv, fmt); xdebug_str_add_va_fmt(&formatted_string, fmt, argv); va_end(argv); gzwrite(file->fp.gz, formatted_string.d, formatted_string.l); xdebug_str_destroy(&formatted_string); break; } #endif default: xdebug_log_ex(XLOG_CHAN_BASE, XLOG_CRIT, "FTYPE", "Unknown file type used with '%s'", file->name); return 0; } return 1; } int xdebug_file_flush(xdebug_file *file) { switch (file->type) { case XDEBUG_FILE_TYPE_NORMAL: return fflush(file->fp.normal); #if HAVE_XDEBUG_ZLIB case XDEBUG_FILE_TYPE_GZ: return gzflush(file->fp.gz, Z_FULL_FLUSH); #endif default: xdebug_log_ex(XLOG_CHAN_BASE, XLOG_CRIT, "FTYPE", "Unknown file type used with '%s'", file->name); return EOF; } } int xdebug_file_close(xdebug_file *file) { switch (file->type) { case XDEBUG_FILE_TYPE_NORMAL: return fclose(file->fp.normal); #if HAVE_XDEBUG_ZLIB case XDEBUG_FILE_TYPE_GZ: { int gzret; gzret = gzclose(file->fp.gz); fclose(file->fp.normal); return gzret; } #endif default: xdebug_log_ex(XLOG_CHAN_BASE, XLOG_CRIT, "FTYPE", "Unknown file type used with '%s'", file->name); return EOF; } } size_t xdebug_file_write(const void *ptr, size_t size, size_t nmemb, xdebug_file *file) { switch (file->type) { case XDEBUG_FILE_TYPE_NORMAL: return fwrite(ptr, size, nmemb, file->fp.normal); #if HAVE_XDEBUG_ZLIB case XDEBUG_FILE_TYPE_GZ: return gzfwrite(ptr, size, nmemb, file->fp.gz); #endif default: xdebug_log_ex(XLOG_CHAN_BASE, XLOG_CRIT, "FTYPE", "Unknown file type used with '%s'", file->name); return EOF; } } xdebug-3.4.3/src/lib/file.h0000664000175000017500000000367615011062311014714 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_LIB_FILE_H__ #define __HAVE_LIB_FILE_H__ #include "php_xdebug.h" #include "src/lib/compat.h" #if HAVE_XDEBUG_ZLIB # include #endif #define XDEBUG_FILE_TYPE_NULL 0 #define XDEBUG_FILE_TYPE_NORMAL 1 #if HAVE_XDEBUG_ZLIB # define XDEBUG_FILE_TYPE_GZ 2 #endif typedef struct _xdebug_file { int type; struct { FILE *normal; #if HAVE_XDEBUG_ZLIB gzFile gz; #endif } fp; char *name; } xdebug_file; xdebug_file *xdebug_file_ctor(void); void xdebug_file_dtor(xdebug_file *xf); void xdebug_file_init(xdebug_file *xf); void xdebug_file_deinit(xdebug_file *xf); int xdebug_file_open(xdebug_file *file, const char *filename, const char *extension, const char *mode); int xdebug_file_flush(xdebug_file *file); int XDEBUG_ATTRIBUTE_FORMAT(printf, 2, 3) xdebug_file_printf(xdebug_file *file, const char *fmt, ...); size_t xdebug_file_write(const void *ptr, size_t size, size_t nmemb, xdebug_file *file); int xdebug_file_close(xdebug_file *file); #endif xdebug-3.4.3/src/lib/hash.c0000664000175000017500000001744615011062311014713 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include #include "hash.h" #include "llist.h" /* * Helper function to make a null terminated string from a key */ char *xdebug_hash_key_to_str(xdebug_hash_key* key, int* new_len) { char *tmp; tmp = calloc (key->value.str.len + 1, 1); memcpy(tmp, key->value.str.val, key->value.str.len); *new_len = key->value.str.len; return tmp; } static xdebug_ui32 xdebug_hash_str(const char *key, unsigned int key_length) { char *p = (char *) key; char *end = (char *) key + key_length; unsigned long h = 5381; while (p < end) { h += h << 5; h ^= (unsigned long) *p++; } return h; } static xdebug_ui32 xdebug_hash_num(xdebug_ui32 key) { key += ~(key << 15); key ^= (key >> 10); key += (key << 3); key ^= (key >> 6); key += (key << 11); key ^= (key >> 16); return key; } static void hash_element_dtor(void *u, void *ele) { xdebug_hash_element *e = (xdebug_hash_element *) ele; xdebug_hash *h = (xdebug_hash *) u; if (e->key.type == XDEBUG_HASH_KEY_IS_STRING) { free(e->key.value.str.val); } if (h->dtor) { h->dtor(e->ptr); } free(e); e = NULL; } xdebug_hash *xdebug_hash_alloc(int slots, xdebug_hash_dtor_t dtor) { xdebug_hash *h; int i; h = malloc(sizeof(xdebug_hash)); h->dtor = dtor; h->sorter = NULL; h->size = 0; h->slots = slots; h->table = (xdebug_llist **) malloc(slots * sizeof(xdebug_llist *)); for (i = 0; i < h->slots; ++i) { h->table[i] = xdebug_llist_alloc((xdebug_llist_dtor) hash_element_dtor); } return h; } xdebug_hash *xdebug_hash_alloc_with_sort(int slots, xdebug_hash_dtor_t dtor, xdebug_hash_apply_sorter_t sorter) { xdebug_hash *h; h = xdebug_hash_alloc(slots, dtor); h->sorter = sorter; return h; } #define FIND_SLOT(__h, __s_key, __s_key_len, __n_key) \ ((__s_key ? xdebug_hash_str(__s_key, __s_key_len) : xdebug_hash_num(__n_key)) % (__h)->slots) #define KEY_CREATE(__k, __s_key, __s_key_len, __n_key, __dup) \ if (__s_key) { \ if (__dup) { \ (__k)->value.str.val = (char *) malloc(__s_key_len); \ memcpy((__k)->value.str.val, __s_key, __s_key_len); \ } else { \ (__k)->value.str.val = __s_key; \ } \ (__k)->value.str.len = __s_key_len; \ (__k)->type = XDEBUG_HASH_KEY_IS_STRING; \ } else { \ (__k)->value.str.len = 0; \ (__k)->value.num = __n_key; \ (__k)->type = XDEBUG_HASH_KEY_IS_NUM; \ } static int xdebug_hash_key_compare(xdebug_hash_key *key1, xdebug_hash_key *key2) { if (key1->type == XDEBUG_HASH_KEY_IS_NUM) { if (key2->type == XDEBUG_HASH_KEY_IS_STRING) return 0; if (key1->value.num == key2->value.num) return 1; } else { if (key2->type == XDEBUG_HASH_KEY_IS_NUM) return 0; if (key1->value.str.len == key2->value.str.len && *key1->value.str.val == *key2->value.str.val && memcmp(key1->value.str.val, key2->value.str.val, key1->value.str.len) == 0) { return 1; } } return 0; } int xdebug_hash_add_or_update(xdebug_hash *h, const char *str_key, unsigned int str_key_len, unsigned long num_key, const void *p) { xdebug_hash_element *e; xdebug_hash_key tmp; xdebug_llist *l; xdebug_llist_element *le; int slot; slot = FIND_SLOT(h, str_key, str_key_len, num_key); l = h->table[slot]; KEY_CREATE(&tmp, (char*) str_key, str_key_len, num_key, 0); for (le = XDEBUG_LLIST_HEAD(l); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { if (xdebug_hash_key_compare(&tmp, &((xdebug_hash_element *) XDEBUG_LLIST_VALP(le))->key)) { xdebug_hash_element *to_update = XDEBUG_LLIST_VALP(le); if (h->dtor) { h->dtor(to_update->ptr); } to_update->ptr = (void *) p; return 1; } } e = (xdebug_hash_element *) malloc(sizeof(xdebug_hash_element)); KEY_CREATE(&e->key, (char*) str_key, str_key_len, num_key, 1); e->ptr = (void *) p; if (xdebug_llist_insert_next(l, XDEBUG_LLIST_TAIL(l), e)) { ++h->size; return 1; } else { return 0; } } int xdebug_hash_extended_delete(xdebug_hash *h, const char *str_key, unsigned int str_key_len, xdebug_ui32 num_key) { xdebug_llist *l; xdebug_llist_element *le; xdebug_hash_key tmp; int slot; slot = FIND_SLOT(h, str_key, str_key_len, num_key); l = h->table[slot]; KEY_CREATE(&tmp, (char*) str_key, str_key_len, num_key, 0); for (le = XDEBUG_LLIST_HEAD(l); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { if (xdebug_hash_key_compare(&tmp, &((xdebug_hash_element *) XDEBUG_LLIST_VALP(le))->key)) { xdebug_llist_remove(l, le, (void *) h); --h->size; return 1; } } return 0; } int xdebug_hash_extended_find(xdebug_hash *h, const char *str_key, unsigned int str_key_len, xdebug_ui32 num_key, void **p) { xdebug_llist *l; xdebug_llist_element *le; xdebug_hash_key tmp; int slot; slot = FIND_SLOT(h, str_key, str_key_len, num_key); l = h->table[slot]; KEY_CREATE(&tmp, (char*) str_key, str_key_len, num_key, 0); for (le = XDEBUG_LLIST_HEAD(l); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { if (xdebug_hash_key_compare(&tmp, &((xdebug_hash_element *) XDEBUG_LLIST_VALP(le))->key)) { *p = ((xdebug_hash_element *) XDEBUG_LLIST_VALP(le))->ptr; return 1; } } return 0; } void xdebug_hash_apply(xdebug_hash *h, void *user, void (*cb)(void *, xdebug_hash_element *)) { xdebug_llist_element *le; int i; for (i = 0; i < h->slots; ++i) { for (le = XDEBUG_LLIST_HEAD(h->table[i]); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { cb(user, (xdebug_hash_element *) XDEBUG_LLIST_VALP(le)); } } } void xdebug_hash_apply_with_argument(xdebug_hash *h, void *user, void (*cb)(void *, xdebug_hash_element *, void *), void *argument) { xdebug_llist_element *le; int i; int num_items = 0; xdebug_hash_element **pp_he_list; if (h->sorter) { for (i = 0; i < h->slots; ++i) { for (le = XDEBUG_LLIST_HEAD(h->table[i]); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { num_items += 1; } } pp_he_list = (xdebug_hash_element **) malloc((size_t) (num_items * sizeof(xdebug_hash_element **))); if (pp_he_list) { int j = 0; for (i = 0; i < h->slots; ++i) { for (le = XDEBUG_LLIST_HEAD(h->table[i]); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { pp_he_list[j++] = XDEBUG_LLIST_VALP(le); } } qsort(pp_he_list, num_items, sizeof(xdebug_llist_element *), h->sorter); for (i = 0; i < num_items; ++i) { cb(user, pp_he_list[i], argument); } free((void *) pp_he_list); return; } } for (i = 0; i < h->slots; ++i) { for (le = XDEBUG_LLIST_HEAD(h->table[i]); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { cb(user, (xdebug_hash_element *) XDEBUG_LLIST_VALP(le), argument); } } } void xdebug_hash_destroy(xdebug_hash *h) { int i; for (i = 0; i < h->slots; ++i) { xdebug_llist_destroy(h->table[i], (void *) h); } free(h->table); free(h); } /* * Local Variables: * c-basic-offset: 4 * tab-width: 4 * End: * vim600: fdm=marker * vim: noet sw=4 ts=4 */ xdebug-3.4.3/src/lib/hash.h0000664000175000017500000000655615011062311014720 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_HASH_H__ #define __XDEBUG_HASH_H__ #include #include "llist.h" #define XDEBUG_HASH_KEY_IS_STRING 0 #define XDEBUG_HASH_KEY_IS_NUM 1 #define xdebug_ui32 unsigned long typedef void (*xdebug_hash_dtor_t)(void *); typedef int (*xdebug_hash_apply_sorter_t)(const void *le1, const void *le2); typedef struct _xdebug_hash { xdebug_llist **table; xdebug_hash_dtor_t dtor; xdebug_hash_apply_sorter_t sorter; int slots; size_t size; } xdebug_hash; typedef struct _xdebug_hash_key { union { struct { char *val; unsigned int len; } str; unsigned long num; } value; int type; } xdebug_hash_key; typedef struct _xdebug_hash_element { void *ptr; xdebug_hash_key key; } xdebug_hash_element; /* Helper functions */ char* xdebug_hash_key_to_str(xdebug_hash_key* key, int* new_len); /* Standard functions */ xdebug_hash *xdebug_hash_alloc(int slots, xdebug_hash_dtor_t dtor); xdebug_hash *xdebug_hash_alloc_with_sort(int slots, xdebug_hash_dtor_t dtor, xdebug_hash_apply_sorter_t sort_func); int xdebug_hash_add_or_update(xdebug_hash *h, const char *str_key, unsigned int str_key_len, unsigned long num_key, const void *p); int xdebug_hash_extended_delete(xdebug_hash *h, const char *str_key, unsigned int str_key_len, unsigned long num_key); int xdebug_hash_extended_find(xdebug_hash *h, const char *str_key, unsigned int str_key_len, unsigned long num_key, void **p); void xdebug_hash_apply(xdebug_hash *h, void *user, void (*cb)(void *, xdebug_hash_element *)); void xdebug_hash_apply_with_argument(xdebug_hash *h, void *user, void (*cb)(void *, xdebug_hash_element *, void *), void *argument); void xdebug_hash_destroy(xdebug_hash *h); #define xdebug_hash_find(h, key, key_len, p) xdebug_hash_extended_find(h, key, key_len, 0, p) #define xdebug_hash_delete(h, key, key_len) xdebug_hash_extended_delete(h, key, key_len, 0) #define xdebug_hash_add(h, key, key_len, p) xdebug_hash_add_or_update(h, key, key_len, 0, p) #define xdebug_hash_update xdebug_hash_add #define xdebug_hash_index_find(h, key, p) xdebug_hash_extended_find(h, NULL, 0, key, p) #define xdebug_hash_index_delete(h, key) xdebug_hash_extended_delete(h, NULL, 0, key) #define xdebug_hash_index_add(h, key, p) xdebug_hash_add_or_update(h, NULL, 0, key, p) #define xdebug_hash_index_update xdebug_hash_index_add #endif /* __XDEBUG_HASH_H__ */ xdebug-3.4.3/src/lib/headers.c0000664000175000017500000000711315011062311015371 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "lib_private.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); static int xdebug_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s); static int (*xdebug_orig_header_handler)(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s); static void xdebug_header_remove_with_prefix(xdebug_llist *headers, char *prefix, size_t prefix_len) { xdebug_llist_element *le; char *header; for (le = XDEBUG_LLIST_HEAD(XG_LIB(headers)); le != NULL; /* intentionally left blank*/) { header = XDEBUG_LLIST_VALP(le); if ((strlen(header) > prefix_len + 1) && (header[prefix_len] == ':') && (strncasecmp(header, prefix, prefix_len) == 0)) { xdebug_llist_element *current = le; le = XDEBUG_LLIST_NEXT(le); xdebug_llist_remove(headers, current, NULL); } else { le = XDEBUG_LLIST_NEXT(le); } } } static int xdebug_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) { if (XG_LIB(headers)) { switch (op) { case SAPI_HEADER_ADD: xdebug_llist_insert_next(XG_LIB(headers), XDEBUG_LLIST_TAIL(XG_LIB(headers)), xdstrdup(h->header)); break; case SAPI_HEADER_REPLACE: { char *colon_offset = strchr(h->header, ':'); if (colon_offset) { char save = *colon_offset; *colon_offset = '\0'; xdebug_header_remove_with_prefix(XG_LIB(headers), h->header, strlen(h->header)); *colon_offset = save; } xdebug_llist_insert_next(XG_LIB(headers), XDEBUG_LLIST_TAIL(XG_LIB(headers)), xdstrdup(h->header)); } break; case SAPI_HEADER_DELETE_ALL: xdebug_llist_empty(XG_LIB(headers), NULL); case SAPI_HEADER_DELETE: case SAPI_HEADER_SET_STATUS: break; } } if (xdebug_orig_header_handler) { return xdebug_orig_header_handler(h, op, s); } return SAPI_HEADER_ADD; } void xdebug_lib_zend_startup_overload_sapi_headers(void) { /* Override header handler in SAPI */ if (xdebug_orig_header_handler != NULL) { return; } xdebug_orig_header_handler = sapi_module.header_handler; sapi_module.header_handler = xdebug_header_handler; } void xdebug_lib_zend_shutdown_restore_sapi_headers(void) { /* Restore original header handler in SAPI */ sapi_module.header_handler = xdebug_orig_header_handler; xdebug_orig_header_handler = NULL; } PHP_FUNCTION(xdebug_get_headers) { xdebug_llist_element *le; char *string; array_init(return_value); if (!XG_LIB(headers)) { return; } for (le = XDEBUG_LLIST_HEAD(XG_LIB(headers)); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { string = XDEBUG_LLIST_VALP(le); add_next_index_string(return_value, string); } } xdebug-3.4.3/src/lib/headers.h0000664000175000017500000000213015011062311015370 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ void xdebug_lib_zend_startup_overload_sapi_headers(void); void xdebug_lib_zend_shutdown_restore_sapi_headers(void); xdebug-3.4.3/src/lib/lib.c0000664000175000017500000005660515011062311014536 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "headers.h" #include "lib_private.h" #include "log.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); void xdebug_init_library_globals(xdebug_library_globals_t *xg) { xg->headers = NULL; xg->mode_from_environment = 0; xg->log_file = 0; xg->active_execute_data = NULL; xg->opcode_handlers_set = xdebug_set_create(256); memset(xg->original_opcode_handlers, 0, sizeof(xg->original_opcode_handlers)); memset(xg->opcode_multi_handlers, 0, sizeof(xg->opcode_multi_handlers)); XINI_LIB(log_level) = 0; xg->diagnosis_buffer = NULL; } void xdebug_library_zend_startup(void) { xdebug_lib_zend_startup_overload_sapi_headers(); } void xdebug_library_zend_shutdown(void) { xdebug_lib_zend_shutdown_restore_sapi_headers(); } void xdebug_library_minit(void) { xdebug_set_opcode_multi_handler(ZEND_ASSIGN); xdebug_set_opcode_multi_handler(ZEND_ASSIGN_DIM); xdebug_set_opcode_multi_handler(ZEND_ASSIGN_OBJ); xdebug_set_opcode_multi_handler(ZEND_ASSIGN_STATIC_PROP); xdebug_set_opcode_multi_handler(ZEND_QM_ASSIGN); xdebug_set_opcode_multi_handler(ZEND_INCLUDE_OR_EVAL); } static void xdebug_multi_opcode_handler_dtor(xdebug_multi_opcode_handler_t *ptr) { if (ptr->next) { xdebug_multi_opcode_handler_dtor(ptr->next); } xdfree(ptr); } void xdebug_library_mshutdown(void) { int i; /* Restore all opcode handlers that we have set */ for (i = 0; i < 256; i++) { if (XG_LIB(opcode_multi_handlers)[i] != NULL) { xdebug_multi_opcode_handler_dtor(XG_LIB(opcode_multi_handlers)[i]); } xdebug_unset_opcode_handler(i); } xdebug_set_free(XG_LIB(opcode_handlers_set)); } void xdebug_library_rinit(void) { XG_LIB(diagnosis_buffer) = xdebug_str_new(); xdebug_open_log(); XG_LIB(headers) = xdebug_llist_alloc(xdebug_llist_string_dtor); XG_LIB(dumped) = 0; XG_LIB(do_collect_errors) = 0; XG_LIB(trait_location_map) = xdebug_hash_alloc(256, (xdebug_hash_dtor_t) zend_string_release); } void xdebug_library_post_deactivate(void) { /* Clean up collected headers */ xdebug_llist_destroy(XG_LIB(headers), NULL); XG_LIB(headers) = NULL; xdebug_hash_destroy(XG_LIB(trait_location_map)); xdebug_close_log(); xdebug_str_free(XG_LIB(diagnosis_buffer)); XG_LIB(diagnosis_buffer) = NULL; } void xdebug_disable_opcache_optimizer(void) { zend_string *key = zend_string_init(ZEND_STRL("opcache.optimization_level"), 1); zend_string *value = zend_string_init(ZEND_STRL("0"), 1); zend_alter_ini_entry(key, value, ZEND_INI_SYSTEM, ZEND_INI_STAGE_STARTUP); zend_string_release(key); zend_string_release(value); } static int xdebug_lib_set_mode_item(const char *mode, int len) { if (strncmp(mode, "off", len) == 0) { xdebug_global_mode |= XDEBUG_MODE_OFF; return 1; } if (strncmp(mode, "develop", len) == 0) { xdebug_global_mode |= XDEBUG_MODE_DEVELOP; return 1; } if (strncmp(mode, "coverage", len) == 0) { xdebug_global_mode |= XDEBUG_MODE_COVERAGE; return 1; } if (strncmp(mode, "debug", len) == 0) { xdebug_global_mode |= XDEBUG_MODE_STEP_DEBUG; return 1; } if (strncmp(mode, "gcstats", len) == 0) { xdebug_global_mode |= XDEBUG_MODE_GCSTATS; return 1; } if (strncmp(mode, "profile", len) == 0) { xdebug_global_mode |= XDEBUG_MODE_PROFILING; return 1; } if (strncmp(mode, "trace", len) == 0) { xdebug_global_mode |= XDEBUG_MODE_TRACING; return 1; } return 0; } static int xdebug_lib_set_mode_from_setting(const char *mode) { const char *mode_ptr = mode; char *comma = NULL; int errors = 0; xdebug_global_mode = 0; comma = strchr(mode_ptr, ','); while (comma) { errors += !xdebug_lib_set_mode_item(mode_ptr, comma - mode_ptr); mode_ptr = comma + 1; while (*mode_ptr == ' ') { mode_ptr++; } comma = strchr(mode_ptr, ','); } errors += !xdebug_lib_set_mode_item(mode_ptr, strlen(mode_ptr)); return !errors; } int xdebug_lib_set_mode(const char *mode) { char *config = getenv("XDEBUG_MODE"); int result = 0; /* XDEBUG_MODE environment variable */ if (config && strlen(config)) { result = xdebug_lib_set_mode_from_setting(config); if (!result) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_CRIT, "ENVMODE", "Invalid mode '%s' set for 'XDEBUG_MODE' environment variable, fall back to 'xdebug.mode' configuration setting", config); } else { XG_LIB(mode_from_environment) = 1; return result; } } /* 'xdebug.mode' configuration setting */ result = xdebug_lib_set_mode_from_setting(mode); if (!result) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_CRIT, "MODE", "Invalid mode '%s' set for 'xdebug.mode' configuration setting", mode); } return result; } #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT int xdebug_lib_set_control_socket_granularity(char *value) { if (strcmp(value, "no") == 0 || value[0] == '\0') { XINI_BASE(control_socket_granularity) = XDEBUG_CONTROL_SOCKET_OFF; return 1; } if (strcmp(value, "default") == 0) { XINI_BASE(control_socket_granularity) = XDEBUG_CONTROL_SOCKET_DEFAULT; XINI_BASE(control_socket_threshold_ms) = 25; return 1; } if (strcmp(value, "time") == 0) { XINI_BASE(control_socket_granularity) = XDEBUG_CONTROL_SOCKET_TIME; XINI_BASE(control_socket_threshold_ms) = 25; return 1; } return 0; } #endif int xdebug_lib_get_start_with_request(void) { return XG_LIB(start_with_request); } int xdebug_lib_set_start_with_request(char *value) { if (strcmp(value, "default") == 0) { XG_LIB(start_with_request) = XDEBUG_START_WITH_REQUEST_DEFAULT; return 1; } if (strcmp(value, "yes") == 0 || strcmp(value, "1") == 0) { XG_LIB(start_with_request) = XDEBUG_START_WITH_REQUEST_YES; return 1; } if (strcmp(value, "no") == 0 || value[0] == '\0') { XG_LIB(start_with_request) = XDEBUG_START_WITH_REQUEST_NO; return 1; } if (strcmp(value, "trigger") == 0) { XG_LIB(start_with_request) = XDEBUG_START_WITH_REQUEST_TRIGGER; return 1; } return 0; } int xdebug_lib_start_with_request(int for_mode) { if (XG_LIB(start_with_request) == XDEBUG_START_WITH_REQUEST_YES) { return 1; } if (XG_LIB(start_with_request) == XDEBUG_START_WITH_REQUEST_DEFAULT) { if (for_mode == XDEBUG_MODE_PROFILING && XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { return 1; } } return 0; } int xdebug_lib_never_start_with_request(void) { if (XG_LIB(start_with_request) == XDEBUG_START_WITH_REQUEST_NO) { return 1; } return 0; } int xdebug_lib_get_start_upon_error(void) { return XG_LIB(start_upon_error); } int xdebug_lib_set_start_upon_error(char *value) { if (strcmp(value, "default") == 0) { XG_LIB(start_upon_error) = XDEBUG_START_UPON_ERROR_DEFAULT; return 1; } if (strcmp(value, "yes") == 0 || strcmp(value, "1") == 0) { XG_LIB(start_upon_error) = XDEBUG_START_UPON_ERROR_YES; return 1; } if (strcmp(value, "no") == 0 || value[0] == '\0') { XG_LIB(start_upon_error) = XDEBUG_START_UPON_ERROR_NO; return 1; } return 0; } int xdebug_lib_start_upon_error(void) { if (XG_LIB(start_upon_error) == XDEBUG_START_UPON_ERROR_YES) { return 1; } return 0; } const char *xdebug_lib_mode_from_value(int mode) { switch (mode) { case XDEBUG_MODE_DEVELOP: return "develop"; case XDEBUG_MODE_COVERAGE: return "coverage"; case XDEBUG_MODE_STEP_DEBUG: return "debug"; case XDEBUG_MODE_GCSTATS: return "gcstats"; case XDEBUG_MODE_PROFILING: return "profile"; case XDEBUG_MODE_TRACING: return "trace"; default: return "?"; } } const char *xdebug_lib_find_in_globals(const char *element, const char **found_in_global) { zval *trigger_val = NULL; const char *env_value = getenv(element); zval *st; /* Elements in Superglobal Symbols */ st = zend_hash_str_find_deref(&EG(symbol_table), "_GET", strlen("_GET")); if (st && Z_TYPE_P(st) == IS_ARRAY && (trigger_val = zend_hash_str_find_deref(Z_ARRVAL_P(st), element, strlen(element))) != NULL) { *found_in_global = "GET"; return Z_STRVAL_P(trigger_val); } st = zend_hash_str_find_deref(&EG(symbol_table), "_POST", strlen("_POST")); if (st && Z_TYPE_P(st) == IS_ARRAY && (trigger_val = zend_hash_str_find_deref(Z_ARRVAL_P(st), element, strlen(element))) != NULL) { *found_in_global = "POST"; return Z_STRVAL_P(trigger_val); } st = zend_hash_str_find_deref(&EG(symbol_table), "_COOKIE", strlen("_COOKIE")); if (st && Z_TYPE_P(st) == IS_ARRAY && (trigger_val = zend_hash_str_find_deref(Z_ARRVAL_P(st), element, strlen(element))) != NULL) { *found_in_global = "COOKIE"; return Z_STRVAL_P(trigger_val); } /* Actual Superglobals */ if ((trigger_val = zend_hash_str_find_deref(Z_ARR(PG(http_globals)[TRACK_VARS_GET]), element, strlen(element))) != NULL) { *found_in_global = "GET"; return Z_STRVAL_P(trigger_val); } if ((trigger_val = zend_hash_str_find_deref(Z_ARR(PG(http_globals)[TRACK_VARS_POST]), element, strlen(element))) != NULL) { *found_in_global = "POST"; return Z_STRVAL_P(trigger_val); } if ((trigger_val = zend_hash_str_find_deref(Z_ARR(PG(http_globals)[TRACK_VARS_COOKIE]), element, strlen(element))) != NULL) { *found_in_global = "COOKIE"; return Z_STRVAL_P(trigger_val); } /* Environment. This goes last. */ if (env_value) { *found_in_global = "ENV"; return env_value; } st = zend_hash_str_find_deref(&EG(symbol_table), "_ENV", strlen("_ENV")); if (st && Z_TYPE_P(st) == IS_ARRAY && (trigger_val = zend_hash_str_find_deref(Z_ARRVAL_P(st), element, strlen(element))) != NULL) { *found_in_global = "ENV"; return Z_STRVAL_P(trigger_val); } if ((trigger_val = zend_hash_str_find_deref(Z_ARR(PG(http_globals)[TRACK_VARS_ENV]), element, strlen(element))) != NULL) { *found_in_global = "ENV"; return Z_STRVAL_P(trigger_val); } return NULL; } int xdebug_lib_has_shared_secret(void) { char *shared_secret = XINI_LIB(trigger_value); if (shared_secret != NULL && shared_secret[0] != '\0') { return 1; } return 0; } static int does_shared_secret_match_single(int mode, const char *trimmed_trigger_value, const char *trimmed_shared_secret, char **found_trigger_value) { if (strcmp(trimmed_shared_secret, trimmed_trigger_value) == 0) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_DEBUG, "TRGSEC-MATCH", "The trigger value '%s' matched the shared secret '%s' for mode '%s'", trimmed_trigger_value, trimmed_shared_secret, xdebug_lib_mode_from_value(mode)); if (found_trigger_value != NULL) { *found_trigger_value = xdstrdup(trimmed_trigger_value); } return 1; } return 0; } static int does_shared_secret_match(int mode, const char *trigger_name, const char *trigger_value, char **found_trigger_value) { int retval = 0; const char *shared_secret = XINI_LIB(trigger_value); char *trimmed_trigger_value = xdebug_trim(trigger_value); /* Check we have a multi-value-shared secret setting */ if (strchr(shared_secret, ',') != NULL) { int i; xdebug_arg *values = xdebug_arg_ctor(); xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_DEBUG, "TRGSEC-MULT", "The shared secret (xdebug.trigger_value) is multi-value for mode '%s'", xdebug_lib_mode_from_value(mode)); xdebug_explode(",", shared_secret, values, -1); for (i = 0; i < values->c; i++) { char *trimmed_shared_secret = xdebug_trim(values->args[i]); retval = does_shared_secret_match_single(mode, trimmed_trigger_value, trimmed_shared_secret, found_trigger_value); xdfree(trimmed_shared_secret); /* Jump out of the loop if we found a match */ if (retval != 0) { break; } } xdebug_arg_dtor(values); if (retval == 0) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "TRGSEC-MNO", "The trigger value '%s', as set through '%s', did not match any of the shared secrets (xdebug.trigger_value) for mode '%s'", trimmed_trigger_value, trigger_name, xdebug_lib_mode_from_value(mode)); } } else { char *trimmed_shared_secret = xdebug_trim(shared_secret); retval = does_shared_secret_match_single(mode, trimmed_trigger_value, trimmed_shared_secret, found_trigger_value); xdfree(trimmed_shared_secret); if (retval == 0) { xdebug_log_ex(XLOG_CHAN_CONFIG, XLOG_WARN, "TRGSEC-NO", "The trigger value '%s', as set through '%s', did not match the shared secret (xdebug.trigger_value) for mode '%s'", trimmed_trigger_value, trigger_name, xdebug_lib_mode_from_value(mode)); } } xdfree(trimmed_trigger_value); return retval; } static int trigger_enabled(int for_mode, char **found_trigger_value) { const char *trigger_value = NULL; const char *trigger_name = "XDEBUG_TRIGGER"; const char *found_in_global; xdebug_log(XLOG_CHAN_CONFIG, XLOG_DEBUG, "Checking if trigger 'XDEBUG_TRIGGER' is enabled for mode '%s'", xdebug_lib_mode_from_value(for_mode)); /* First we check for the generic 'XDEBUG_TRIGGER' option */ trigger_value = xdebug_lib_find_in_globals(trigger_name, &found_in_global); /* If not found, we fall back to the per-mode name for backwards compatibility reasons */ if (!trigger_value) { if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING) && (for_mode == XDEBUG_MODE_PROFILING)) { trigger_name = "XDEBUG_PROFILE"; } else if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING) && (for_mode == XDEBUG_MODE_TRACING)) { trigger_name = "XDEBUG_TRACE"; } else if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG) && (for_mode == XDEBUG_MODE_STEP_DEBUG)) { trigger_name = "XDEBUG_SESSION"; } if (trigger_name) { xdebug_log(XLOG_CHAN_CONFIG, XLOG_INFO, "Trigger value for 'XDEBUG_TRIGGER' not found, falling back to '%s'", trigger_name); trigger_value = xdebug_lib_find_in_globals(trigger_name, &found_in_global); } } if (!trigger_value) { xdebug_log(XLOG_CHAN_CONFIG, XLOG_INFO, "Trigger value for '%s' not found, so not activating", trigger_name); if (found_trigger_value != NULL) { *found_trigger_value = NULL; } return 0; } /* If there is no configured shared secret trigger, always trigger */ if (!xdebug_lib_has_shared_secret()) { xdebug_log(XLOG_CHAN_CONFIG, XLOG_INFO, "No shared secret: Activating"); if (found_trigger_value != NULL) { *found_trigger_value = xdstrdup(trigger_value); } return 1; } /* Check if the configured trigger value matches the one found in the * trigger element */ if (does_shared_secret_match(for_mode, trigger_name, trigger_value, found_trigger_value)) { return 1; } return 0; } static int is_mode_trigger_and_enabled(int for_mode, int force_trigger, char **found_trigger_value) { if (XG_LIB(start_with_request) == XDEBUG_START_WITH_REQUEST_TRIGGER) { return force_trigger || trigger_enabled(for_mode, found_trigger_value); } if (XG_LIB(start_with_request) == XDEBUG_START_WITH_REQUEST_DEFAULT) { if ( XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG) || XDEBUG_MODE_IS(XDEBUG_MODE_TRACING) ) { return force_trigger || trigger_enabled(for_mode, found_trigger_value); } } return 0; } /* Returns 1 if the mode is 'trigger', or 'default', where the default mode for * a feature is to trigger, and the trigger is present. If found_trigger_value * is not NULL, then it is set to the found trigger value */ int xdebug_lib_start_with_trigger(int for_mode, char **found_trigger_value) { return is_mode_trigger_and_enabled(for_mode, 0, found_trigger_value); } /* Returns 1 if the mode is 'trigger', or 'default', where the default mode for * a feature is to trigger. Does not check whether a trigger is present. */ int xdebug_lib_start_if_mode_is_trigger(int for_mode) { return is_mode_trigger_and_enabled(for_mode, 1, NULL); } function_stack_entry *xdebug_get_stack_frame(int nr) { if (!XG_BASE(stack)) { return NULL; } if (nr < 0 || nr >= XDEBUG_VECTOR_COUNT(XG_BASE(stack))) { return NULL; } return xdebug_vector_element_get(XG_BASE(stack), XDEBUG_VECTOR_COUNT(XG_BASE(stack)) - nr - 1); } static void xdebug_used_var_hash_from_llist_dtor(void *data) { xdebug_str *var_name = (xdebug_str*) data; xdebug_str_free(var_name); } static int xdebug_compare_le_xdebug_str(const void *le1, const void *le2) { return strcmp( ((xdebug_str *) XDEBUG_LLIST_VALP(*(xdebug_llist_element **) le1))->d, ((xdebug_str *) XDEBUG_LLIST_VALP(*(xdebug_llist_element **) le2))->d ); } xdebug_hash* xdebug_declared_var_hash_from_llist(xdebug_llist *list) { xdebug_hash *tmp; xdebug_llist_element *le; xdebug_str *var_name; tmp = xdebug_hash_alloc_with_sort(32, xdebug_used_var_hash_from_llist_dtor, xdebug_compare_le_xdebug_str); for (le = XDEBUG_LLIST_HEAD(list); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { var_name = (xdebug_str*) XDEBUG_LLIST_VALP(le); xdebug_hash_add(tmp, var_name->d, var_name->l, xdebug_str_copy(var_name)); } return tmp; } void xdebug_lib_set_active_data(zend_execute_data *execute_data) { XG_LIB(active_execute_data) = execute_data; XG_LIB(active_object) = execute_data ? &execute_data->This : NULL; } void xdebug_lib_set_active_stack_entry(function_stack_entry *fse) { XG_LIB(active_stack_entry) = fse; } void xdebug_lib_set_active_symbol_table(HashTable *symbol_table) { XG_LIB(active_symbol_table) = symbol_table; } int xdebug_lib_has_active_data(void) { return !!XG_LIB(active_execute_data); } int xdebug_lib_has_active_function(void) { return !!XG_LIB(active_execute_data)->func; } int xdebug_lib_has_active_object(void) { return !!XG_LIB(active_object); } int xdebug_lib_has_active_symbol_table(void) { return !!XG_LIB(active_symbol_table); } zend_execute_data *xdebug_lib_get_active_data(void) { return XG_LIB(active_execute_data); } zend_op_array *xdebug_lib_get_active_func_oparray(void) { return &XG_LIB(active_execute_data)->func->op_array; } function_stack_entry *xdebug_lib_get_active_stack_entry(void) { return XG_LIB(active_stack_entry); } HashTable *xdebug_lib_get_active_symbol_table(void) { return XG_LIB(active_symbol_table); } zval *xdebug_lib_get_active_object(void) { return XG_LIB(active_object); } int xdebug_isset_opcode_handler(int opcode) { return xdebug_set_in(XG_LIB(opcode_handlers_set), opcode); } void xdebug_set_opcode_handler(int opcode, user_opcode_handler_t handler) { if (xdebug_isset_opcode_handler(opcode)) { abort(); } XG_LIB(original_opcode_handlers[opcode]) = zend_get_user_opcode_handler(opcode); xdebug_set_add(XG_LIB(opcode_handlers_set), opcode); zend_set_user_opcode_handler(opcode, handler); } static int xdebug_opcode_multi_handler(zend_execute_data *execute_data) { const zend_op *cur_opcode = execute_data->opline; xdebug_multi_opcode_handler_t *handler_ptr = XG_LIB(opcode_multi_handlers[cur_opcode->opcode]); while (handler_ptr) { handler_ptr->handler(execute_data); handler_ptr = handler_ptr->next; } return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } void xdebug_set_opcode_multi_handler(int opcode) { if (xdebug_isset_opcode_handler(opcode)) { abort(); } XG_LIB(original_opcode_handlers[opcode]) = zend_get_user_opcode_handler(opcode); xdebug_set_add(XG_LIB(opcode_handlers_set), opcode); zend_set_user_opcode_handler(opcode, xdebug_opcode_multi_handler); } void xdebug_register_with_opcode_multi_handler(int opcode, user_opcode_handler_t handler) { xdebug_multi_opcode_handler_t *ptr; xdebug_multi_opcode_handler_t *tmp = xdmalloc(sizeof(xdebug_multi_opcode_handler_t)); tmp->handler = handler; tmp->next = NULL; if (!xdebug_isset_opcode_handler(opcode)) { abort(); } if (XG_LIB(opcode_multi_handlers)[opcode] == NULL) { XG_LIB(opcode_multi_handlers)[opcode] = tmp; return; } ptr = XG_LIB(opcode_multi_handlers)[opcode]; while (ptr->next) { ptr = ptr->next; } ptr->next = tmp; } void xdebug_unset_opcode_handler(int opcode) { if (xdebug_set_in(XG_LIB(opcode_handlers_set), opcode)) { zend_set_user_opcode_handler(opcode, XG_LIB(original_opcode_handlers[opcode])); } } int xdebug_call_original_opcode_handler_if_set(int opcode, XDEBUG_OPCODE_HANDLER_ARGS) { if (xdebug_isset_opcode_handler(opcode)) { user_opcode_handler_t handler = XG_LIB(original_opcode_handlers[opcode]); if (handler) { return handler(XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } } return ZEND_USER_OPCODE_DISPATCH; } /* Does not duplicate the return value, don't free. Return NULL if it's * not-set, or an empty string */ char *xdebug_lib_get_output_dir(void) { char *output_dir = XINI_LIB(output_dir); if (output_dir == NULL || output_dir[0] == '\0') { return NULL; } return output_dir; } void xdebug_llist_string_dtor(void *dummy, void *elem) { char *s = elem; if (s) { xdfree(s); } } zend_string *xdebug_get_trait_scope(const char *function) { zend_string *trait_scope; if ( function[0] != '{' && function[strlen(function)-1] == '}' && xdebug_hash_find(XG_LIB(trait_location_map), function, strlen(function), (void *) &trait_scope) ) { return trait_scope; } return NULL; } zend_string *xdebug_wrap_location_around_function_name(const char *prefix, zend_op_array *opa, zend_string *fname) { void *dummy; zend_string *wrapped = zend_strpprintf( 0, "%s{%s:%s:%d-%d}", ZSTR_VAL(fname), prefix, ZSTR_VAL(opa->filename), opa->line_start, opa->line_end ); if (!xdebug_hash_find(XG_LIB(trait_location_map), ZSTR_VAL(wrapped), ZSTR_LEN(wrapped), &dummy)) { xdebug_hash_add(XG_LIB(trait_location_map), ZSTR_VAL(wrapped), ZSTR_LEN(wrapped), zend_string_copy(opa->scope->name)); } return wrapped; } #if PHP_VERSION_ID >= 80400 zend_string* xdebug_wrap_closure_location_around_function_name(zend_op_array *opa, zend_string *fname) { zend_string *tmp, *tmp_loc_info; if (ZSTR_VAL(fname)[ZSTR_LEN(fname) - 1] != '}') { return zend_string_copy(fname); } tmp = zend_string_init(ZSTR_VAL(fname), strlen("{closure"), false); tmp_loc_info = zend_strpprintf( 0, "%s:%s:%d-%d}", ZSTR_VAL(tmp), ZSTR_VAL(opa->filename), opa->line_start, opa->line_end ); zend_string_release(tmp); return tmp_loc_info; } #else zend_string* xdebug_wrap_closure_location_around_function_name(zend_op_array *opa, zend_string *fname) { zend_string *tmp, *tmp_loc_info; if (ZSTR_VAL(fname)[ZSTR_LEN(fname) - 1] != '}') { return zend_string_copy(fname); } tmp = zend_string_init(ZSTR_VAL(fname), ZSTR_LEN(fname) - 1, false); tmp_loc_info = zend_strpprintf( 0, "%s:%s:%d-%d}", ZSTR_VAL(tmp), ZSTR_VAL(opa->filename), opa->line_start, opa->line_end ); zend_string_release(tmp); return tmp_loc_info; } #endif static void xdebug_declared_var_dtor(void *dummy, void *elem) { xdebug_str *s = (xdebug_str*) elem; xdebug_str_free(s); } void xdebug_lib_register_compiled_variables(function_stack_entry *fse) { unsigned int i = 0; if (fse->declared_vars) { return; } if (!fse->op_array->vars) { return; } fse->declared_vars = xdebug_llist_alloc(xdebug_declared_var_dtor); /* gather used variables from compiled vars information */ while (i < (unsigned int) fse->op_array->last_var) { xdebug_llist_insert_next(fse->declared_vars, XDEBUG_LLIST_TAIL(fse->declared_vars), xdebug_str_create(STR_NAME_VAL(fse->op_array->vars[i]), STR_NAME_LEN(fse->op_array->vars[i]))); i++; } } xdebug-3.4.3/src/lib/lib.h0000664000175000017500000002554315011062311014540 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_LIBRARY_H__ #define __XDEBUG_LIBRARY_H__ #ifdef ZTS # include "TSRM.h" #endif #include "zend.h" #include "zend_API.h" #include "compat.h" extern int xdebug_global_mode; typedef struct xdebug_var_name { zend_string *name; zval data; int is_variadic; } xdebug_var_name; #define XFUNC_UNKNOWN 0x00 #define XFUNC_NORMAL 0x01 #define XFUNC_STATIC_MEMBER 0x02 #define XFUNC_MEMBER 0x03 #define XFUNC_INCLUDES 0x10 #define XFUNC_EVAL 0x10 #define XFUNC_INCLUDE 0x11 #define XFUNC_INCLUDE_ONCE 0x12 #define XFUNC_REQUIRE 0x13 #define XFUNC_REQUIRE_ONCE 0x14 #define XFUNC_MAIN 0x15 #if PHP_VERSION_ID >= 80100 # define XFUNC_FIBER 0x16 #endif #define XFUNC_ZEND_PASS 0x20 #define XDEBUG_IS_NORMAL_FUNCTION(f) ((f)->type == XFUNC_NORMAL || (f)->type == XFUNC_STATIC_MEMBER || (f)->type == XFUNC_MEMBER) #define XDEBUG_REGISTER_LONG_CONSTANT(__c) REGISTER_LONG_CONSTANT(#__c, __c, CONST_CS|CONST_PERSISTENT) #define XDEBUG_NONE 0 #define XDEBUG_JIT 1 #define XDEBUG_REQ 2 #define XDEBUG_BREAK 1 #define XDEBUG_STEP 2 #define XDEBUG_BUILT_IN 0 #define XDEBUG_USER_DEFINED 1 #define XDEBUG_MAX_FUNCTION_LEN 1024 #define XDEBUG_TRACE_OPTION_APPEND 0x01 #define XDEBUG_TRACE_OPTION_COMPUTERIZED 0x02 #define XDEBUG_TRACE_OPTION_HTML 0x04 #define XDEBUG_TRACE_OPTION_NAKED_FILENAME 0x08 #define XDEBUG_TRACE_OPTION_FLAMEGRAPH_COST 0x10 #define XDEBUG_TRACE_OPTION_FLAMEGRAPH_MEM 0x20 #define XDEBUG_CC_OPTION_UNUSED 1 #define XDEBUG_CC_OPTION_DEAD_CODE 2 #define XDEBUG_CC_OPTION_BRANCH_CHECK 4 #define STATUS_STARTING 0 #define STATUS_STOPPING 1 #define STATUS_STOPPED 2 #define STATUS_RUNNING 3 #define STATUS_BREAK 4 #define REASON_OK 0 #define REASON_ERROR 1 #define REASON_ABORTED 2 #define REASON_EXCEPTION 3 #define XDEBUG_CMDLOOP_NONBLOCK 0 #define XDEBUG_CMDLOOP_BLOCK 1 #define XDEBUG_CMDLOOP_NONBAIL 0 #define XDEBUG_CMDLOOP_BAIL 1 typedef struct _xdebug_func { zend_string *object_class; zend_string *scope_class; zend_string *function; zend_string *include_filename; int type; int internal; } xdebug_func; typedef struct xdebug_profile { uint64_t nanotime; uint64_t nanotime_mark; long memory; long mem_mark; xdebug_llist *call_list; } xdebug_profile; typedef struct _function_stack_entry { /* function properties */ xdebug_func function; unsigned int function_nr; unsigned short user_defined:1; unsigned short level:15; /* argument properties */ unsigned short varc; xdebug_var_name *var; zval *return_value; xdebug_llist *declared_vars; HashTable *symbol_table; zend_execute_data *execute_data; unsigned char is_variadic; unsigned char is_trampoline; unsigned char arg_done; /* debugging properties */ bool has_line_breakpoints; /* filter properties */ unsigned char filtered_code_coverage; unsigned char filtered_stack; unsigned char filtered_tracing; /* coverage properties */ bool code_coverage_init; char *code_coverage_function_name; zend_string *code_coverage_filename; /* location properties */ int lineno; zend_string *filename; /* tracing properties */ signed long memory; signed long prev_memory; uint64_t nanotime; bool function_call_traced; /* profiling properties */ xdebug_profile profile; struct { int lineno; zend_string *filename; zend_string *function; } profiler; /* misc properties */ zend_op_array *op_array; #if PHP_VERSION_ID >= 80100 void (*soap_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); #else void (*soap_error_cb)(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message); #endif } function_stack_entry; function_stack_entry *xdebug_get_stack_frame(int nr); xdebug_hash* xdebug_declared_var_hash_from_llist(xdebug_llist *list); int xdebug_trigger_enabled(int setting, const char *var_name, char *var_value); typedef struct _xdebug_multi_opcode_handler_t xdebug_multi_opcode_handler_t; struct _xdebug_multi_opcode_handler_t { user_opcode_handler_t handler; xdebug_multi_opcode_handler_t *next; }; typedef struct _xdebug_library_globals_t { int start_with_request; /* One of the XDEBUG_START_WITH_REQUEST_* constants */ int start_upon_error; /* One of the XDEBUG_START_UPON_ERROR_* constants */ int mode_from_environment; /* Keeps track whether the mode was set with XDEBUG_MODE for diagnostics purposes */ zend_execute_data *active_execute_data; function_stack_entry *active_stack_entry; HashTable *active_symbol_table; zval *active_object; /* Headers */ xdebug_llist *headers; zend_bool dumped; /* used for collection errors */ zend_bool do_collect_errors; FILE *log_file; /* File handler for protocol log */ zend_bool log_opened_message_sent; char *log_open_timestring; xdebug_str *diagnosis_buffer; /* Trait location map */ xdebug_hash *trait_location_map; /* Opcode overrite handlers */ user_opcode_handler_t original_opcode_handlers[256]; xdebug_multi_opcode_handler_t *opcode_multi_handlers[256]; xdebug_set *opcode_handlers_set; } xdebug_library_globals_t; typedef struct _xdebug_library_settings_t { char *requested_mode; char *output_dir; char *trigger_value; char *file_link_format; char *filename_format; /* Whether to use zlib compression for profiling and trace files, if ZLIB support * is enabled */ zend_bool use_compression; /* variable dumping limitation settings */ zend_long display_max_children; zend_long display_max_data; zend_long display_max_depth; /* Logging settings */ char *log; /* Filename to log protocol communication to */ zend_long log_level; /* Log level XDEBUG_LOG_{ERR,WARN,INFO,DEBUG} */ } xdebug_library_settings_t; void xdebug_init_library_globals(xdebug_library_globals_t *xg); void xdebug_library_zend_startup(void); void xdebug_library_zend_shutdown(void); void xdebug_library_minit(void); void xdebug_library_mshutdown(void); void xdebug_library_rinit(void); void xdebug_library_post_deactivate(void); void xdebug_disable_opcache_optimizer(void); #define XDEBUG_MODE_OFF 0 #define XDEBUG_MODE_DEVELOP 1<<0 #define XDEBUG_MODE_COVERAGE 1<<1 #define XDEBUG_MODE_STEP_DEBUG 1<<2 #define XDEBUG_MODE_GCSTATS 1<<3 #define XDEBUG_MODE_PROFILING 1<<4 #define XDEBUG_MODE_TRACING 1<<5 int xdebug_lib_set_mode(const char *mode); #define XDEBUG_MODE_IS_OFF() ((xdebug_global_mode == XDEBUG_MODE_OFF)) #define XDEBUG_MODE_IS(v) ((xdebug_global_mode & (v)) ? 1 : 0) #define RETURN_IF_MODE_IS_NOT(m) if (!XDEBUG_MODE_IS((m))) { return; } #define RETURN_FALSE_IF_MODE_IS_NOT(m) if (!XDEBUG_MODE_IS((m))) { RETURN_FALSE; } #define WARN_AND_RETURN_IF_MODE_IS_NOT(m) if (!XDEBUG_MODE_IS((m))) { php_error(E_NOTICE, "Functionality is not enabled"); return; } #define XDEBUG_START_WITH_REQUEST_DEFAULT 1 #define XDEBUG_START_WITH_REQUEST_YES 2 #define XDEBUG_START_WITH_REQUEST_NO 3 #define XDEBUG_START_WITH_REQUEST_TRIGGER 4 int xdebug_lib_set_start_with_request(char *value); int xdebug_lib_start_with_request(int for_mode); int xdebug_lib_start_with_trigger(int for_mode, char **found_trigger_value); int xdebug_lib_start_if_mode_is_trigger(int for_mode); int xdebug_lib_never_start_with_request(void); int xdebug_lib_get_start_with_request(void); int xdebug_lib_has_shared_secret(void); const char *xdebug_lib_find_in_globals(const char *element, const char **found_in_global); #define XDEBUG_START_UPON_ERROR_DEFAULT 1 #define XDEBUG_START_UPON_ERROR_YES 2 #define XDEBUG_START_UPON_ERROR_NO 3 int xdebug_lib_set_start_upon_error(char *value); int xdebug_lib_start_upon_error(void); int xdebug_lib_get_start_upon_error(void); #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT # define XDEBUG_CONTROL_SOCKET_OFF 1 # define XDEBUG_CONTROL_SOCKET_DEFAULT 2 # define XDEBUG_CONTROL_SOCKET_TIME 3 int xdebug_lib_set_control_socket_granularity(char *value); #endif const char *xdebug_lib_mode_from_value(int mode); void xdebug_lib_set_active_data(zend_execute_data *execute_data); void xdebug_lib_set_active_stack_entry(function_stack_entry *fse); void xdebug_lib_set_active_symbol_table(HashTable *symbol_table); int xdebug_lib_has_active_data(void); int xdebug_lib_has_active_function(void); int xdebug_lib_has_active_object(void); int xdebug_lib_has_active_symbol_table(void); zend_execute_data *xdebug_lib_get_active_data(void); zend_op_array *xdebug_lib_get_active_func_oparray(void); zval *xdebug_lib_get_active_object(void); function_stack_entry *xdebug_lib_get_active_stack_entry(void); HashTable *xdebug_lib_get_active_symbol_table(void); int xdebug_isset_opcode_handler(int opcode); void xdebug_set_opcode_handler(int opcode, user_opcode_handler_t handler); void xdebug_unset_opcode_handler(int opcode); void xdebug_set_opcode_multi_handler(int opcode); void xdebug_register_with_opcode_multi_handler(int opcode, user_opcode_handler_t handler); int xdebug_call_original_opcode_handler_if_set(int opcode, XDEBUG_OPCODE_HANDLER_ARGS); char *xdebug_lib_get_output_dir(void); void xdebug_llist_string_dtor(void *dummy, void *elem); zend_string *xdebug_get_trait_scope(const char *function); zend_string* xdebug_wrap_location_around_function_name(const char *prefix, zend_op_array *opa, zend_string *fname); zend_string* xdebug_wrap_closure_location_around_function_name(zend_op_array *opa, zend_string *fname); void xdebug_lib_register_compiled_variables(function_stack_entry *fse); #endif xdebug-3.4.3/src/lib/log.c0000664000175000017500000010161715011062311014543 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include "php_xdebug.h" #include "ext/standard/info.h" #include "headers.h" #include "lib_private.h" #include "log.h" #include "var.h" #define DOCS_LINK_ICON "⊕" char* xdebug_lib_docs_base(void) { char *env = getenv("XDEBUG_DOCS_BASE"); if (env) { return env; } return (char*) "https://xdebug.org/docs/"; } extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); const char *xdebug_level_msg_prefix[11] = { "C", "E", "", "W", "", "", "", "I", "", "", "D" }; const char *xdebug_log_prefix[11] = { "CRIT:", "ERR: ", "", "WARN: ", "", "", "", "INFO: ", "", "", "DEBUG: " }; const char *xdebug_log_prefix_emoji[11] = { "☠", "🛑 ", "", "⚠️ ", "", "", "", "🛈 ", "", "", "• " }; const char *xdebug_channel_msg_prefix[8] = { "CFG-", "LOG-", "DBG-", "GC-", "PROF-", "TRACE-", "COV-", "BASE-" }; const char *xdebug_channel_name[8] = { "[Config] ", "[Log Files] ", "[Step Debug] ", "[GC Stats] ", "[Profiler] ", "[Tracing] ", "[Coverage] ", "[Base] " }; static inline int xdebug_internal_log(int channel, int log_level, const char *message) { zend_ulong pid; if (!XG_LIB(log_file)) { return 0; } pid = xdebug_get_pid(); if (!XG_LIB(log_opened_message_sent) && XG_LIB(log_open_timestring)) { XG_LIB(log_opened_message_sent) = 1; fprintf(XG_LIB(log_file), "[" ZEND_ULONG_FMT "] Log opened at %s\n", pid, XG_LIB(log_open_timestring)); fflush(XG_LIB(log_file)); xdfree(XG_LIB(log_open_timestring)); XG_LIB(log_open_timestring) = NULL; } fprintf( XG_LIB(log_file), "[" ZEND_ULONG_FMT "] %s%s%s\n", pid, xdebug_channel_name[channel], xdebug_log_prefix[log_level], message ); fflush(XG_LIB(log_file)); return 1; } #define TR_START "" #define TR_END "\n" static inline void xdebug_diagnostic_log(int channel, int log_level, const char *error_code, const char *message) { if (!XG_LIB(diagnosis_buffer) || log_level > XLOG_WARN) { return; } if (sapi_module.phpinfo_as_text) { xdebug_str_add(XG_LIB(diagnosis_buffer), xdebug_channel_name[channel], 0); xdebug_str_add(XG_LIB(diagnosis_buffer), xdebug_log_prefix[log_level], 0); xdebug_str_add(XG_LIB(diagnosis_buffer), message, 0); } else { xdebug_str_add_const(XG_LIB(diagnosis_buffer), ""); xdebug_str_add(XG_LIB(diagnosis_buffer), xdebug_log_prefix_emoji[log_level], 0); xdebug_str_add_const(XG_LIB(diagnosis_buffer), ""); xdebug_str_add(XG_LIB(diagnosis_buffer), xdebug_channel_name[channel], 0); xdebug_str_add(XG_LIB(diagnosis_buffer), message, 0); xdebug_str_add_const(XG_LIB(diagnosis_buffer), "" DOCS_LINK_ICON ""); } xdebug_str_addc(XG_LIB(diagnosis_buffer), '\n'); } static inline void xdebug_php_log(int channel, int log_level, const char *error_code, const char *message) { xdebug_str formatted_message = XDEBUG_STR_INITIALIZER; if (log_level > XLOG_ERR) { return; } xdebug_str_add_const(&formatted_message, "Xdebug: "); xdebug_str_add(&formatted_message, xdebug_channel_name[channel], 0); xdebug_str_add(&formatted_message, message, 0); if (error_code && log_level == XLOG_CRIT) { xdebug_str_add_const(&formatted_message, " (See: "); xdebug_str_add(&formatted_message, xdebug_lib_docs_base(), 0); xdebug_str_add_const(&formatted_message, "errors#"); xdebug_str_add(&formatted_message, xdebug_channel_msg_prefix[channel], 0); xdebug_str_add(&formatted_message, xdebug_level_msg_prefix[log_level], 0); xdebug_str_addc(&formatted_message, '-'); xdebug_str_add(&formatted_message, error_code, 0); xdebug_str_addc(&formatted_message, ')'); } php_log_err(formatted_message.d); xdebug_str_destroy(&formatted_message); } void XDEBUG_ATTRIBUTE_FORMAT(printf, 4, 5) xdebug_log_ex(int channel, int log_level, const char *error_code, const char *fmt, ...) { xdebug_str message = XDEBUG_STR_INITIALIZER; va_list argv; int logged_to_xdebug_log = 0; if (XINI_LIB(log_level) < log_level) { return; } va_start(argv, fmt); xdebug_str_add_va_fmt(&message, fmt, argv); va_end(argv); logged_to_xdebug_log = xdebug_internal_log(channel, log_level, message.d); xdebug_diagnostic_log(channel, log_level, error_code, message.d); if (!logged_to_xdebug_log || XINI_LIB(log_level) == XLOG_CRIT) { xdebug_php_log(channel, log_level, error_code, message.d); } xdebug_str_destroy(&message); } static void log_filename_not_opened(int channel, const char *directory, const char *filename) { xdebug_str full_filename = XDEBUG_STR_INITIALIZER; if (directory) { xdebug_str_add(&full_filename, directory, 0); if (!IS_SLASH(directory[strlen(directory) - 1])) { xdebug_str_addc(&full_filename, DEFAULT_SLASH); } } xdebug_str_add(&full_filename, filename, 0); xdebug_log_ex(channel, XLOG_ERR, "OPEN", "File '%s' could not be opened.", full_filename.d); xdebug_str_destroy(&full_filename); } void xdebug_log_diagnose_permissions(int channel, const char *directory, const char *filename) { #ifndef WIN32 struct stat dir_info; #endif log_filename_not_opened(channel, directory, filename); #ifndef WIN32 if (!directory) { return; } if (stat(directory, &dir_info) == -1) { xdebug_log_ex(channel, XLOG_WARN, "STAT", "%s: %s", directory, strerror(errno)); return; } if (!S_ISDIR(dir_info.st_mode)) { xdebug_log_ex(channel, XLOG_WARN, "NOTDIR", "The path '%s' is not a directory.", directory); return; } xdebug_log_ex(channel, XLOG_WARN, "PERM", "The path '%s' has the permissions: 0%03o.", directory, dir_info.st_mode & 0777); #endif } static int xdebug_info_printf(const char *fmt, ...) /* {{{ */ { char *buf; size_t len, written; va_list argv; va_start(argv, fmt); len = vspprintf(&buf, 0, fmt, argv); va_end(argv); written = php_output_write(buf, len); efree(buf); return written; } /* }}} */ static void print_logo(void) { if (!sapi_module.phpinfo_as_text) { PUTS(""); PUTS("Xdebug"); PUTS("\n"); } else { PUTS("\33[1m__ __ _ _ \n" "\33[1m\\ \\ / / | | | | \n" "\33[1m \\ V / __| | ___| |__ _ _ __ _ \n" "\33[1m > < / _` |/ _ \\ '_ \\| | | |/ _` |\n" "\33[1m / . \\ (_| | __/ |_) | |_| | (_| |\n" "\33[1m/_/ \\_\\__,_|\\___|_.__/ \\__,_|\\__, |\n" "\33[1m __/ |\n" "\33[1m |___/ \n\n\33[0m"); } } static void print_feature_row(const char *name, int flag, const char *doc_name) { if (!sapi_module.phpinfo_as_text) { PUTS(""); PUTS(""); PUTS(name); PUTS(""); PUTS(XDEBUG_MODE_IS(flag) ? "✔ enabled" : "✘ disabled"); PUTS("" DOCS_LINK_ICON "\n"); } else { php_info_print_table_row(2, name, XDEBUG_MODE_IS(flag) ? "✔ enabled" : "✘ disabled"); } } void xdebug_print_info(void) { /* Header block */ php_info_print_table_start(); print_logo(); php_info_print_table_row(2, "Version", XDEBUG_VERSION); if (!sapi_module.phpinfo_as_text) { xdebug_info_printf("%s\n", "Support Xdebug on Patreon, GitHub, or as a business"); } else { xdebug_info_printf("Support Xdebug on Patreon, GitHub, or as a business: https://xdebug.org/support\n"); } php_info_print_table_end(); /* Modes block */ php_info_print_table_start(); if (!sapi_module.phpinfo_as_text) { php_info_print_table_colspan_header( 3, (char*) (XG_LIB(mode_from_environment) ? "Enabled Features
(through 'XDEBUG_MODE' env variable)" : "Enabled Features
(through 'xdebug.mode' setting)") ); } else { php_info_print_table_colspan_header( 2, (char*) (XG_LIB(mode_from_environment) ? "Enabled Features (through 'XDEBUG_MODE' env variable)" : "Enabled Features (through 'xdebug.mode' setting)") ); } if (!sapi_module.phpinfo_as_text) { php_info_print_table_header(3, "Feature", "Enabled/Disabled", "Docs"); } else { php_info_print_table_header(2, "Feature", "Enabled/Disabled"); } print_feature_row("Development Helpers", XDEBUG_MODE_DEVELOP, "develop"); print_feature_row("Coverage", XDEBUG_MODE_COVERAGE, "code_coverage"); print_feature_row("GC Stats", XDEBUG_MODE_GCSTATS, "garbage_collection"); print_feature_row("Profiler", XDEBUG_MODE_PROFILING, "profiler"); print_feature_row("Step Debugger", XDEBUG_MODE_STEP_DEBUG, "remote"); print_feature_row("Tracing", XDEBUG_MODE_TRACING, "trace"); php_info_print_table_end(); /* Optional compiled in features */ php_info_print_table_start(); php_info_print_table_colspan_header(2, (char*) "Optional Features"); #if HAVE_XDEBUG_ZLIB php_info_print_table_row(2, "Compressed File Support", "yes (gzip)"); #else php_info_print_table_row(2, "Compressed File Support", "no"); #endif #if WIN32 php_info_print_table_row(2, "Clock Source", XG_BASE(nanotime_context).use_rel_time ? "QueryPerformanceFrequency" : "GetSystemTimePreciseAsFileTime"); #else # if HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP php_info_print_table_row(2, "Clock Source", "clock_gettime_nsec_np"); # elif HAVE_XDEBUG_CLOCK_GETTIME php_info_print_table_row(2, "Clock Source", "clock_gettime"); php_info_print_table_row(2, "TSC Clock Source", XG_BASE(working_tsc_clock) ? "available" : "unavailable"); # else php_info_print_table_row(2, "Clock Source", "gettimeofday"); # endif #endif #if HAVE_LINUX_RTNETLINK_H php_info_print_table_row(2, "'xdebug://gateway' pseudo-host support", "yes"); #else php_info_print_table_row(2, "'xdebug://gateway' pseudo-host support", "no"); #endif #if HAVE_RES_NINIT php_info_print_table_row(2, "'xdebug://nameserver' pseudo-host support", "yes"); #else php_info_print_table_row(2, "'xdebug://nameserver' pseudo-host support", "no"); #endif if (XG_BASE(private_tmp)) { php_info_print_table_row(2, "Systemd Private Temp Directory", XG_BASE(private_tmp)); } else { php_info_print_table_row(2, "Systemd Private Temp Directory", "not enabled"); } php_info_print_table_end(); } PHPAPI extern char *php_ini_opened_path; PHPAPI extern char *php_ini_scanned_path; PHPAPI extern char *php_ini_scanned_files; static void xdebug_print_php_section(void) { php_info_print_table_start(); php_info_print_table_colspan_header(2, (char*) "PHP"); php_info_print_table_colspan_header(2, (char*) "Build Configuration"); php_info_print_table_row(2, "Version (Run Time)", XG_BASE(php_version_run_time)); php_info_print_table_row(2, "Version (Compile Time)", XG_BASE(php_version_compile_time)); #if ZEND_DEBUG php_info_print_table_row(2, "Debug Build", "yes"); #else php_info_print_table_row(2, "Debug Build", "no"); #endif #ifdef ZTS php_info_print_table_row(2, "Thread Safety", "enabled"); php_info_print_table_row(2, "Thread API", tsrm_api_name()); #else php_info_print_table_row(2, "Thread Safety", "disabled"); #endif php_info_print_table_colspan_header(2, (char*) "Settings"); php_info_print_table_row(2, "Configuration File (php.ini) Path", PHP_CONFIG_FILE_PATH); php_info_print_table_row(2, "Loaded Configuration File", php_ini_opened_path ? php_ini_opened_path : "(none)"); php_info_print_table_row(2, "Scan this dir for additional .ini files", php_ini_scanned_path ? php_ini_scanned_path : "(none)"); php_info_print_table_row(2, "Additional .ini files parsed", php_ini_scanned_files ? php_ini_scanned_files : "(none)"); php_info_print_table_end(); } static ZEND_COLD void php_ini_displayer_cb(zend_ini_entry *ini_entry, int type) { if (ini_entry->displayer) { ini_entry->displayer(ini_entry, type); } else { const char *display_string; size_t display_string_length; int esc_html=0; if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { if (ini_entry->orig_value && ZSTR_VAL(ini_entry->orig_value)[0]) { display_string = ZSTR_VAL(ini_entry->orig_value); display_string_length = ZSTR_LEN(ini_entry->orig_value); esc_html = !sapi_module.phpinfo_as_text; } else { if (!sapi_module.phpinfo_as_text) { display_string = "no value"; display_string_length = sizeof("no value") - 1; } else { display_string = "no value"; display_string_length = sizeof("no value") - 1; } } } else if (ini_entry->value && ZSTR_VAL(ini_entry->value)[0]) { display_string = ZSTR_VAL(ini_entry->value); display_string_length = ZSTR_LEN(ini_entry->value); esc_html = !sapi_module.phpinfo_as_text; } else { if (!sapi_module.phpinfo_as_text) { display_string = "no value"; display_string_length = sizeof("no value") - 1; } else { display_string = "no value"; display_string_length = sizeof("no value") - 1; } } if (esc_html) { zend_html_puts(display_string, display_string_length); } else { PHPWRITE(display_string, display_string_length); } } } static int if_overridden_xdebug_mode(char *name) { if ((strcmp("xdebug.mode", name) == 0) && XG_LIB(mode_from_environment)) { return 1; } return 0; } static int is_using_private_tmp_directory(char *file_name) { if (!file_name) { return 0; } return (XG_BASE(private_tmp) && (strstr(file_name, "/tmp") == file_name)); } static const char* private_tmp_directory(char *file_name) { if (is_using_private_tmp_directory(file_name)) { return XG_BASE(private_tmp); } return ""; } static void xdebug_print_settings(void) { zend_module_entry *module; zend_ini_entry *ini_entry; int module_number; zend_string *name = zend_string_init("xdebug", 6, 0); module = zend_hash_find_ptr(&module_registry, name); zend_string_release(name); if (!module) { return; } module_number = module->module_number; php_info_print_table_start(); if (!sapi_module.phpinfo_as_text) { php_info_print_table_header(4, "Directive", "Local Value", "Master Value", "Docs"); } else { php_info_print_table_header(3, "Directive", "Local Value", "Master Value"); } ZEND_HASH_FOREACH_PTR(EG(ini_directives), ini_entry) { if (ini_entry->module_number != module_number) { continue; } /* Hack to not show changed and removed settings */ if (ini_entry->value && strncmp(ZSTR_VAL(ini_entry->value), "This setting has", 16) == 0) { continue; } if (!sapi_module.phpinfo_as_text) { PUTS(""); PUTS(""); PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name)); if (if_overridden_xdebug_mode(ZSTR_VAL(ini_entry->name))) { PUTS(" (through XDEBUG_MODE)"); } PUTS(""); if (if_overridden_xdebug_mode(ZSTR_VAL(ini_entry->name))) { PUTS(getenv("XDEBUG_MODE")); } else { /* Hack for Systemd PrivateTmp */ if ( ( (strcmp(ZSTR_VAL(ini_entry->name), "xdebug.output_dir") == 0) || (strcmp(ZSTR_VAL(ini_entry->name), "xdebug.log") == 0) ) && (ini_entry->value && ZSTR_VAL(ini_entry->value)[0]) && is_using_private_tmp_directory(ZSTR_VAL(ini_entry->value)) ) { PUTS(XG_BASE(private_tmp)); } /* Normal value */ php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE); } PUTS(""); php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG); PUTS("name), ZSTR_LEN(ini_entry->name)); PUTS("\">" DOCS_LINK_ICON "\n"); } else { PHPWRITE(ZSTR_VAL(ini_entry->name), ZSTR_LEN(ini_entry->name)); if (if_overridden_xdebug_mode(ZSTR_VAL(ini_entry->name))) { PUTS(" (through XDEBUG_MODE)"); } PUTS(" => "); if (if_overridden_xdebug_mode(ZSTR_VAL(ini_entry->name))) { PUTS(getenv("XDEBUG_MODE")); } else { php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE); } PUTS(" => "); php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG); PUTS("\n"); } } ZEND_HASH_FOREACH_END(); php_info_print_table_end(); } static void print_html_header(void) { if (sapi_module.phpinfo_as_text) { return; } PUTS("\n"); PUTS(""); PUTS("\n"); PUTS("\n"); PUTS("Xdebug "); PUTS(XDEBUG_VERSION); PUTS(""); PUTS(""); PUTS("\n"); PUTS("
\n"); } static void print_html_footer(void) { if (sapi_module.phpinfo_as_text) { return; } php_output_write("
", strlen("")); } static void print_diagnostic_log(void) { php_info_print_table_start(); if (!sapi_module.phpinfo_as_text) { php_info_print_table_colspan_header(3, (char*) "Diagnostic Log"); } else { php_info_print_table_colspan_header(2, (char*) "Diagnostic Log"); } if (XG_LIB(diagnosis_buffer) && XG_LIB(diagnosis_buffer)->l) { if (!sapi_module.phpinfo_as_text) { PUTS("MessageDocs\n"); } php_output_write(XG_LIB(diagnosis_buffer)->d, XG_LIB(diagnosis_buffer)->l); } else { if (!sapi_module.phpinfo_as_text) { PUTS("No messages\n"); } else { PUTS("No messages\n"); } } php_info_print_table_end(); } static void print_profile_information(void) { char *file_name; if (!XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { return; } file_name = xdebug_get_profiler_filename(); php_info_print_table_start(); if (!sapi_module.phpinfo_as_text) { PUTS("ProfilerDocs\n"); if (file_name) { xdebug_info_printf("Profile File%s%s" DOCS_LINK_ICON "\n", private_tmp_directory(file_name), file_name, xdebug_lib_docs_base()); } else { xdebug_info_printf("Profiler is not active" DOCS_LINK_ICON "\n", xdebug_lib_docs_base()); } } else { php_info_print_table_colspan_header(2, (char*) "Profiler"); if (file_name) { if (is_using_private_tmp_directory(file_name)) { php_info_print_table_row(2, "Profile File Directory", XG_BASE(private_tmp)); } php_info_print_table_row(2, "Profile File", file_name); } else { PUTS("Profiler is not active\n"); } } php_info_print_table_end(); } static void print_step_debug_information(void) { int is_active; if (!XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { return; } is_active = xdebug_is_debug_connection_active(); php_info_print_table_start(); if (!sapi_module.phpinfo_as_text) { PUTS("Step DebuggingDocs\n"); xdebug_info_printf( "Debugger%s" "" DOCS_LINK_ICON "\n", is_active ? "Active" : (XG_DBG(detached) ? "Detached" : "Not Active"), xdebug_lib_docs_base()); if (XG_DBG(context).connected_hostname) { if (strcmp(XINI_DBG(cloud_id), "") == 0) { xdebug_info_printf( "Connected Client%s:%d \n", XG_DBG(context).connected_hostname, XG_DBG(context).connected_port ); } else { xdebug_info_printf( "Xdebug Cloud Host%s:%d \n", XG_DBG(context).connected_hostname, XG_DBG(context).connected_port ); xdebug_info_printf( "Xdebug Cloud ID%s \n", XINI_DBG(cloud_id) ); } } if (XG_DBG(detached)) { xdebug_info_printf( "Detached%s \n", XG_DBG(context).detached_message ? XG_DBG(context).detached_message : "Yes" ); } else if (XG_DBG(context).options) { xdebug_var_export_options *options; PUTS("DBGp Settings\n"); options = (xdebug_var_export_options*) XG_DBG(context).options; xdebug_info_printf( "Max Children%d \n", options->max_children ); xdebug_info_printf( "Max Data%d \n", options->max_data ); xdebug_info_printf( "Max Depth%d \n", options->max_depth ); xdebug_info_printf( "Show Hidden Properties%s \n", options->show_hidden ? "Yes" : "No" ); xdebug_info_printf( "Extended Properties%s \n", options->extended_properties ? "Yes" : "No" ); xdebug_info_printf( "Notifications%s \n", XG_DBG(context).send_notifications ? "Yes" : "No" ); xdebug_info_printf( "Resolved Breakpoints%s \n", XG_DBG(context).resolved_breakpoints ? "Yes" : "No" ); xdebug_info_printf( "Breakpoint Details%s \n", XG_DBG(context).breakpoint_details ? "Yes" : "No" ); } } else { php_info_print_table_colspan_header(2, (char*) "Step Debugging"); if (is_active) { PUTS("Debugger is active\n"); } else { PUTS("Debugger is not active\n"); } if (XG_DBG(context).connected_hostname) { if (strcmp(XINI_DBG(cloud_id), "") == 0) { xdebug_info_printf("Connected Client => %s:%d\n", XG_DBG(context).connected_hostname, XG_DBG(context).connected_port ); } else { xdebug_info_printf( "Xdebug Cloud Host => %s:%d\n", XG_DBG(context).connected_hostname, XG_DBG(context).connected_port ); xdebug_info_printf("Xdebug Cloud ID => %s\n", XINI_DBG(cloud_id)); } } if (XG_DBG(detached)) { xdebug_info_printf("Detached => %s\n", XG_DBG(context).detached_message ? XG_DBG(context).detached_message : "Yes"); } else if (XG_DBG(context).options) { xdebug_var_export_options *options = (xdebug_var_export_options*) XG_DBG(context).options; PUTS("\nDBGp Settings\n"); xdebug_info_printf("Max Children => %d\n", options->max_children); xdebug_info_printf("Max Data => %d\n", options->max_data); xdebug_info_printf("Max Depth => %d\n", options->max_depth); xdebug_info_printf("Show Hidden Properties => %s\n", options->show_hidden ? "Yes" : "No"); xdebug_info_printf("Extended Properties => %s\n", options->extended_properties ? "Yes" : "No"); xdebug_info_printf("Notifications => %s\n", XG_DBG(context).send_notifications ? "Yes" : "No"); xdebug_info_printf("Resolved Breakpoints => %s\n", XG_DBG(context).resolved_breakpoints ? "Yes" : "No"); xdebug_info_printf("Breakpoint: Details => %s\n", XG_DBG(context).breakpoint_details ? "Yes" : "No"); xdebug_info_printf("Breakpoint: Include Return Values => %s\n", XG_DBG(context).breakpoint_include_return_value ? "Yes" : "No"); } } php_info_print_table_end(); } static void print_trace_information(void) { char *file_name; if (!XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { return; } file_name = xdebug_get_trace_filename(); php_info_print_table_start(); if (!sapi_module.phpinfo_as_text) { PUTS("Function TracingDocs\n"); if (file_name) { xdebug_info_printf("Trace File%s%s" DOCS_LINK_ICON "\n", private_tmp_directory(file_name), file_name, xdebug_lib_docs_base()); } else { xdebug_info_printf("Function tracing is not active" DOCS_LINK_ICON "\n", xdebug_lib_docs_base()); } } else { php_info_print_table_colspan_header(2, (char*) "Function Tracing"); if (file_name) { if (is_using_private_tmp_directory(file_name)) { php_info_print_table_row(2, "Trace File Directory", XG_BASE(private_tmp)); } php_info_print_table_row(2, "Trace File", file_name); } else { PUTS("Function tracing is not active\n"); } } php_info_print_table_end(); } static void xdebug_display_all_info(void) { print_html_header(); xdebug_print_info(); print_diagnostic_log(); print_step_debug_information(); print_profile_information(); print_trace_information(); xdebug_print_php_section(); xdebug_print_settings(); print_html_footer(); } static void info_modes_set(INTERNAL_FUNCTION_PARAMETERS) { array_init_size(return_value, 6); if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { add_next_index_stringl(return_value, "coverage", 8); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { add_next_index_stringl(return_value, "debug", 5); } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { add_next_index_stringl(return_value, "develop", 7); } if (XDEBUG_MODE_IS(XDEBUG_MODE_GCSTATS)) { add_next_index_stringl(return_value, "gcstats", 7); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { add_next_index_stringl(return_value, "profile", 7); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { add_next_index_stringl(return_value, "trace", 5); } } static void info_extension_flags_set(INTERNAL_FUNCTION_PARAMETERS) { array_init_size(return_value, 1); #if HAVE_XDEBUG_ZLIB add_next_index_stringl(return_value, "compression", strlen("compression")); #endif #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT add_next_index_stringl(return_value, "control-socket", strlen("control-socket")); if (XG_BASE(working_tsc_clock) == 1) { add_next_index_stringl(return_value, "tsc", strlen("tsc")); } #endif } PHP_FUNCTION(xdebug_info) { zend_string *group = NULL; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_STR(group) ZEND_PARSE_PARAMETERS_END(); if (group == NULL) { xdebug_display_all_info(); return; } if (zend_string_equals_literal(group, "mode")) { info_modes_set(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } if (zend_string_equals_literal(group, "extension-flags")) { info_extension_flags_set(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } php_error_docref(NULL, E_WARNING, "The information group '%s' is not available", ZSTR_VAL(group)); } void xdebug_open_log(void) { /* initialize remote log file */ XG_LIB(log_file) = NULL; XG_LIB(log_opened_message_sent) = 0; XG_LIB(log_open_timestring) = NULL; if (XINI_LIB(log) && strlen(XINI_LIB(log))) { XG_LIB(log_file) = xdebug_fopen(XINI_LIB(log), "a", NULL, NULL); } if (XG_LIB(log_file)) { XG_LIB(log_open_timestring) = xdebug_nanotime_to_chars(xdebug_get_nanotime(), 6); } else if (strlen(XINI_LIB(log))) { xdebug_log_diagnose_permissions(XLOG_CHAN_LOGFILE, NULL, XINI_LIB(log)); } } void xdebug_close_log() { char *timestr; if (!XG_LIB(log_file)) { return; } if (XG_LIB(log_opened_message_sent)) { zend_ulong pid; pid = xdebug_get_pid(); timestr = xdebug_nanotime_to_chars(xdebug_get_nanotime(), 6); fprintf(XG_LIB(log_file), "[" ZEND_ULONG_FMT "] Log closed at %s\n\n", pid, timestr); fflush(XG_LIB(log_file)); xdfree(timestr); } if (XG_LIB(log_open_timestring)) { xdfree(XG_LIB(log_open_timestring)); XG_LIB(log_open_timestring) = NULL; } fclose(XG_LIB(log_file)); XG_LIB(log_file) = NULL; } xdebug-3.4.3/src/lib/log.h0000664000175000017500000000377215011062311014553 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_LIBRARY_LOG_H__ #define __XDEBUG_LIBRARY_LOG_H__ #ifdef ZTS # include "TSRM.h" #endif #include "lib.h" #include "zend.h" #include "zend_API.h" #include "compat.h" #define XLOG_CRIT 0 #define XLOG_ERR 1 #define XLOG_WARN 3 #define XLOG_COM 5 #define XLOG_INFO 7 #define XLOG_DEBUG 10 #define XLOG_DEFAULT "7" /* as a string, as that's what STD_PHP_INI_ENTRY wants */ #define XLOG_CHAN_CONFIG 0 #define XLOG_CHAN_LOGFILE 1 #define XLOG_CHAN_DEBUG 2 #define XLOG_CHAN_GCSTATS 3 #define XLOG_CHAN_PROFILE 4 #define XLOG_CHAN_TRACE 5 #define XLOG_CHAN_COVERAGE 6 #define XLOG_CHAN_BASE 7 extern const char* xdebug_log_prefix[11]; void XDEBUG_ATTRIBUTE_FORMAT(printf, 4, 5) xdebug_log_ex(int channel, int log_level, const char *error_code, const char *fmt, ...); #define xdebug_log(c,l,f, ...) xdebug_log_ex((c), (l), NULL, (f), ##__VA_ARGS__) void xdebug_log_diagnose_permissions(int channel, const char *directory, const char *filename); char* xdebug_lib_docs_base(void); #endif xdebug-3.4.3/src/lib/lib_private.h0000664000175000017500000000247215011062311016266 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_LIBRARY_PRIVATE_H__ #define __XDEBUG_LIBRARY_PRIVATE_H__ #define XG_LIB(v) (XG(globals.library.v)) #define XINI_LIB(v) (XG(settings.library.v)) void xdebug_open_log(void); void xdebug_close_log(void); void XDEBUG_ATTRIBUTE_FORMAT(printf, 3, 4) xdebug_log(int channel, int log_level, const char *fmt, ...); #endif xdebug-3.4.3/src/lib/llist.c0000664000175000017500000000764015011062311015112 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include "llist.h" xdebug_llist *xdebug_llist_alloc(xdebug_llist_dtor dtor) { xdebug_llist *l; l = malloc(sizeof(xdebug_llist)); xdebug_llist_init(l, dtor); return l; } void xdebug_llist_init(xdebug_llist *l, xdebug_llist_dtor dtor) { l->size = 0; l->dtor = dtor; l->head = NULL; l->tail = NULL; } int xdebug_llist_insert_next(xdebug_llist *l, xdebug_llist_element *e, const void *p) { xdebug_llist_element *ne; if (!e) { e = XDEBUG_LLIST_TAIL(l); } ne = (xdebug_llist_element *) malloc(sizeof(xdebug_llist_element)); ne->ptr = (void *) p; if (l->size == 0) { l->head = ne; l->head->prev = NULL; l->head->next = NULL; l->tail = ne; } else { ne->next = e->next; ne->prev = e; if (e->next) { e->next->prev = ne; } else { l->tail = ne; } e->next = ne; } ++l->size; return 1; } int xdebug_llist_insert_prev(xdebug_llist *l, xdebug_llist_element *e, const void *p) { xdebug_llist_element *ne; if (!e) { e = XDEBUG_LLIST_HEAD(l); } ne = (xdebug_llist_element *) malloc(sizeof(xdebug_llist_element)); ne->ptr = (void *) p; if (l->size == 0) { l->head = ne; l->head->prev = NULL; l->head->next = NULL; l->tail = ne; } else { ne->next = e; ne->prev = e->prev; if (e->prev) e->prev->next = ne; else l->head = ne; e->prev = ne; } ++l->size; return 0; } int xdebug_llist_remove(xdebug_llist *l, xdebug_llist_element *e, void *user) { if (e == NULL || l->size == 0) return 0; if (e == l->head) { l->head = e->next; if (l->head == NULL) l->tail = NULL; else e->next->prev = NULL; } else { e->prev->next = e->next; if (!e->next) l->tail = e->prev; else e->next->prev = e->prev; } if (l->dtor) { l->dtor(user, e->ptr); } free(e); --l->size; return 0; } int xdebug_llist_remove_next(xdebug_llist *l, xdebug_llist_element *e, void *user) { return xdebug_llist_remove(l, e->next, user); } int xdebug_llist_remove_prev(xdebug_llist *l, xdebug_llist_element *e, void *user) { return xdebug_llist_remove(l, e->prev, user); } xdebug_llist_element *xdebug_llist_jump(xdebug_llist *l, int where, int pos) { xdebug_llist_element *e=NULL; int i; if (where == LIST_HEAD) { e = XDEBUG_LLIST_HEAD(l); for (i = 0; i < pos; ++i) { e = XDEBUG_LLIST_NEXT(e); } } else if (where == LIST_TAIL) { e = XDEBUG_LLIST_TAIL(l); for (i = 0; i < pos; ++i) { e = XDEBUG_LLIST_PREV(e); } } return e; } size_t xdebug_llist_count(xdebug_llist *l) { return l->size; } void xdebug_llist_empty(xdebug_llist *l, void *user) { while (xdebug_llist_count(l) > 0 && XDEBUG_LLIST_TAIL(l)) { xdebug_llist_remove(l, XDEBUG_LLIST_TAIL(l), user); } } void xdebug_llist_destroy(xdebug_llist *l, void *user) { xdebug_llist_empty(l, user); free (l); } /* * Local Variables: * c-basic-offset: 4 * tab-width: 4 * End: * vim600: fdm=marker * vim: noet sw=4 ts=4 */ xdebug-3.4.3/src/lib/llist.h0000664000175000017500000000511215011062311015107 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_LLIST_H__ #define __XDEBUG_LLIST_H__ #include typedef void (*xdebug_llist_dtor)(void *, void *); typedef struct _xdebug_llist_element { void *ptr; struct _xdebug_llist_element *prev; struct _xdebug_llist_element *next; } xdebug_llist_element; typedef struct _xdebug_llist { xdebug_llist_element *head; xdebug_llist_element *tail; xdebug_llist_dtor dtor; size_t size; } xdebug_llist; xdebug_llist *xdebug_llist_alloc(xdebug_llist_dtor dtor); void xdebug_llist_init(xdebug_llist *l, xdebug_llist_dtor dtor); int xdebug_llist_insert_next(xdebug_llist *l, xdebug_llist_element *e, const void *p); int xdebug_llist_insert_prev(xdebug_llist *l, xdebug_llist_element *e, const void *p); int xdebug_llist_remove(xdebug_llist *l, xdebug_llist_element *e, void *user); int xdebug_llist_remove_next(xdebug_llist *l, xdebug_llist_element *e, void *user); xdebug_llist_element *xdebug_llist_jump(xdebug_llist *l, int where, int pos); size_t xdebug_llist_count(xdebug_llist *l); void xdebug_llist_empty(xdebug_llist *l, void *user); void xdebug_llist_destroy(xdebug_llist *l, void *user); #if !defined(LIST_HEAD) #define LIST_HEAD 0 #endif #if !defined(LIST_TAIL) #define LIST_TAIL 1 #endif #define XDEBUG_LLIST_HEAD(__l) ((__l)->head) #define XDEBUG_LLIST_TAIL(__l) ((__l)->tail) #define XDEBUG_LLIST_NEXT(__e) ((__e)->next) #define XDEBUG_LLIST_PREV(__e) ((__e)->prev) #define XDEBUG_LLIST_VALP(__e) ((__e)->ptr) #define XDEBUG_LLIST_IS_TAIL(__e) ((__e)->next ? 0 : 1) #define XDEBUG_LLIST_IS_HEAD(__e) ((__e)->prev ? 0 : 1) #define XDEBUG_LLIST_COUNT(__l) ((__l)->size) #endif /* __XDEBUG_LLIST_H__ */ xdebug-3.4.3/src/lib/mm.h0000664000175000017500000000264315011062311014377 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_MM_H__ #define __HAVE_XDEBUG_MM_H__ /* Memory allocators */ #if 0 #define xdmalloc emalloc #define xdcalloc ecalloc #define xdrealloc erealloc #define xdfree efree #define xdstrdup estrdup #define xdstrndup estrndup #else #define xdmalloc malloc #define xdcalloc calloc #define xdrealloc realloc #define xdfree free #define xdstrdup strdup #define xdstrndup xdebug_strndup #endif #endif xdebug-3.4.3/src/lib/php-header.h0000664000175000017500000000215315011062311015777 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" #include "php.h" #pragma GCC diagnostic pop xdebug-3.4.3/src/lib/set.c0000664000175000017500000000411715011062311014552 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include "set.h" xdebug_set *xdebug_set_create(unsigned int size) { xdebug_set *tmp; tmp = calloc(1, sizeof(xdebug_set)); tmp->size = size; size = (size / 8) + 1 + ((size % 8) != 0); tmp->setinfo = calloc(1, size); return tmp; } void xdebug_set_free(xdebug_set *set) { free(set->setinfo); free(set); } void xdebug_set_add(xdebug_set *set, unsigned int position) { unsigned char *byte; unsigned int bit; byte = &(set->setinfo[position / 8]); bit = position % 8; *byte = *byte | 1 << bit; } void xdebug_set_remove(xdebug_set *set, unsigned int position) { unsigned char *byte; unsigned int bit; byte = &(set->setinfo[position / 8]); bit = position % 8; *byte = *byte & ~(1 << bit); } int xdebug_set_in_ex(xdebug_set *set, unsigned int position, int noisy) { unsigned char *byte; unsigned int bit; byte = &(set->setinfo[position / 8]); bit = position % 8; return (*byte & (1 << bit)); } void xdebug_set_dump(xdebug_set *set) { unsigned int i; for (i = 0; i < set->size; i++) { if (xdebug_set_in_ex(set, i, 0)) { printf("%02d ", i); } } } xdebug-3.4.3/src/lib/set.h0000664000175000017500000000276115011062311014562 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_SET_H__ #define __XDEBUG_SET_H__ typedef struct _xdebug_set { unsigned int size; unsigned char *setinfo; } xdebug_set; xdebug_set *xdebug_set_create(unsigned int size); void xdebug_set_add(xdebug_set *set, unsigned int position); void xdebug_set_remove(xdebug_set *set, unsigned int position); #define xdebug_set_in(x,y) xdebug_set_in_ex(x,y,1) int xdebug_set_in_ex(xdebug_set *set, unsigned int position, int noisy); void xdebug_set_dump(xdebug_set *set); void xdebug_set_free(xdebug_set *set); #endif xdebug-3.4.3/src/lib/str.c0000664000175000017500000001260215011062311014565 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include #include #include #if PHP_VERSION_ID < 80200 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeclaration-after-statement" # include "zend_smart_str.h" # pragma GCC diagnostic pop #endif #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "mm.h" #include "str.h" inline static void realloc_if_needed(xdebug_str *xs, int size_to_fit) { if (!xs->a || !xs->l || xs->l + size_to_fit > xs->a - 1) { xs->d = xdrealloc(xs->d, xs->a + size_to_fit + XDEBUG_STR_PREALLOC); xs->a = xs->a + size_to_fit + XDEBUG_STR_PREALLOC; } if (!xs->l) { xs->d[0] = '\0'; } } inline static void xdebug_str_internal_addl(xdebug_str *xs, const char *str, int le, int f) { realloc_if_needed(xs, le); memcpy(xs->d + xs->l, str, le); xs->d[xs->l + le] = '\0'; xs->l = xs->l + le; if (f) { xdfree((char*) str); } } void xdebug_str_add(xdebug_str *xs, const char *str, int f) { xdebug_str_internal_addl(xs, str, strlen(str), f); } void xdebug_str_addl(xdebug_str *xs, const char *str, int le, int f) { xdebug_str_internal_addl(xs, str, le, f); } void xdebug_str_add_str(xdebug_str *xs, const xdebug_str *str) { xdebug_str_internal_addl(xs, str->d, str->l, 0); } void xdebug_str_add_zstr(xdebug_str *xs, const zend_string *str) { xdebug_str_internal_addl(xs, ZSTR_VAL(str), ZSTR_LEN(str), 0); } void xdebug_str_addc(xdebug_str *xs, char letter) { realloc_if_needed(xs, 1); xs->d[xs->l] = letter; xs->d[xs->l + 1] = '\0'; xs->l = xs->l + 1; } void xdebug_str_add_uint64(xdebug_str *xs, uint64_t num) { char buffer[21]; char *pos; int digit; pos = &buffer[20]; *pos = '\0'; do { digit = num % 10; num = num / 10; if (digit < 10) { *--pos = '0' + digit; } else { *--pos = 'a' + digit - 10; } } while (num != 0L); xdebug_str_internal_addl(xs, pos, &buffer[20] - pos, 0); } #if PHP_VERSION_ID >= 80200 void xdebug_str_add_va_fmt(xdebug_str *xs, const char *fmt, va_list argv) { int size; int n; va_list argv_size, argv_copy; realloc_if_needed(xs, 1); size = xs->a - xs->l; va_copy(argv_size, argv); n = vsnprintf(xs->d + xs->l, size, fmt, argv_size); va_end(argv_size); if (n > -1 && n < size) { xs->l += n; return; } realloc_if_needed(xs, n + 1); size = xs->a - xs->l; va_copy(argv_copy, argv); n = vsnprintf(xs->d + xs->l, size, fmt, argv_copy); va_end(argv_copy); if (n > -1 && n < size) { xs->l += n; return; } assert(0); } #else void xdebug_str_add_va_fmt(xdebug_str *xs, const char *fmt, va_list argv) { smart_str buf = {0}; php_printf_to_smart_str(&buf, fmt, argv); if (!buf.s) { return; } xdebug_str_add_zstr(xs, buf.s); smart_str_free(&buf); } #endif void xdebug_str_add_fmt(xdebug_str *xs, const char *fmt, ...) { va_list args; va_start(args, fmt); xdebug_str_add_va_fmt(xs, fmt, args); va_end(args); } void xdebug_str_chop(xdebug_str *xs, size_t c) { if (c > xs->l) { /* Do nothing if the chop amount is larger than the buffer size */ } else { xs->l -= c; xs->d[xs->l] = '\0'; } } xdebug_str *xdebug_str_new(void) { xdebug_str *tmp = xdmalloc(sizeof(xdebug_str)); tmp->l = 0; tmp->a = 0; tmp->d = NULL; return tmp; } xdebug_str *xdebug_str_create(const char *c, size_t len) { xdebug_str *tmp = xdebug_str_new(); tmp->l = tmp->a = len; tmp->a++; tmp->d = xdmalloc(tmp->a); memcpy(tmp->d, c, tmp->l); tmp->d[tmp->l] = '\0'; return tmp; } xdebug_str *xdebug_str_create_from_char(char *c) { return xdebug_str_create(c, strlen(c)); } xdebug_str *xdebug_str_copy(xdebug_str *orig) { xdebug_str *tmp = xdebug_str_new(); tmp->l = tmp->a = orig->l; tmp->a++; tmp->d = xdmalloc(tmp->a); memcpy(tmp->d, orig->d, tmp->l); tmp->d[orig->l] = '\0'; return tmp; } void xdebug_str_destroy(xdebug_str *s) { if (s->d) { xdfree(s->d); } } void xdebug_str_free(xdebug_str *s) { xdebug_str_destroy(s); xdfree(s); } char *xdebug_sprintf(const char* fmt, ...) { va_list args; xdebug_str tmp_str = {0}; va_start(args, fmt); xdebug_str_add_va_fmt(&tmp_str, fmt, args); va_end(args); return tmp_str.d; } /** * Duplicate zend_strndup in core to avoid mismatches * in C-runtime libraries when xdebug and core are built * with different run-time libraries. */ char *xdebug_strndup(const char *s, int length) { char *p; p = (char *) xdmalloc(length + 1); if (p == NULL) { return p; } if (length) { memcpy(p, s, length); } p[length] = 0; return p; } xdebug-3.4.3/src/lib/str.h0000664000175000017500000000502315011062311014571 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_STR_H__ #define __HAVE_XDEBUG_STR_H__ #include #include "lib/php-header.h" #include "mm.h" #define XDEBUG_STR_INITIALIZER { 0, 0, NULL } #define XDEBUG_STR_PREALLOC 1024 #define xdebug_str_dtor(str) xdfree(str.d) #define XDEBUG_STR_WRAP_CHAR(v) (&((xdebug_str){strlen(v), strlen(v)+1, ((char*)(v))})) typedef struct xdebug_str { size_t l; size_t a; char *d; } xdebug_str; void xdebug_str_add(xdebug_str *xs, const char *str, int f); void xdebug_str_addl(xdebug_str *xs, const char *str, int le, int f); void xdebug_str_add_str(xdebug_str *xs, const xdebug_str *str); void xdebug_str_add_zstr(xdebug_str *xs, const zend_string *str); void xdebug_str_addc(xdebug_str *xs, char letter); void xdebug_str_add_uint64(xdebug_str *xs, uint64_t num); void xdebug_str_add_fmt(xdebug_str *xs, const char *fmt, ...); void xdebug_str_add_va_fmt(xdebug_str *xs, const char *fmt, va_list argv); #define xdebug_str_add_literal(s,l) xdebug_str_addl((s), (l), sizeof(l)-1, 0) #define xdebug_str_add_const(s,l) xdebug_str_addl((s), (l), strlen(l), 0) void xdebug_str_chop(xdebug_str *xs, size_t c); xdebug_str *xdebug_str_new(void); xdebug_str *xdebug_str_create_from_char(char *c); #define xdebug_str_create_from_const_char(c) xdebug_str_create_from_char((char*) (c)) xdebug_str *xdebug_str_create(const char *c, size_t len); xdebug_str *xdebug_str_copy(xdebug_str *orig); void xdebug_str_destroy(xdebug_str *s); void xdebug_str_free(xdebug_str *s); char* xdebug_sprintf(const char* fmt, ...); char* xdebug_strndup(const char *s, int length); #endif xdebug-3.4.3/src/lib/timing.c0000664000175000017500000001505615011062311015252 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #if PHP_WIN32 # include "win32/time.h" # include #else # include #endif #if HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP # if defined(__APPLE) # include # endif #endif #include "timing.h" #define NANOTIME_MIN_STEP 10 #if PHP_WIN32 # define WIN_NANOS_IN_TICK 100 # define WIN_TICKS_SINCE_1601_JAN_1 116444736000000000ULL #endif ZEND_EXTERN_MODULE_GLOBALS(xdebug) static uint64_t xdebug_get_nanotime_abs(xdebug_nanotime_context *nanotime_context) { #if PHP_WIN32 // Windows, win_precise_time_func is only available on Win 8 and later. { FILETIME filetime; if (nanotime_context->win_precise_time_func != NULL) { nanotime_context->win_precise_time_func(&filetime); return ((((uint64_t)filetime.dwHighDateTime << 32) + (uint64_t)filetime.dwLowDateTime) - WIN_TICKS_SINCE_1601_JAN_1) * WIN_NANOS_IN_TICK; } } #endif #if HAVE_GETTIMEOFDAY | PHP_WIN32 // Fallback to gettimeofday() if better platform specific time is not // available. gettimeofday() is always available in PHP on Windows, as it's // expose through 'win32/time.[ch]' { struct timeval tp; if (gettimeofday(&tp, NULL) == 0) { return (uint64_t)tp.tv_sec * NANOS_IN_SEC + (uint64_t)tp.tv_usec * NANOS_IN_MICROSEC; } } #endif // We give up php_error(E_WARNING, "Xdebug could not determine a suitable clock source on your system"); return 0; } #if PHP_WIN32 static uint64_t xdebug_counter_and_freq_to_nanotime(uint64_t counter, uint64_t freq) { uint32_t mul = 1, freq32; uint64_t q, r; while (freq >= (1ULL << 32)) { freq /= 2; mul *= 2; } freq32 = (uint32_t)freq; q = counter / freq32; r = counter % freq32; return (q * NANOS_IN_SEC + (r * NANOS_IN_SEC) / freq32) * mul; } #endif // Windows 7 and lower #if PHP_WIN32 static uint64_t xdebug_get_nanotime_rel(xdebug_nanotime_context *nanotime_context) { LARGE_INTEGER tcounter; if (nanotime_context->win_precise_time_func == NULL) { QueryPerformanceCounter(&tcounter); return xdebug_counter_and_freq_to_nanotime((uint64_t)tcounter.QuadPart, nanotime_context->win_freq); } } // Mac // should be fast but can be relative #elif HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP static uint64_t xdebug_get_nanotime_rel(xdebug_nanotime_context *nanotime_context) { return clock_gettime_nsec_np(CLOCK_UPTIME_RAW); } // Linux/Unix with clock_gettime #elif HAVE_XDEBUG_CLOCK_GETTIME static uint64_t xdebug_get_nanotime_rel(xdebug_nanotime_context *nanotime_context) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { return (uint64_t)ts.tv_sec * NANOS_IN_SEC + (uint64_t)ts.tv_nsec; } return 0; } # ifdef __linux__ bool detect_linux_working_tsc_clock(void) { FILE *current_clocksource; char contents[64]; current_clocksource = fopen("/sys/devices/system/clocksource/clocksource0/current_clocksource", "r"); if (!current_clocksource) { /* Inconclusive, assume working clock */ return true; } if (!fgets(contents, sizeof(contents), current_clocksource)) { /* Can't read any clock source */ fclose(current_clocksource); return false; } if (strcmp(contents, "tsc\n") != 0) { /* Current clock is not 'tsc' (+ newline) */ fclose(current_clocksource); return false; } fclose(current_clocksource); return true; } # endif #endif void xdebug_nanotime_init(xdebug_base_globals_t *base) { xdebug_nanotime_context context = {0}; base->working_tsc_clock = -1; #if PHP_WIN32 LARGE_INTEGER tcounter; if (IsWindows8OrGreater()) { context.win_precise_time_func = (WIN_PRECISE_TIME_FUNC)GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetSystemTimePreciseAsFileTime" ); } else { context.win_precise_time_func = NULL; QueryPerformanceFrequency(&tcounter); context.win_freq = (uint64_t)tcounter.QuadPart; context.use_rel_time = 1; } #elif HAVE_XDEBUG_CLOCK_GETTIME | HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP context.use_rel_time = 1; #endif context.start_abs = xdebug_get_nanotime_abs(&context); context.last_abs = 0; #if PHP_WIN32 | HAVE_XDEBUG_CLOCK_GETTIME | HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP context.start_rel = xdebug_get_nanotime_rel(&context); context.last_rel = 0; #endif #ifdef __linux__ # if HAVE_XDEBUG_CLOCK_GETTIME /* Detect if we have a 'broken' tsc clock on Linux for clock_gettime */ base->working_tsc_clock = detect_linux_working_tsc_clock(); # endif #endif base->nanotime_context = context; } uint64_t xdebug_get_nanotime(void) { uint64_t nanotime; xdebug_nanotime_context *context; context = &XG_BASE(nanotime_context); #if PHP_WIN32 | HAVE_XDEBUG_CLOCK_GETTIME | HAVE_XDEBUG_CLOCK_GETTIME_NSEC_NP /* Relative timing */ if (context->use_rel_time) { nanotime = xdebug_get_nanotime_rel(context); if (nanotime < context->last_rel + NANOTIME_MIN_STEP) { context->last_rel += NANOTIME_MIN_STEP; nanotime = context->last_rel; } context->last_rel = nanotime; nanotime = context->start_abs + (nanotime - context->start_rel); return nanotime; } #endif /* Absolute timing */ nanotime = xdebug_get_nanotime_abs(context); if (nanotime < context->last_abs + NANOTIME_MIN_STEP) { context->last_abs += NANOTIME_MIN_STEP; nanotime = context->last_abs; } context->last_abs = nanotime; return nanotime; } char* xdebug_nanotime_to_chars(uint64_t nanotime, unsigned char precision) { char *res; time_t secs; secs = (time_t)(nanotime / NANOS_IN_SEC); if (precision > 0) { res = xdmalloc(30); } else { res = xdmalloc(20); } strftime(res, 20, "%Y-%m-%d %H:%M:%S", gmtime(&secs)); if (precision > 0) { sprintf(res + 19, ".%09u", (uint32_t)(nanotime % NANOS_IN_SEC)); if (precision < 9) { *(res + 20 + precision) = '\0'; } } return res; } xdebug-3.4.3/src/lib/timing.h0000664000175000017500000000264215011062311015254 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_TIMING_H__ #define __XDEBUG_TIMING_H__ #define NANOS_IN_MICROSEC 1000 #define NANOS_IN_MILLISEC 1000000 #define NANOS_IN_SEC 1000000000 void xdebug_nanotime_init(xdebug_base_globals_t *xg); uint64_t xdebug_get_nanotime(void); char* xdebug_nanotime_to_chars(uint64_t nanotime, unsigned char precision); #define XDEBUG_SECONDS_SINCE_START(nanotime) (((nanotime) - XG_BASE(start_nanotime)) / (double)NANOS_IN_SEC) #endif xdebug-3.4.3/src/lib/var.c0000664000175000017500000010114115011062311014542 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2025 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "ext/standard/url.h" #include "zend.h" #include "zend_exceptions.h" #include "zend_extensions.h" #include "ext/standard/php_smart_string.h" #include "zend_smart_str.h" #include "zend_closures.h" #include "php_xdebug.h" #include "lib/compat.h" #include "lib/lib_private.h" #include "lib/mm.h" #include "lib/var.h" #include "lib/var_export_html.h" #include "lib/var_export_line.h" #include "lib/xml.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) static inline int object_or_ancestor_is_internal(zval dzval) { zend_class_entry *tmp_ce = Z_OBJCE(dzval); do { if (tmp_ce->type == ZEND_INTERNAL_CLASS) { return 1; } tmp_ce = tmp_ce->parent; } while (tmp_ce); return 0; } typedef struct _xdebug_zend_closure_copy { zend_object std; zend_function func; zval this_ptr; zend_class_entry *called_scope; zif_handler orig_internal_handler; } xdebug_zend_closure_copy; static int object_with_missing_closure_variables(zval dzval) { zend_class_entry *tmp_ce = NULL; xdebug_zend_closure_copy *closure = NULL; if (Z_TYPE(dzval) != IS_OBJECT) { return 0; } tmp_ce = Z_OBJCE(dzval); if (tmp_ce != zend_ce_closure) { return 0; } closure = (xdebug_zend_closure_copy *) Z_OBJ(dzval); if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) { HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); if (!static_variables) { return 1; } } return 0; } HashTable *xdebug_objdebug_pp(zval **zval_pp, int flags) { zval dzval = **zval_pp; HashTable *tmp; if ( !XG_BASE(in_debug_info) && (object_or_ancestor_is_internal(dzval) || (flags & XDEBUG_VAR_OBJDEBUG_USE_DEBUGINFO)) && !object_with_missing_closure_variables(dzval) && Z_OBJ_HANDLER(dzval, get_debug_info) && !EG(exception) ) { void *original_trace_context; zend_object *orig_exception; xdebug_tracing_save_trace_context(&original_trace_context); XG_BASE(in_debug_info) = 1; orig_exception = EG(exception); EG(exception) = NULL; tmp = zend_get_properties_for(&dzval, ZEND_PROP_PURPOSE_DEBUG); if (EG(exception)) { zend_clear_exception(); } XG_BASE(in_debug_info) = 0; xdebug_tracing_restore_trace_context(original_trace_context); EG(exception) = orig_exception; return tmp; } else { return zend_get_properties_for(&dzval, ZEND_PROP_PURPOSE_VAR_EXPORT); } return NULL; } char* xdebug_error_type_simple(int type) { switch (type) { case E_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: case E_USER_ERROR: return xdstrdup("fatal-error"); break; case E_RECOVERABLE_ERROR: return xdstrdup("recoverable-fatal-error"); break; case E_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: case E_USER_WARNING: return xdstrdup("warning"); break; case E_PARSE: return xdstrdup("parse-error"); break; case E_NOTICE: case E_USER_NOTICE: return xdstrdup("notice"); break; case E_STRICT: return xdstrdup("strict-standards"); break; case E_DEPRECATED: case E_USER_DEPRECATED: return xdstrdup("deprecated"); break; case 0: return xdstrdup("xdebug"); break; default: return xdstrdup("unknown-error"); break; } } char* xdebug_error_type(int type) { switch (type) { case E_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: case E_USER_ERROR: return xdstrdup("Fatal error"); break; case E_RECOVERABLE_ERROR: return xdstrdup("Recoverable fatal error"); break; case E_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: case E_USER_WARNING: return xdstrdup("Warning"); break; case E_PARSE: return xdstrdup("Parse error"); break; case E_NOTICE: case E_USER_NOTICE: return xdstrdup("Notice"); break; case E_STRICT: return xdstrdup("Strict standards"); break; case E_DEPRECATED: case E_USER_DEPRECATED: return xdstrdup("Deprecated"); break; case 0: return xdstrdup("Xdebug"); break; default: return xdstrdup("Unknown error"); break; } } /*************************************************************************************************************************************/ #define T(offset) (*(union _temp_variable *)((char*)zdata->current_execute_data->Ts + offset)) zval *xdebug_get_zval_with_opline(zend_execute_data *zdata, const zend_op *opline, int node_type, const znode_op *node) { return zend_get_zval_ptr(opline, node_type, node, zdata); } zval *xdebug_get_zval(zend_execute_data *zdata, int node_type, const znode_op *node) { return xdebug_get_zval_with_opline(zdata, zdata->opline, node_type, node); } /***************************************************************************** ** PHP Variable related utility functions */ /***************************************************************************** ** Data returning functions */ #define XF_ST_ROOT 0 #define XF_ST_ARRAY_INDEX_NUM 1 #define XF_ST_ARRAY_INDEX_ASSOC 2 #define XF_ST_OBJ_PROPERTY 3 #define XF_ST_STATIC_ROOT 4 #define XF_ST_STATIC_PROPERTY 5 #define XF_ST_ESCAPED_OBJ_PROPERTY 6 inline static HashTable *fetch_ht_from_zval(zval *z) { switch (Z_TYPE_P(z)) { case IS_ARRAY: return Z_ARRVAL_P(z); break; case IS_OBJECT: return Z_OBJPROP_P(z); break; } return NULL; } inline static char *fetch_classname_from_zval(zval *z, int *length, zend_class_entry **ce) { zend_string *class_name; if (Z_TYPE_P(z) == IS_INDIRECT) { z = z->value.zv; } if (Z_TYPE_P(z) == IS_REFERENCE) { z = &z->value.ref->val; } if (Z_TYPE_P(z) != IS_OBJECT) { return NULL; } class_name = Z_OBJ_HANDLER_P(z, get_class_name)(Z_OBJ_P(z)); *ce = Z_OBJCE_P(z); *length = class_name->len; return estrdup(class_name->val); } static char* prepare_search_key(char *name, unsigned int *name_length, const char *prefix, int prefix_length) { char *element; int extra_length = 0; if (prefix_length) { if (prefix[0] == '*') { extra_length = 3; } else { extra_length = 2 + prefix_length; } } element = malloc(*name_length + 1 + extra_length); memset(element, 0, *name_length + 1 + extra_length); if (extra_length) { memcpy(element + 1, prefix, extra_length - 2); } memcpy(element + extra_length, name, *name_length); *name_length += extra_length; return element; } char *replace_star_by_null(const char *name, int name_length) { char *tmp = xdstrdup(name); int i; for (i = 0; i < name_length; i++) { if (tmp[i] == '*') { tmp[i] = '\0'; } } return tmp; } static inline zval *get_spl_storage(zval *parent, HashTable **properties, const char *prop_name, int prop_name_len) { *properties = zend_get_properties_for(parent, ZEND_PROP_PURPOSE_DEBUG); return zend_hash_str_find(*properties, prop_name, prop_name_len); } static int handle_spl_classes( const char *class_name, int class_name_len, const char *prop_name, int prop_name_len, zval *value_in, char **element, HashTable **myht, zval *tmp_retval ) { zval *tmp; /* ArrayObject, ArrayIterator, and SplObjectStorage uses a private 'storage' property */ if (strncmp(prop_name, "storage", prop_name_len) == 0) { if (strncmp(class_name, "ArrayObject", class_name_len) == 0) { tmp = get_spl_storage(value_in, myht, "\0ArrayObject\0storage", sizeof("*ArrayObject*storage") - 1); } else if (strncmp(class_name, "ArrayIterator", class_name_len) == 0) { tmp = get_spl_storage(value_in, myht, "\0ArrayIterator\0storage", sizeof("*ArrayIterator*storage") - 1); } else if (strncmp(class_name, "SplObjectStorage", class_name_len) == 0) { tmp = get_spl_storage(value_in, myht, "\0SplObjectStorage\0storage", sizeof("*SplObjectStorage*storage") - 1); } else { return 1; } } else /* SplDoublyLinkedList uses a private 'dllist' property */ if (strncmp(prop_name, "dllist", prop_name_len) == 0) { if (strncmp(class_name, "SplDoublyLinkedList", class_name_len) == 0) { tmp = get_spl_storage(value_in, myht, "\0SplDoublyLinkedList\0dllist", sizeof("*SplDoublyLinkedList*dllist") - 1); } else { return 1; } } else /* SplPriorityQueue uses a private 'heap' property */ if (strncmp(prop_name, "heap", prop_name_len) == 0) { if (strncmp(class_name, "SplPriorityQueue", class_name_len) == 0) { tmp = get_spl_storage(value_in, myht, "\0SplPriorityQueue\0heap", sizeof("*SplPriorityQueue*heap") - 1); } else { return 1; } } else { return 1; } *element = NULL; if (tmp != NULL) { ZVAL_COPY(tmp_retval, tmp); zend_release_properties(*myht); return 0; } zend_release_properties(*myht); return 1; } static void fetch_zval_from_symbol_table( zval *value_in, char *name, unsigned int name_length, int type, char* ccn, int ccnl, zend_class_entry *cce) { HashTable *ht = NULL; char *element = NULL; unsigned int element_length = name_length; zend_property_info *zpp; int free_duplicated_name = 0; HashTable *myht = NULL; zval *orig_value_in = value_in; zval tmp_retval; ZVAL_UNDEF(&tmp_retval); if (Z_TYPE_P(value_in) == IS_INDIRECT) { value_in = Z_INDIRECT_P(value_in); } ZVAL_DEREF(value_in); ht = fetch_ht_from_zval(value_in); switch (type) { case XF_ST_STATIC_ROOT: case XF_ST_STATIC_PROPERTY: /* First we try a public,private,protected property */ if (cce && (cce->type == ZEND_INTERNAL_CLASS || (cce->ce_flags & ZEND_ACC_IMMUTABLE))) { zend_class_init_statics(cce); } element = prepare_search_key(name, &element_length, "", 0); if (cce && ((zpp = zend_hash_str_find_ptr(&cce->properties_info, element, element_length)) != NULL) && cce->default_static_members_count && CE_STATIC_MEMBERS(cce)) { ZVAL_COPY(&tmp_retval, &CE_STATIC_MEMBERS(cce)[zpp->offset]); goto cleanup; } element_length = name_length; /* Then we try to see whether the first char is * and use the part between * and * as class name for the private property */ if (name[0] == '*') { char *secondStar; secondStar = strstr(name + 1, "*"); if (secondStar) { free(element); element_length = name_length - (secondStar + 1 - name); element = prepare_search_key(secondStar + 1, &element_length, "", 0); if (cce && ((zpp = zend_hash_str_find_ptr(&cce->properties_info, element, element_length)) != NULL)) { ZVAL_COPY(&tmp_retval, &CE_STATIC_MEMBERS(cce)[zpp->offset]); goto cleanup; } } } break; case XF_ST_ROOT: /* Check for return value */ if ( XG_DBG(current_return_value) && (strncmp(name, XDEBUG_RETURN_VALUE_VAR_NAME, name_length) == 0) ) { ZVAL_COPY(&tmp_retval, XG_DBG(current_return_value)); goto cleanup; } /* Check for compiled vars */ element = prepare_search_key(name, &element_length, "", 0); if (xdebug_lib_has_active_data() && xdebug_lib_has_active_function()) { int i = 0; zend_ulong hash_value = zend_inline_hash_func(element, element_length); zend_op_array *opa = xdebug_lib_get_active_func_oparray(); zval **CV; if (ZEND_USER_CODE(opa->type)) { while (opa->vars && i < opa->last_var) { if (ZSTR_H(opa->vars[i]) == hash_value && ZSTR_LEN(opa->vars[i]) == element_length && strncmp(STR_NAME_VAL(opa->vars[i]), element, element_length) == 0) { zval *CV_z = ZEND_CALL_VAR_NUM(xdebug_lib_get_active_data(), i); CV = &CV_z; if (CV) { ZVAL_COPY(&tmp_retval, *CV); goto cleanup; } } i++; } } } free(element); ht = xdebug_lib_get_active_symbol_table(); XDEBUG_BREAK_INTENTIONALLY_MISSING case XF_ST_ARRAY_INDEX_ASSOC: element = prepare_search_key(name, &name_length, "", 0); xdebug_stripcslashes(element, (int *) &name_length); /* Handle "this" in a different way */ if (type == XF_ST_ROOT && strcmp("this", element) == 0) { if (xdebug_lib_has_active_object()) { ZVAL_COPY(&tmp_retval, xdebug_lib_get_active_object()); } else { ZVAL_UNDEF(&tmp_retval); } goto cleanup; } if (ht) { zval *tmp = zend_hash_str_find(ht, element, name_length); if (tmp != NULL) { ZVAL_COPY(&tmp_retval, tmp); goto cleanup; } } break; case XF_ST_ARRAY_INDEX_NUM: element = prepare_search_key(name, &name_length, "", 0); if (ht) { zval *tmp = zend_hash_index_find(ht, strtoull(element, NULL, 10)); if (tmp != NULL) { ZVAL_COPY(&tmp_retval, tmp); goto cleanup; } } break; case XF_ST_ESCAPED_OBJ_PROPERTY: name = xdstrndup(name, name_length); free_duplicated_name = 1; xdebug_stripcslashes(name, (int *) &name_length); XDEBUG_BREAK_INTENTIONALLY_MISSING case XF_ST_OBJ_PROPERTY: /* If we don't have an object, bail out */ if (!value_in || Z_TYPE_P(value_in) != IS_OBJECT) { ZVAL_UNDEF(&tmp_retval); goto cleanup; } /* Let's see if there is a debug handler */ myht = xdebug_objdebug_pp(&value_in, XDEBUG_VAR_OBJDEBUG_DEFAULT); if (myht) { /* As a normal (public) property */ zval *tmp = zend_symtable_str_find(myht, name, name_length); if (tmp != NULL) { #if PHP_VERSION_ID >= 80400 if (Z_TYPE_P(tmp) == IS_PTR) { zend_release_properties(myht); goto skip_for_property_hook; } #endif ZVAL_COPY(&tmp_retval, tmp); zend_release_properties(myht); goto cleanup; } /* As a private property */ { char *unmangled = replace_star_by_null(name, name_length); zval *tmp = zend_symtable_str_find(myht, unmangled, name_length); if (tmp != NULL) { ZVAL_COPY(&tmp_retval, tmp); zend_release_properties(myht); xdfree(unmangled); goto cleanup; } xdfree(unmangled); } zend_release_properties(myht); } #if PHP_VERSION_ID >= 80400 skip_for_property_hook: #endif /* First we try an object handler */ if (cce) { zval *tmp_val; tmp_val = zend_read_property(cce, Z_OBJ_P(value_in), name, name_length, 1, &tmp_retval); if (tmp_val != &tmp_retval && tmp_val != &EG(uninitialized_zval)) { ZVAL_COPY(&tmp_retval, tmp_val); goto cleanup; } if (EG(exception)) { zend_clear_exception(); } } /* Then we try a public property */ element = prepare_search_key(name, &element_length, "", 0); if (ht) { zval *tmp = zend_symtable_str_find(ht, element, element_length); if (tmp != NULL) { ZVAL_COPY(&tmp_retval, tmp); goto cleanup; } } element_length = name_length; /* Then we try it again as protected property */ free(element); element = prepare_search_key(name, &element_length, "*", 1); if (ht) { zval *tmp = zend_hash_str_find(ht, element, element_length); if (tmp != NULL) { ZVAL_COPY(&tmp_retval, tmp); goto cleanup; } } element_length = name_length; /* Then we try it again as private property */ free(element); element = prepare_search_key(name, &element_length, ccn, ccnl); if (ht) { zval *tmp = zend_hash_str_find(ht, element, element_length); if (tmp != NULL) { ZVAL_COPY(&tmp_retval, tmp); goto cleanup; } } element_length = name_length; if (!handle_spl_classes(ccn, ccnl, name, name_length, value_in, &element, &myht, &tmp_retval)) { goto cleanup; } break; } cleanup: if (element) { free(element); } if (free_duplicated_name && name) { xdfree(name); } zval_ptr_dtor_nogc(orig_value_in); ZVAL_COPY_VALUE(orig_value_in, &tmp_retval); } inline static int is_objectish(zval *value) { switch (Z_TYPE_P(value)) { case IS_OBJECT: return 1; case IS_INDIRECT: if (Z_TYPE_P(Z_INDIRECT_P(value)) == IS_OBJECT) { return 1; } break; case IS_REFERENCE: if (Z_TYPE_P(Z_REFVAL_P(value)) == IS_OBJECT) { return 1; } break; } return 0; } void xdebug_get_php_symbol(zval *retval, xdebug_str* name) { int found = -1; int state = 0; char *ptr = name->d; size_t ctr = 0; char *keyword = NULL, *keyword_end = NULL; int type = XF_ST_ROOT; char *current_classname = NULL; zend_class_entry *current_ce = NULL; int cc_length = 0; char quotechar = 0; ZVAL_UNDEF(retval); do { if (ctr == name->l) { found = 0; } else { switch (state) { case 0: if (ptr[ctr] == '$') { keyword = &ptr[ctr] + 1; break; } if (ptr[ctr] == ':') { /* special tricks */ keyword = &ptr[ctr]; state = 7; break; } keyword = &ptr[ctr]; state = 1; XDEBUG_BREAK_INTENTIONALLY_MISSING case 1: if (ptr[ctr] == '[') { keyword_end = &ptr[ctr]; if (keyword) { fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce); if (current_classname) { efree(current_classname); } current_classname = NULL; cc_length = 0; current_ce = NULL; keyword = NULL; } state = 3; } else if (ptr[ctr] == '-') { keyword_end = &ptr[ctr]; if (keyword) { fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce); if (current_classname) { efree(current_classname); } current_classname = NULL; cc_length = 0; current_ce = NULL; if (is_objectish(retval)) { current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce); } keyword = NULL; } state = 2; type = XF_ST_OBJ_PROPERTY; } else if (ptr[ctr] == ':') { keyword_end = &ptr[ctr]; if (keyword) { fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce); if (current_classname) { efree(current_classname); } current_classname = NULL; cc_length = 0; if (is_objectish(retval)) { current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce); } keyword = NULL; } state = 8; type = XF_ST_STATIC_PROPERTY; } break; case 2: if (ptr[ctr] != '>') { if (ptr[ctr] == '{') { state = 11; } else { keyword = &ptr[ctr]; state = 1; } } break; case 8: if (ptr[ctr] != ':') { keyword = &ptr[ctr]; state = 1; } break; case 3: /* Parsing in [...] */ /* Associative arrays */ if (ptr[ctr] == '\'' || ptr[ctr] == '"') { state = 4; keyword = &ptr[ctr] + 1; quotechar = ptr[ctr]; type = XF_ST_ARRAY_INDEX_ASSOC; } /* Numerical index */ if (ptr[ctr] >= '0' && ptr[ctr] <= '9') { cc_length = 0; state = 6; keyword = &ptr[ctr]; type = XF_ST_ARRAY_INDEX_NUM; } /* Numerical index starting with a - */ if (ptr[ctr] == '-') { state = 9; keyword = &ptr[ctr]; } break; case 9: /* Numerical index starting with a - */ if (ptr[ctr] >= '0' && ptr[ctr] <= '9') { state = 6; type = XF_ST_ARRAY_INDEX_NUM; } break; case 4: if (ptr[ctr] == '\\') { state = 10; /* Escaped character */ } else if (ptr[ctr] == quotechar) { quotechar = 0; state = 5; keyword_end = &ptr[ctr]; fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce); if (current_classname) { efree(current_classname); } current_classname = NULL; cc_length = 0; if (is_objectish(retval)) { current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce); } keyword = NULL; } break; case 10: /* Escaped character */ state = 4; break; case 5: if (ptr[ctr] == ']') { state = 1; } break; case 6: if (ptr[ctr] == ']') { state = 1; keyword_end = &ptr[ctr]; fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce); if (current_classname) { efree(current_classname); } current_classname = NULL; cc_length = 0; if (is_objectish(retval)) { current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce); } keyword = NULL; } break; case 7: /* special cases, started with a ":" */ if (ptr[ctr] == ':') { function_stack_entry *active_fse = xdebug_lib_get_active_stack_entry(); state = 1; keyword_end = &ptr[ctr]; if (strncmp(keyword, "::", 2) == 0 && active_fse->function.object_class) { /* static class properties */ zend_class_entry *ce = zend_fetch_class(active_fse->function.object_class, ZEND_FETCH_CLASS_SELF); current_classname = estrdup(STR_NAME_VAL(ce->name)); cc_length = strlen(STR_NAME_VAL(ce->name)); current_ce = ce; keyword = &ptr[ctr] + 1; type = XF_ST_STATIC_ROOT; } else { keyword = NULL; } } break; case 11: if (ptr[ctr] == '\'' || ptr[ctr] == '"') { state = 12; keyword = &ptr[ctr] + 1; quotechar = ptr[ctr]; type = XF_ST_ESCAPED_OBJ_PROPERTY; } break; case 12: /* Inside {" */ if (ptr[ctr] == '\\') { state = 13; /* Escaped character */ } else if (ptr[ctr] == quotechar) { quotechar = 0; state = 14; keyword_end = &ptr[ctr]; fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce); if (current_classname) { efree(current_classname); } current_classname = NULL; cc_length = 0; if (is_objectish(retval)) { current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce); } keyword = NULL; } break; case 13: /* Escaped character */ state = 12; break; case 14: if (ptr[ctr] == '}') { state = 1; } break; } ctr++; } } while (found < 0); if (keyword != NULL) { fetch_zval_from_symbol_table(retval, keyword, &ptr[ctr] - keyword, type, current_classname, cc_length, current_ce); } if (current_classname) { efree(current_classname); } } /* Copied from Zend/zend_objects_API.h and instead of a ZEND_ASSERT it returns NULL */ static zend_property_info *xdebug_get_property_info_for_slot(zend_object *obj, zval *val) { zend_property_info **table = obj->ce->properties_info_table; intptr_t prop_num = val - obj->properties_table; if (prop_num < 0 || prop_num >= obj->ce->default_properties_count) { return NULL; } return table[prop_num]; } static zend_property_info *xdebug_get_typed_property_info_for_slot(zend_object *obj, zval *val) { zend_property_info *prop_info = xdebug_get_property_info_for_slot(obj, val); if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) { return prop_info; } return NULL; } xdebug_str* xdebug_get_property_type(zval* object, zval *val) { xdebug_str *type_str = NULL; zend_property_info *info; if (Z_TYPE_P(val) != IS_INDIRECT) { return NULL; } val = Z_INDIRECT_P(val); info = xdebug_get_typed_property_info_for_slot(Z_OBJ_P(object), val); if (info) { zend_string *type_info = zend_type_to_string(info->type); type_str = xdebug_str_new(); # if PHP_VERSION_ID >= 80100 if (info->flags & ZEND_ACC_READONLY) { xdebug_str_add_literal(type_str, "readonly "); } # endif xdebug_str_add_zstr(type_str, type_info); zend_string_release(type_info); } return type_str; } xdebug_str* xdebug_get_property_info(char *mangled_property, int mangled_len, const char **modifier, char **class_name) { const char *cls_name, *tmp_prop_name; size_t tmp_prop_name_len; xdebug_str *property_name; zend_string *i_mangled = zend_string_init(mangled_property, mangled_len - 1, 0); zend_unmangle_property_name_ex(i_mangled, &cls_name, &tmp_prop_name, &tmp_prop_name_len); property_name = xdebug_str_create((char*) tmp_prop_name, tmp_prop_name_len); *class_name = cls_name ? xdstrdup(cls_name) : NULL; zend_string_release(i_mangled); if (*class_name) { if (*class_name[0] == '*') { *modifier = "protected"; } else { *modifier = "private"; } } else { *modifier = "public"; } return property_name; } #define XDEBUG_MAX_INT 2147483647 xdebug_var_export_options* xdebug_var_export_options_from_ini(void) { xdebug_var_export_options *options; options = xdmalloc(sizeof(xdebug_var_export_options)); options->max_children = XINI_LIB(display_max_children); options->max_data = XINI_LIB(display_max_data); options->max_depth = XINI_LIB(display_max_depth); options->show_hidden = 0; options->show_location = 1; options->extended_properties = 0; options->encode_as_extended_property = 0; if (options->max_children == -1 || options->max_children > XDEBUG_MAX_INT) { options->max_children = XDEBUG_MAX_INT; } else if (options->max_children < 1) { options->max_children = 0; } if (options->max_data == -1 || options->max_data > XDEBUG_MAX_INT) { options->max_data = XDEBUG_MAX_INT; } else if (options->max_data < 1) { options->max_data = 0; } if (options->max_depth == -1 || options->max_depth > 1023) { options->max_depth = 1023; } else if (options->max_depth < 1) { options->max_depth = 0; } options->runtime = (xdebug_var_runtime_page*) xdcalloc((options->max_depth + 1), sizeof(xdebug_var_runtime_page)); options->no_decoration = 0; return options; } xdebug_var_export_options xdebug_var_nolimit_options = { XDEBUG_MAX_INT, XDEBUG_MAX_INT, 1023, 1, 0, 0, 0, NULL, 0 }; xdebug_var_export_options* xdebug_var_get_nolimit_options(void) { return &xdebug_var_nolimit_options; } /***************************************************************************** ** Normal variable printing routines */ #define XDEBUG_VAR_ATTR_TEXT 0 #define XDEBUG_VAR_ATTR_FANCY 1 void xdebug_add_variable_attributes(xdebug_str *str, zval *struc, zend_bool html) { if (html) { xdebug_str_add_literal(str, "("); } else { xdebug_str_add_literal(str, "("); } if (Z_TYPE_P(struc) >= IS_STRING && Z_TYPE_P(struc) != IS_INDIRECT) { if (Z_TYPE_P(struc) == IS_STRING && ZSTR_IS_INTERNED(Z_STR_P(struc))) { xdebug_str_add_literal(str, "interned"); } else if (Z_TYPE_P(struc) == IS_ARRAY && (GC_FLAGS(Z_ARRVAL_P(struc)) & IS_ARRAY_IMMUTABLE)) { xdebug_str_add_literal(str, "immutable"); } else { xdebug_str_add_fmt(str, "refcount=%d", Z_REFCOUNT_P(struc)); } xdebug_str_add_fmt(str, ", is_ref=%d", Z_TYPE_P(struc) == IS_REFERENCE); } else { xdebug_str_add_literal(str, "refcount=0, is_ref=0"); } if (html) { xdebug_str_add_literal(str, ")"); } else { xdebug_str_add_literal(str, ")="); } } /***************************************************************************** ** XML encoding function */ static const char xml_encode_count[256] = { 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; static const char* xml_encode_map[64] = { "�", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, " ", NULL, NULL, " ", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, """, NULL, NULL, NULL, "&", "'", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "<", NULL, ">", NULL, }; char* xdebug_xmlize(char *s_string, size_t len, size_t *newlen) { int i, w_pos; int encoded_string_length = 0; char *new_string; const unsigned char *string = (unsigned char*) s_string; /* Quick bailout for empty strings */ if (!len) { *newlen = 0; return estrdup(""); } /* Calculate new memory requirement */ for (i = 0; i < len; i++) { encoded_string_length += xml_encode_count[string[i]]; } /* No characters need to be encoded, so just duplicate and return */ if (encoded_string_length == len) { *newlen = len; return estrdup(s_string); } new_string = emalloc(encoded_string_length + 1); w_pos = 0; for (i = 0; i < len; i++) { int replacement_length = xml_encode_count[string[i]]; if (replacement_length != 1) { int j; for (j = 0; j < replacement_length; j++) { new_string[w_pos] = xml_encode_map[string[i]][j]; w_pos++; } continue; } new_string[w_pos] = string[i]; w_pos++; } new_string[w_pos] = '\0'; *newlen = encoded_string_length; /* remove one for null byte */ return new_string; } /***************************************************************************** ** Function name printing function */ static char* xdebug_create_doc_link(xdebug_func f) { char *tmp_target = NULL, *p, *retval; switch (f.type) { case XFUNC_NORMAL: { tmp_target = xdebug_sprintf("function.%s", ZSTR_VAL(f.function)); break; } case XFUNC_STATIC_MEMBER: case XFUNC_MEMBER: { if (zend_string_equals_literal(f.function, "__construct")) { tmp_target = xdebug_sprintf("%s.construct", ZSTR_VAL(f.object_class)); } else { tmp_target = xdebug_sprintf("%s.%s", ZSTR_VAL(f.object_class), ZSTR_VAL(f.function)); } break; } } while ((p = strchr(tmp_target, '_')) != NULL) { *p = '-'; } retval = xdebug_sprintf("%s", (PG(docref_root) && PG(docref_root)[0]) ? PG(docref_root) : "http://www.php.net/", tmp_target, PG(docref_ext), ZSTR_VAL(f.function)); xdfree(tmp_target); return retval; } char* xdebug_show_fname(xdebug_func f, int flags) { switch (f.type) { case XFUNC_NORMAL: { if (PG(html_errors) && (flags & XDEBUG_SHOW_FNAME_ALLOW_HTML) && f.internal) { return xdebug_create_doc_link(f); } else { return xdebug_sprintf("%s", ZSTR_VAL(f.function)); } break; } case XFUNC_STATIC_MEMBER: case XFUNC_MEMBER: { if (PG(html_errors) && (flags & XDEBUG_SHOW_FNAME_ALLOW_HTML) && f.internal) { return xdebug_create_doc_link(f); } else { if (f.scope_class && !(flags & XDEBUG_SHOW_FNAME_IGNORE_SCOPE)) { return xdebug_sprintf("%s%s%s", ZSTR_VAL(f.scope_class), f.type == XFUNC_STATIC_MEMBER ? "::" : "->", f.function ? ZSTR_VAL(f.function) : "?" ); } return xdebug_sprintf("%s%s%s", f.object_class ? ZSTR_VAL(f.object_class) : "?", f.type == XFUNC_STATIC_MEMBER ? "::" : "->", f.function ? ZSTR_VAL(f.function) : "?" ); } break; } case XFUNC_EVAL: return xdstrdup("eval"); break; case XFUNC_INCLUDE: return (flags & XDEBUG_SHOW_FNAME_ADD_FILE_NAME) ? xdebug_sprintf("{include:%s}", ZSTR_VAL(f.include_filename)) : xdstrdup("include"); break; case XFUNC_INCLUDE_ONCE: return (flags & XDEBUG_SHOW_FNAME_ADD_FILE_NAME) ? xdebug_sprintf("{include_once:%s}", ZSTR_VAL(f.include_filename)) : xdstrdup("include_once"); break; case XFUNC_REQUIRE: return (flags & XDEBUG_SHOW_FNAME_ADD_FILE_NAME) ? xdebug_sprintf("{require:%s}", ZSTR_VAL(f.include_filename)) : xdstrdup("require"); break; case XFUNC_REQUIRE_ONCE: return (flags & XDEBUG_SHOW_FNAME_ADD_FILE_NAME) ? xdebug_sprintf("{require_once:%s}", ZSTR_VAL(f.include_filename)) : xdstrdup("require_once"); break; case XFUNC_MAIN: return xdstrdup("{main}"); break; case XFUNC_ZEND_PASS: return xdstrdup("{zend_pass}"); break; #if PHP_VERSION_ID >= 80100 case XFUNC_FIBER: return xdebug_sprintf("%s", ZSTR_VAL(f.function)); break; #endif default: return xdstrdup("{unknown}"); } } xdebug-3.4.3/src/lib/var.h0000664000175000017500000000634415011062311014560 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "php_xdebug.h" #include "lib/compat.h" #include "lib/lib.h" #include "lib/str.h" #include "lib/xml.h" #ifndef __HAVE_XDEBUG_VAR_H__ #define __HAVE_XDEBUG_VAR_H__ /* Set correct int format to use */ #include "Zend/zend_long.h" #if SIZEOF_ZEND_LONG == 4 # define XDEBUG_INT_FMT "%ld" #else # define XDEBUG_INT_FMT "%lld" #endif typedef struct { int page; /* The number of the page to retrieve */ int current_element_nr; int start_element_nr; int end_element_nr; } xdebug_var_runtime_page; typedef struct xdebug_var_export_options { int max_children; int max_data; int max_depth; int show_hidden; int extended_properties; /* Whether the feature is enabled */ int encode_as_extended_property; /* Whether the current node's elements need to be encoded */ int show_location; xdebug_var_runtime_page *runtime; int no_decoration; } xdebug_var_export_options; #define XDEBUG_VAR_TYPE_NORMAL 0x00 #define XDEBUG_VAR_TYPE_STATIC 0x01 #define XDEBUG_VAR_TYPE_CONSTANT 0x02 void xdebug_get_php_symbol(zval *retval, xdebug_str* name); xdebug_var_export_options* xdebug_var_export_options_from_ini(void); xdebug_var_export_options* xdebug_var_get_nolimit_options(void); xdebug_str* xdebug_get_property_type(zval* object, zval *val); xdebug_str* xdebug_get_property_info(char *mangled_property, int mangled_len, const char **modifier, char **class_name); #define XDEBUG_VAR_OBJDEBUG_DEFAULT 0x00 #define XDEBUG_VAR_OBJDEBUG_USE_DEBUGINFO 0x01 HashTable *xdebug_objdebug_pp(zval **zval_pp, int flags); #define XDEBUG_VAR_ATTR_TEXT 0 #define XDEBUG_VAR_ATTR_HTML 1 void xdebug_add_variable_attributes(xdebug_str *str, zval *struc, zend_bool fancy); char* xdebug_xmlize(char *s_string, size_t len, size_t *newlen); char* xdebug_error_type_simple(int type); char* xdebug_error_type(int type); zval *xdebug_get_zval(zend_execute_data *zdata, int node_type, const znode_op *node); zval *xdebug_get_zval_with_opline(zend_execute_data *zdata, const zend_op *opline, int node_type, const znode_op *node); #define XDEBUG_SHOW_FNAME_DEFAULT 0 #define XDEBUG_SHOW_FNAME_ALLOW_HTML 1<<1 #define XDEBUG_SHOW_FNAME_IGNORE_SCOPE 1<<2 #define XDEBUG_SHOW_FNAME_ADD_FILE_NAME 1<<3 char* xdebug_show_fname(xdebug_func t, int flags); #endif xdebug-3.4.3/src/lib/var_export_html.c0000664000175000017500000003636415011062311017205 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "var_export_html.h" #include "lib_private.h" #include "Zend/zend_closures.h" #if PHP_VERSION_ID >= 80100 # include "zend_enum.h" #endif ZEND_EXTERN_MODULE_GLOBALS(xdebug) /***************************************************************************** ** Fancy variable printing routines */ #define COLOR_POINTER "#888a85" #define COLOR_BOOL "#75507b" #define COLOR_LONG "#4e9a06" #define COLOR_NULL "#3465a4" #define COLOR_DOUBLE "#f57900" #define COLOR_STRING "#cc0000" #define COLOR_EMPTY "#888a85" #define COLOR_ARRAY "#ce5c00" #define COLOR_OBJECT "#8f5902" #define COLOR_RESOURCE "#2e3436" static int xdebug_array_element_export_html(zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, int level, xdebug_str *str, int debug_zval, xdebug_var_export_options *options) { zval **zv = &zv_nptr; size_t newlen; char *tmp_str; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "%*s", (level * 4) - 2, ""); if (HASH_KEY_IS_NUMERIC(hash_key)) { /* numeric key */ xdebug_str_add_fmt(str, XDEBUG_INT_FMT " => ", index_key, COLOR_POINTER); } else { /* string key */ xdebug_str_addc(str, '\''); tmp_str = xdebug_xmlize((char*) HASH_APPLY_KEY_VAL(hash_key), HASH_APPLY_KEY_LEN(hash_key) - 1, &newlen); xdebug_str_addl(str, tmp_str, newlen, 0); efree(tmp_str); xdebug_str_add_fmt(str, "' => ", COLOR_POINTER); } xdebug_var_export_html(zv, str, level + 1, debug_zval, options); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "%*s", (level * 4) - 2, ""); xdebug_str_add_literal(str, "more elements...\n"); } options->runtime[level].current_element_nr++; return 0; } static int xdebug_object_element_export_html(zval *object, zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, int level, xdebug_str *str, int debug_zval, xdebug_var_export_options *options, char *class_name) { zval **zv = &zv_nptr; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "%*s", (level * 4) - 2, ""); if (!HASH_KEY_IS_NUMERIC(hash_key)) { char *prop_class_name; xdebug_str *property_name; xdebug_str *property_type = NULL; const char *modifier; property_type = xdebug_get_property_type(object, zv_nptr); property_name = xdebug_get_property_info((char*) HASH_APPLY_KEY_VAL(hash_key), HASH_APPLY_KEY_LEN(hash_key), &modifier, &prop_class_name); xdebug_str_add_fmt(str, "%s ", modifier); if (property_type) { xdebug_str_add_fmt(str, "%s ", property_type->d); } xdebug_str_addc(str, '\''); xdebug_str_add_str(str, property_name); if (strcmp(modifier, "private") != 0 || strcmp(class_name, prop_class_name) == 0) { xdebug_str_add_fmt(str, "' => ", COLOR_POINTER); } else { xdebug_str_add_fmt(str, "' (%s) => ", prop_class_name, COLOR_POINTER); } if (property_type) { xdebug_str_free(property_type); } xdebug_str_free(property_name); xdfree(prop_class_name); } else { xdebug_str_add_fmt(str, "public " XDEBUG_INT_FMT " => ", index_key, COLOR_POINTER); } xdebug_var_export_html(zv, str, level + 1, debug_zval, options); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "%*s", (level * 4) - 2, ""); xdebug_str_add_literal(str, "more elements...\n"); } options->runtime[level].current_element_nr++; return 0; } static void handle_closure(xdebug_str *str, zval *obj, int level) { const zend_function *closure_function; if (Z_TYPE_P(obj) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(obj), zend_ce_closure)) { return; } closure_function = zend_get_closure_method_def(Z_OBJ_P(obj)); xdebug_str_add_fmt(str, "%*svirtual 'closure' '", (level * 4) - 2, "", COLOR_STRING); if (closure_function->common.scope) { if (closure_function->common.fn_flags & ZEND_ACC_STATIC) { xdebug_str_add_zstr(str, closure_function->common.scope->name); xdebug_str_add_literal(str, "::"); } else { xdebug_str_add_literal(str, "$this->"); } } xdebug_str_add_zstr(str, closure_function->common.function_name); xdebug_str_add_literal(str, "'\n"); } void xdebug_var_export_html(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options) { HashTable *myht; char* tmp_str; size_t newlen; zend_ulong num; zend_string *key; zval *val; zval *tmpz; int z_type; z_type = Z_TYPE_P(*struc); if (debug_zval) { xdebug_add_variable_attributes(str, *struc, XDEBUG_VAR_ATTR_HTML); } if (z_type == IS_INDIRECT) { tmpz = Z_INDIRECT_P(*struc); struc = &tmpz; z_type = Z_TYPE_P(*struc); } if (z_type == IS_REFERENCE) { tmpz = &((*struc)->value.ref->val); struc = &tmpz; z_type = Z_TYPE_P(*struc); } switch (z_type) { case IS_TRUE: xdebug_str_add_fmt(str, "boolean true", COLOR_BOOL); break; case IS_FALSE: xdebug_str_add_fmt(str, "boolean false", COLOR_BOOL); break; case IS_NULL: xdebug_str_add_fmt(str, "null", COLOR_NULL); break; case IS_LONG: xdebug_str_add_fmt(str, "int " XDEBUG_INT_FMT "", COLOR_LONG, Z_LVAL_P(*struc)); break; case IS_DOUBLE: xdebug_str_add_fmt(str, "float %.*H", COLOR_DOUBLE, (int) PG(serialize_precision), Z_DVAL_P(*struc)); break; case IS_STRING: xdebug_str_add_fmt(str, "string '", COLOR_STRING); if ((size_t) Z_STRLEN_P(*struc) > (size_t) options->max_data) { tmp_str = xdebug_xmlize(Z_STRVAL_P(*struc), options->max_data, &newlen); xdebug_str_addl(str, tmp_str, newlen, 0); efree(tmp_str); xdebug_str_add_literal(str, "'..."); } else { tmp_str = xdebug_xmlize(Z_STRVAL_P(*struc), Z_STRLEN_P(*struc), &newlen); xdebug_str_addl(str, tmp_str, newlen, 0); efree(tmp_str); xdebug_str_add_literal(str, "'"); } xdebug_str_add_fmt(str, " (length=%d)", Z_STRLEN_P(*struc)); break; case IS_ARRAY: myht = Z_ARRVAL_P(*struc); xdebug_str_add_fmt(str, "\n%*s", (level - 1) * 4, ""); if (!xdebug_zend_hash_is_recursive(myht)) { xdebug_str_add_fmt(str, "array (size=%d)\n", myht->nNumOfElements); if (level <= options->max_depth) { if (myht->nNumOfElements) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) { xdebug_array_element_export_html(val, num, key, level, str, debug_zval, options); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); } else { xdebug_str_add_fmt(str, "%*s", (level * 4) - 2, ""); xdebug_str_add_fmt(str, "empty\n", COLOR_EMPTY); } } else if (myht->nNumOfElements > 0) { xdebug_str_add_fmt(str, "%*s...\n", (level * 4) - 2, ""); } } else { xdebug_str_add_literal(str, "&array\n"); } break; case IS_OBJECT: { #if PHP_VERSION_ID >= 80100 zend_class_entry *ce = Z_OBJCE_P(*struc); if (ce->ce_flags & ZEND_ACC_ENUM) { zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(*struc)); xdebug_str_add_fmt( str, "enum(%s::%s)", ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval) ); if (ce->enum_backing_type != IS_UNDEF) { zval *value = zend_enum_fetch_case_value(Z_OBJ_P(*struc)); if (ce->enum_backing_type == IS_LONG) { xdebug_str_add_fmt( str, " : int " XDEBUG_INT_FMT "", COLOR_LONG, Z_LVAL_P(value) ); } if (ce->enum_backing_type == IS_STRING) { xdebug_str_add_fmt( str, " : string '%s' (length=%d)", COLOR_STRING, Z_STRVAL_P(value), Z_STRLEN_P(value) ); } } xdebug_str_addc(str, '\n'); break; } #endif myht = xdebug_objdebug_pp(struc, XDEBUG_VAR_OBJDEBUG_USE_DEBUGINFO); xdebug_str_add_fmt(str, "\n%*s", (level - 1) * 4, ""); if (!myht || !xdebug_zend_hash_is_recursive(myht)) { xdebug_str_add_literal(str, "object("); xdebug_str_add(str, ZSTR_VAL(Z_OBJCE_P(*struc)->name), 0); xdebug_str_add_literal(str, ")"); xdebug_str_add_fmt(str, "[%d]\n", Z_OBJ_HANDLE_P(*struc)); handle_closure(str, *struc, level); if (myht && (level <= options->max_depth)) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) { xdebug_object_element_export_html(*struc, val, num, key, level, str, debug_zval, options, ZSTR_VAL(Z_OBJCE_P(*struc)->name)); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); } else if (myht && myht->nNumOfElements > 0) { xdebug_str_add_fmt(str, "%*s...\n", (level * 4) - 2, ""); } } else { xdebug_str_add_literal(str, "&object("); xdebug_str_add(str, ZSTR_VAL(Z_OBJCE_P(*struc)->name), 0); xdebug_str_add_literal(str, ")"); xdebug_str_add_fmt(str, "[%d]\n", Z_OBJ_HANDLE_P(*struc)); } zend_release_properties(myht); break; } case IS_RESOURCE: { char *type_name; type_name = (char *) zend_rsrc_list_get_rsrc_type(Z_RES_P(*struc)); xdebug_str_add_fmt(str, "resource(%ld, %s)", Z_RES_P(*struc)->handle, COLOR_RESOURCE, type_name ? type_name : "Unknown"); break; } case IS_UNDEF: xdebug_str_add_fmt(str, "*uninitialized*", COLOR_NULL); break; default: xdebug_str_add_fmt(str, "NFC", COLOR_NULL); break; } if (z_type != IS_ARRAY && z_type != IS_OBJECT) { xdebug_str_addc(str, '\n'); } } xdebug_str* xdebug_get_zval_value_html(char *name, zval *val, int debug_zval, xdebug_var_export_options *options) { xdebug_str *str = xdebug_str_new(); int default_options = 0; if (!options) { options = xdebug_var_export_options_from_ini(); default_options = 1; } xdebug_str_add_literal(str, "
");
	if (options->show_location && !debug_zval) {
		char *formatted_filename;
		xdebug_format_filename(&formatted_filename, "%f", zend_get_executed_filename_ex());

		if (strlen(XINI_LIB(file_link_format)) > 0 && strcmp(zend_get_executed_filename(), "Unknown") != 0) {
			char *file_link;

			xdebug_format_file_link(&file_link, zend_get_executed_filename(), zend_get_executed_lineno());
			xdebug_str_add_fmt(str, "\n%s:%d:", file_link, formatted_filename, zend_get_executed_lineno());
			xdfree(file_link);
		} else {
			xdebug_str_add_fmt(str, "\n%s:%d:", formatted_filename, zend_get_executed_lineno());
		}

		xdfree(formatted_filename);
	}
	xdebug_var_export_html(&val, str, 1, debug_zval, options);
	xdebug_str_add_literal(str, "
"); if (default_options) { xdfree(options->runtime); xdfree(options); } return str; } static void xdebug_var_synopsis_html(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options) { HashTable *myht; zval *tmpz; int z_type; z_type = Z_TYPE_P(*struc); if (debug_zval) { xdebug_add_variable_attributes(str, *struc, XDEBUG_VAR_ATTR_HTML); } if (z_type == IS_REFERENCE) { tmpz = &((*struc)->value.ref->val); struc = &tmpz; } switch (z_type) { case IS_TRUE: xdebug_str_add_fmt(str, "true", COLOR_BOOL); break; case IS_FALSE: xdebug_str_add_fmt(str, "false", COLOR_BOOL); break; case IS_NULL: xdebug_str_add_fmt(str, "null", COLOR_NULL); break; case IS_LONG: xdebug_str_add_fmt(str, "long", COLOR_LONG); break; case IS_DOUBLE: xdebug_str_add_fmt(str, "double", COLOR_DOUBLE); break; case IS_STRING: xdebug_str_add_fmt(str, "string(%d)", COLOR_STRING, Z_STRLEN_P(*struc)); break; case IS_ARRAY: myht = Z_ARRVAL_P(*struc); xdebug_str_add_fmt(str, "array(%d)", COLOR_ARRAY, myht->nNumOfElements); break; case IS_OBJECT: { #if PHP_VERSION_ID >= 80100 zend_class_entry *ce = Z_OBJCE_P(*struc); if (ce->ce_flags & ZEND_ACC_ENUM) { zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(*struc)); xdebug_str_add_fmt( str, "enum(%s::%s)", COLOR_OBJECT, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval)); break; } #endif xdebug_str_add_fmt(str, "object(%s)[%d]", COLOR_OBJECT, ZSTR_VAL(Z_OBJCE_P(*struc)->name), Z_OBJ_HANDLE_P(*struc)); } break; case IS_RESOURCE: { char *type_name; type_name = (char *) zend_rsrc_list_get_rsrc_type(Z_RES_P(*struc)); xdebug_str_add_fmt(str, "resource(%ld, %s)", COLOR_RESOURCE, Z_RES_P(*struc)->handle, type_name ? type_name : "Unknown"); break; } case IS_UNDEF: xdebug_str_add_fmt(str, "*uninitialized*", COLOR_NULL); break; default: xdebug_str_add_fmt(str, "NFC", COLOR_NULL); break; } } xdebug_str* xdebug_get_zval_synopsis_html(const char *name, zval *val, int debug_zval, xdebug_var_export_options *options) { xdebug_str *str = xdebug_str_new(); int default_options = 0; if (!options) { options = xdebug_var_export_options_from_ini(); default_options = 1; } xdebug_var_synopsis_html(&val, str, 1, debug_zval, options); if (default_options) { xdfree(options->runtime); xdfree(options); } return str; } xdebug-3.4.3/src/lib/var_export_html.h0000664000175000017500000000270515011062311017202 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_LIB_VAR_EXPORT_HTML_H__ #define __HAVE_XDEBUG_LIB_VAR_EXPORT_HTML_H__ #include "var.h" void xdebug_var_export_html(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options); xdebug_str* xdebug_get_zval_value_html(char *name, zval *val, int debug_zval, xdebug_var_export_options *options); xdebug_str* xdebug_get_zval_synopsis_html(const char *name, zval *val, int debug_zval, xdebug_var_export_options *options); #endif xdebug-3.4.3/src/lib/var_export_line.c0000664000175000017500000003044415011062311017161 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "Zend/zend_closures.h" #if PHP_VERSION_ID >= 80100 # include "zend_enum.h" #endif #include "var_export_line.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) static int xdebug_array_element_export(zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, int level, xdebug_str *str, int debug_zval, xdebug_var_export_options *options) { zval **zv = &zv_nptr; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { if (HASH_KEY_IS_NUMERIC(hash_key)) { /* numeric key */ xdebug_str_add_fmt(str, XDEBUG_INT_FMT " => ", index_key); } else { /* string key */ zend_string *tmp_zstr; tmp_zstr = php_addcslashes(hash_key, (char*) "'\\\0..\37", 7); xdebug_str_addc(str, '\''); xdebug_str_add_zstr(str, tmp_zstr); xdebug_str_add_literal(str, "' => "); zend_string_release(tmp_zstr); } xdebug_var_export_line(zv, str, level + 2, debug_zval, options); xdebug_str_add_literal(str, ", "); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_add_literal(str, "..., "); } options->runtime[level].current_element_nr++; return 0; } static int xdebug_object_element_export(zval *object, zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, int level, xdebug_str *str, int debug_zval, xdebug_var_export_options *options, char *class_name) { zval **zv = &zv_nptr; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { if (!HASH_KEY_IS_NUMERIC(hash_key)) { xdebug_str *property_name; xdebug_str *property_type = NULL; char *prop_class_name; const char *modifier; property_type = xdebug_get_property_type(object, zv_nptr); property_name = xdebug_get_property_info((char*) HASH_APPLY_KEY_VAL(hash_key), HASH_APPLY_KEY_LEN(hash_key), &modifier, &prop_class_name); xdebug_str_add(str, modifier, 0); if (property_type) { xdebug_str_addc(str, ' '); xdebug_str_add_str(str, property_type); } xdebug_str_add_literal(str, " $"); if (strcmp(modifier, "private") != 0 || strcmp(class_name, prop_class_name) == 0) { xdebug_str_add_str(str, property_name); xdebug_str_add_literal(str, " = "); } else { xdebug_str_addc(str, '{'); xdebug_str_add(str, prop_class_name, 0); xdebug_str_addc(str, '}'); xdebug_str_add_str(str, property_name); xdebug_str_add_literal(str, " = "); } if (property_type) { xdebug_str_free(property_type); } xdebug_str_free(property_name); xdfree(prop_class_name); } else { xdebug_str_add_fmt(str, "public $%d = ", index_key); } xdebug_var_export_line(zv, str, level + 2, debug_zval, options); xdebug_str_add_literal(str, "; "); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_add_literal(str, "...; "); } options->runtime[level].current_element_nr++; return 0; } #if PHP_VERSION_ID < 80200 static void handle_closure(xdebug_str *str, zval *obj) { const zend_function *closure_function; if (Z_TYPE_P(obj) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(obj), zend_ce_closure)) { return; } closure_function = zend_get_closure_method_def(Z_OBJ_P(obj)); xdebug_str_add_literal(str, "virtual $closure = \""); if (closure_function->common.scope) { if (closure_function->common.fn_flags & ZEND_ACC_STATIC) { xdebug_str_add(str, ZSTR_VAL(closure_function->common.scope->name), 0); xdebug_str_add_literal(str, "::"); } else { xdebug_str_add_literal(str, "$this->"); } } xdebug_str_add_zstr(str, closure_function->common.function_name); xdebug_str_add_literal(str, "\", "); } #endif void xdebug_var_export_line(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options) { HashTable *myht; zend_ulong num; zend_string *key; zval *val; zval *tmpz; int z_type; if (!struc || !(*struc)) { return; } z_type = Z_TYPE_P(*struc); if (debug_zval) { xdebug_add_variable_attributes(str, *struc, XDEBUG_VAR_ATTR_TEXT); } if (z_type == IS_INDIRECT) { tmpz = Z_INDIRECT_P(*struc); struc = &tmpz; z_type = Z_TYPE_P(*struc); } if (z_type == IS_REFERENCE) { tmpz = &((*struc)->value.ref->val); struc = &tmpz; z_type = Z_TYPE_P(*struc); } switch (z_type) { case IS_TRUE: xdebug_str_add_literal(str, "TRUE"); break; case IS_FALSE: xdebug_str_add_literal(str, "FALSE"); break; case IS_NULL: xdebug_str_add_literal(str, "NULL"); break; case IS_LONG: xdebug_str_add_fmt(str, XDEBUG_INT_FMT, Z_LVAL_P(*struc)); break; case IS_DOUBLE: xdebug_str_add_fmt(str, "%.*H", (int) PG(serialize_precision), Z_DVAL_P(*struc)); break; case IS_STRING: { if (options->no_decoration) { xdebug_str_add_zstr(str, Z_STR_P(*struc)); } else if ((size_t) Z_STRLEN_P(*struc) <= (size_t) options->max_data) { zend_string *tmp_zstr; tmp_zstr = php_addcslashes(Z_STR_P(*struc), (char*) "'\\\0..\37", 7); xdebug_str_addc(str, '\''); xdebug_str_add_zstr(str, tmp_zstr); xdebug_str_addc(str, '\''); zend_string_release(tmp_zstr); } else { zend_string *tmp_zstr = php_addcslashes_str(ZSTR_VAL(Z_STR_P(*struc)), options->max_data, (char*) "'\\\0..\37", 7); xdebug_str_addc(str, '\''); xdebug_str_add_zstr(str, tmp_zstr); xdebug_str_add_literal(str, "\'..."); zend_string_release(tmp_zstr); } } break; case IS_ARRAY: myht = Z_ARRVAL_P(*struc); if (!xdebug_zend_hash_is_recursive(myht)) { if (debug_zval) { xdebug_str_add_literal(str, "array ("); } else { xdebug_str_addc(str, '['); } if (level <= options->max_depth) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) { xdebug_array_element_export(val, num, key, level, str, debug_zval, options); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); /* Remove the ", " at the end of the string */ if (myht->nNumOfElements > 0) { xdebug_str_chop(str, 2); } } else { xdebug_str_add_literal(str, "..."); } xdebug_str_addc(str, debug_zval ? ')' : ']'); } else { xdebug_str_add_literal(str, "..."); } break; case IS_OBJECT: { #if PHP_VERSION_ID >= 80100 zend_class_entry *ce = Z_OBJCE_P(*struc); if (ce->ce_flags & ZEND_ACC_ENUM) { zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(*struc)); xdebug_str_add_fmt(str, "enum %s::%s", ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval)); if (ce->enum_backing_type != IS_UNDEF) { zval *value = zend_enum_fetch_case_value(Z_OBJ_P(*struc)); if (ce->enum_backing_type == IS_LONG) { xdebug_str_add_fmt(str, "(" XDEBUG_INT_FMT ")", Z_LVAL_P(value)); } if (ce->enum_backing_type == IS_STRING) { zend_string *tmp_zstr = php_addcslashes(Z_STR_P(value), (char*) "'\\\0..\37", 7); xdebug_str_add_literal(str, "('"); xdebug_str_add_zstr(str, tmp_zstr); xdebug_str_add_literal(str, "')"); zend_string_release(tmp_zstr); } } break; } #endif myht = xdebug_objdebug_pp(struc, XDEBUG_VAR_OBJDEBUG_USE_DEBUGINFO); if (!myht || !xdebug_zend_hash_is_recursive(myht)) { xdebug_str_add_literal(str, "class "); xdebug_str_add(str, ZSTR_VAL(Z_OBJCE_P(*struc)->name), 0); xdebug_str_add_literal(str, " { "); #if PHP_VERSION_ID < 80200 handle_closure(str, *struc); #endif if (myht && (level <= options->max_depth)) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) { xdebug_object_element_export(*struc, val, num, key, level, str, debug_zval, options, ZSTR_VAL(Z_OBJCE_P(*struc)->name)); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); } else { xdebug_str_add_literal(str, "..."); } /* Remove the ", " and "; " at the end of the string */ if ( str->l > 2 && ((str->d[str->l - 2] == ',') || (str->d[str->l - 2] == ';' )) && (str->d[str->l - 1] == ' ') ) { xdebug_str_chop(str, 2); } xdebug_str_add_literal(str, " }"); } else { xdebug_str_add_literal(str, "..."); } zend_release_properties(myht); break; } case IS_RESOURCE: { char *type_name; type_name = (char *) zend_rsrc_list_get_rsrc_type(Z_RES_P(*struc)); xdebug_str_add_fmt(str, "resource(%ld) of type (%s)", Z_RES_P(*struc)->handle, type_name ? type_name : "Unknown"); break; } case IS_UNDEF: xdebug_str_add_literal(str, "*uninitialized*"); break; default: xdebug_str_add_literal(str, "NFC"); break; } } xdebug_str* xdebug_get_zval_value_line(zval *val, int debug_zval, xdebug_var_export_options *options) { xdebug_str *str = xdebug_str_new(); int default_options = 0; if (!options) { options = xdebug_var_export_options_from_ini(); default_options = 1; } xdebug_var_export_line(&val, str, 1, debug_zval, options); if (default_options) { xdfree(options->runtime); xdfree(options); } return str; } static void xdebug_var_synopsis(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options) { HashTable *myht; zval *tmpz; if (!struc || !(*struc)) { return; } if (debug_zval) { xdebug_add_variable_attributes(str, *struc, XDEBUG_VAR_ATTR_TEXT); } if (Z_TYPE_P(*struc) == IS_REFERENCE) { tmpz = &((*struc)->value.ref->val); struc = &tmpz; } switch (Z_TYPE_P(*struc)) { case IS_TRUE: xdebug_str_add_literal(str, "true"); break; case IS_FALSE: xdebug_str_add_literal(str, "false"); break; case IS_NULL: xdebug_str_add_literal(str, "null"); break; case IS_LONG: xdebug_str_add_literal(str, "long"); break; case IS_DOUBLE: xdebug_str_add_literal(str, "double"); break; case IS_STRING: xdebug_str_add_fmt(str, "string(%d)", Z_STRLEN_P(*struc)); break; case IS_ARRAY: myht = Z_ARRVAL_P(*struc); xdebug_str_add_fmt(str, "array(%d)", myht->nNumOfElements); break; case IS_OBJECT: { xdebug_str_add_literal(str, "class "); xdebug_str_add(str, ZSTR_VAL(Z_OBJCE_P(*struc)->name), 0); break; } case IS_RESOURCE: { char *type_name; type_name = (char *) zend_rsrc_list_get_rsrc_type(Z_RES_P(*struc)); xdebug_str_add_fmt(str, "resource(%ld) of type (%s)", Z_RES_P(*struc)->handle, type_name ? type_name : "Unknown"); break; } case IS_UNDEF: xdebug_str_add_literal(str, "*uninitialized*"); break; default: xdebug_str_add_literal(str, "NFC"); break; } } xdebug_str* xdebug_get_zval_synopsis_line(zval *val, int debug_zval, xdebug_var_export_options *options) { xdebug_str *str = xdebug_str_new(); int default_options = 0; if (!options) { options = xdebug_var_export_options_from_ini(); default_options = 1; } xdebug_var_synopsis(&val, str, 1, debug_zval, options); if (default_options) { xdfree(options->runtime); xdfree(options); } return str; } xdebug-3.4.3/src/lib/var_export_line.h0000664000175000017500000000264715011062311017172 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_LIB_VAR_EXPORT_LINE_H__ #define __HAVE_XDEBUG_LIB_VAR_EXPORT_LINE_H__ #include "var.h" void xdebug_var_export_line(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options); xdebug_str* xdebug_get_zval_value_line(zval *val, int debug_zval, xdebug_var_export_options *options); xdebug_str* xdebug_get_zval_synopsis_line(zval *val, int debug_zval, xdebug_var_export_options *options); #endif xdebug-3.4.3/src/lib/var_export_text.c0000664000175000017500000004141415011062311017215 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2024 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "Zend/zend_closures.h" #if PHP_VERSION_ID >= 80100 # include "zend_enum.h" #endif #include "php_xdebug.h" #include "var_export_text.h" static void xdebug_var_export_text_ansi(zval **struc, xdebug_str *str, int mode, int level, int debug_zval, xdebug_var_export_options *options); #define xdebug_var_export_text(struc, str, level, debug_zval, options) xdebug_var_export_text_ansi(struc, str, 0, level, debug_zval, options); #define xdebug_var_export_ansi(struc, str, level, debug_zval, options) xdebug_var_export_text_ansi(struc, str, 1, level, debug_zval, options); ZEND_EXTERN_MODULE_GLOBALS(xdebug) /***************************************************************************** ** Plain text/ANSI coloured variable printing routines */ #define ANSI_COLOR_POINTER (mode == 1 ? "" : "") #define ANSI_COLOR_BOOL (mode == 1 ? "" : "") #define ANSI_COLOR_LONG (mode == 1 ? "" : "") #define ANSI_COLOR_NULL (mode == 1 ? "" : "") #define ANSI_COLOR_DOUBLE (mode == 1 ? "" : "") #define ANSI_COLOR_STRING (mode == 1 ? "" : "") #define ANSI_COLOR_EMPTY (mode == 1 ? "" : "") #define ANSI_COLOR_ARRAY (mode == 1 ? "" : "") #define ANSI_COLOR_OBJECT (mode == 1 ? "" : "") #define ANSI_COLOR_RESOURCE (mode == 1 ? "" : "") #define ANSI_COLOR_MODIFIER (mode == 1 ? "" : "") #define ANSI_COLOR_RESET (mode == 1 ? "" : "") #define ANSI_COLOR_BOLD (mode == 1 ? "" : "") #define ANSI_COLOR_BOLD_OFF (mode == 1 ? "" : "") static int xdebug_array_element_export_text_ansi(zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, int level, int mode, xdebug_str *str, int debug_zval, xdebug_var_export_options *options) { zval **zv = &zv_nptr; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "%*s", (level * 2), ""); if (HASH_KEY_IS_NUMERIC(hash_key)) { /* numeric key */ xdebug_str_add_fmt(str, "[" XDEBUG_INT_FMT "] %s=>%s\n", index_key, ANSI_COLOR_POINTER, ANSI_COLOR_RESET); } else { /* string key */ zend_string *tmp, *tmp2; tmp = php_str_to_str(ZSTR_VAL(hash_key), ZSTR_LEN(hash_key), (char*) "'", 1, (char*) "\\'", 2); tmp2 = php_str_to_str(ZSTR_VAL(tmp), ZSTR_LEN(tmp), (char*) "\0", 1, (char*) "\\0", 2); if (tmp) { zend_string_release(tmp); } xdebug_str_addc(str, '\''); if (tmp2) { xdebug_str_add_zstr(str, tmp2); zend_string_release(tmp2); } xdebug_str_add_literal(str, "' =>\n"); } xdebug_var_export_text_ansi(zv, str, mode, level + 1, debug_zval, options); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "\n%*s(more elements)...\n", (level * 2), ""); } options->runtime[level].current_element_nr++; return 0; } static int xdebug_object_element_export_text_ansi(zval *object, zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, int level, int mode, xdebug_str *str, int debug_zval, xdebug_var_export_options *options) { zval **zv = &zv_nptr; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "%*s", (level * 2), ""); if (!HASH_KEY_IS_NUMERIC(hash_key)) { char *class_name; xdebug_str *property_name; const char *modifier; xdebug_str *property_type = NULL; property_type = xdebug_get_property_type(object, zv_nptr); property_name = xdebug_get_property_info((char*) HASH_APPLY_KEY_VAL(hash_key), HASH_APPLY_KEY_LEN(hash_key), &modifier, &class_name); xdebug_str_add_fmt( str, "%s%s%s%s%s%s%s $", ANSI_COLOR_MODIFIER, ANSI_COLOR_BOLD, modifier, ANSI_COLOR_BOLD_OFF, property_type ? " " : "", property_type ? property_type->d : "", ANSI_COLOR_RESET ); xdebug_str_add_str(str, property_name); xdebug_str_add_fmt(str, " %s=>%s\n", ANSI_COLOR_POINTER, ANSI_COLOR_RESET); if (property_type) { xdebug_str_free(property_type); } xdebug_str_free(property_name); xdfree(class_name); } else { xdebug_str_add_fmt(str, "%s%spublic%s%s ${" XDEBUG_INT_FMT "} %s=>%s\n", ANSI_COLOR_MODIFIER, ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_RESET, index_key, ANSI_COLOR_POINTER, ANSI_COLOR_RESET ); } xdebug_var_export_text_ansi(zv, str, mode, level + 1, debug_zval, options); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_add_fmt(str, "\n%*s(more elements)...\n", (level * 2), ""); } options->runtime[level].current_element_nr++; return 0; } #if PHP_VERSION_ID < 80200 static void handle_closure(xdebug_str *str, zval *obj, int level, int mode) { const zend_function *closure_function; if (Z_TYPE_P(obj) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(obj), zend_ce_closure)) { return; } closure_function = zend_get_closure_method_def(Z_OBJ_P(obj)); xdebug_str_add_fmt( str, "%*s%s%svirtual%s $closure =>\n%*s\"", (level * 4) - 2, "", ANSI_COLOR_MODIFIER, ANSI_COLOR_BOLD, ANSI_COLOR_RESET, (level * 4) - 2, "" ); if (closure_function->common.scope) { if (closure_function->common.fn_flags & ZEND_ACC_STATIC) { xdebug_str_add_fmt(str, "%s", ANSI_COLOR_OBJECT); xdebug_str_add_zstr(str, closure_function->common.scope->name); xdebug_str_add_fmt(str, "%s::", ANSI_COLOR_RESET); } else { xdebug_str_add_fmt(str, "%s$this%s->", ANSI_COLOR_OBJECT, ANSI_COLOR_RESET); } } xdebug_str_add_fmt(str, "%s", ANSI_COLOR_STRING); xdebug_str_add_zstr(str, closure_function->common.function_name); xdebug_str_add_fmt(str, "%s\"\n", ANSI_COLOR_RESET); } #endif static void xdebug_var_export_text_ansi(zval **struc, xdebug_str *str, int mode, int level, int debug_zval, xdebug_var_export_options *options) { HashTable *myht; char* tmp_str; int tmp_len; zend_ulong num; zend_string *key; zval *val; zval *tmpz; int z_type; if (!struc || !(*struc)) { return; } xdebug_str_add_fmt(str, "%*s", (level * 2) - 2, ""); z_type = Z_TYPE_P(*struc); if (debug_zval) { xdebug_add_variable_attributes(str, *struc, XDEBUG_VAR_ATTR_TEXT); } if (z_type == IS_INDIRECT) { tmpz = Z_INDIRECT_P(*struc); struc = &tmpz; z_type = Z_TYPE_P(*struc); } if (z_type == IS_REFERENCE) { tmpz = &((*struc)->value.ref->val); struc = &tmpz; z_type = Z_TYPE_P(*struc); } switch (z_type) { case IS_TRUE: xdebug_str_add_fmt(str, "%sbool%s(%s%s%s)", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_BOOL, "true", ANSI_COLOR_RESET); break; case IS_FALSE: xdebug_str_add_fmt(str, "%sbool%s(%s%s%s)", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_BOOL, "false", ANSI_COLOR_RESET); break; case IS_NULL: xdebug_str_add_fmt(str, "%s%sNULL%s%s", ANSI_COLOR_BOLD, ANSI_COLOR_NULL, ANSI_COLOR_RESET, ANSI_COLOR_BOLD_OFF); break; case IS_LONG: xdebug_str_add_fmt(str, "%sint%s(%s" XDEBUG_INT_FMT "%s)", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_LONG, Z_LVAL_P(*struc), ANSI_COLOR_RESET); break; case IS_DOUBLE: xdebug_str_add_fmt(str, "%sdouble%s(%s%.*H%s)", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_DOUBLE, (int) PG(serialize_precision), Z_DVAL_P(*struc), ANSI_COLOR_RESET); break; case IS_STRING: { const char *pattern = (mode == 1) ? "'\\\0..\37" : "\0"; size_t pattern_len = (mode == 1) ? 7 : 1; zend_string *i_string = zend_string_init(Z_STRVAL_P(*struc), Z_STRLEN_P(*struc), 0); zend_string *tmp_zstr; tmp_zstr = php_addcslashes(i_string, (char*) pattern, pattern_len); tmp_str = estrndup(tmp_zstr->val, tmp_zstr->len); tmp_len = tmp_zstr->len; zend_string_release(tmp_zstr); zend_string_release(i_string); if (options->no_decoration) { xdebug_str_addl(str, tmp_str, tmp_len, 0); } else if ((size_t) Z_STRLEN_P(*struc) <= (size_t) options->max_data) { xdebug_str_add_fmt( str, "%sstring%s(%s%ld%s) \"%s%s%s\"", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_LONG, Z_STRLEN_P(*struc), ANSI_COLOR_RESET, ANSI_COLOR_STRING, tmp_str, ANSI_COLOR_RESET ); } else { xdebug_str_add_fmt( str, "%sstring%s(%s%ld%s) \"%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_LONG, Z_STRLEN_P(*struc), ANSI_COLOR_RESET, ANSI_COLOR_STRING ); xdebug_str_addl(str, tmp_str, options->max_data, 0); xdebug_str_add_fmt(str, "%s\"...", ANSI_COLOR_RESET); } efree(tmp_str); } break; case IS_ARRAY: myht = Z_ARRVAL_P(*struc); if (!xdebug_zend_hash_is_recursive(myht)) { xdebug_str_add_fmt(str, "%sarray%s(%s%d%s) {\n", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_LONG, myht->nNumOfElements, ANSI_COLOR_RESET); if (level <= options->max_depth) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) { xdebug_array_element_export_text_ansi(val, num, key, level, mode, str, debug_zval, options); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); } else if (myht->nNumOfElements > 0) { xdebug_str_add_fmt(str, "%*s...\n", (level * 2), ""); } xdebug_str_add_fmt(str, "%*s}", (level * 2) - 2 , ""); } else { xdebug_str_add_fmt(str, "&%sarray%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF); } break; case IS_OBJECT: { #if PHP_VERSION_ID >= 80100 zend_class_entry *ce = Z_OBJCE_P(*struc); if (ce->ce_flags & ZEND_ACC_ENUM) { zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(*struc)); xdebug_str_add_fmt( str, "%senum%s %s%s%s::%s%s%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_OBJECT, ZSTR_VAL(ce->name), ANSI_COLOR_RESET, ANSI_COLOR_STRING, Z_STRVAL_P(case_name_zval), ANSI_COLOR_RESET ); if (ce->enum_backing_type != IS_UNDEF) { zval *value = zend_enum_fetch_case_value(Z_OBJ_P(*struc)); if (ce->enum_backing_type == IS_LONG) { xdebug_str_add_fmt( str, " : %s%s%s(%s%d%s)", ANSI_COLOR_BOLD, "int", ANSI_COLOR_RESET, ANSI_COLOR_LONG, Z_LVAL_P(value), ANSI_COLOR_RESET ); } if (ce->enum_backing_type == IS_STRING) { xdebug_str_add_fmt( str, " : %s%s%s(%s\"%s\"%s)", ANSI_COLOR_BOLD, "string", ANSI_COLOR_RESET, ANSI_COLOR_STRING, Z_STRVAL_P(value), ANSI_COLOR_RESET ); } } xdebug_str_addc(str, ';'); break; } #endif myht = xdebug_objdebug_pp(struc, XDEBUG_VAR_OBJDEBUG_USE_DEBUGINFO); if (!myht || !xdebug_zend_hash_is_recursive(myht)) { xdebug_str_add_fmt( str, "%sclass%s %s%s%s#%d (%s%d%s) {\n", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_OBJECT, STR_NAME_VAL(Z_OBJCE_P(*struc)->name), ANSI_COLOR_RESET, Z_OBJ_HANDLE_P(*struc), ANSI_COLOR_LONG, myht ? myht->nNumOfElements : 0, ANSI_COLOR_RESET ); #if PHP_VERSION_ID < 80200 handle_closure(str, *struc, level, mode); #endif if (myht && (level <= options->max_depth)) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) { xdebug_object_element_export_text_ansi(*struc, val, num, key, level, mode, str, debug_zval, options); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); } else if (myht && myht->nNumOfElements > 0) { xdebug_str_add_fmt(str, "%*s...\n", (level * 2), ""); } xdebug_str_add_fmt(str, "%*s}", (level * 2) - 2, ""); } else { xdebug_str_add_fmt(str, "%*s...\n", (level * 2), ""); } zend_release_properties(myht); break; } case IS_RESOURCE: { char *type_name; type_name = (char *) zend_rsrc_list_get_rsrc_type(Z_RES_P(*struc)); xdebug_str_add_fmt( str, "%sresource%s(%s%ld%s) of type (%s)", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_RESOURCE, Z_RES_P(*struc)->handle, ANSI_COLOR_RESET, type_name ? type_name : "Unknown" ); break; } case IS_UNDEF: xdebug_str_add_fmt(str, "%s*uninitialized*%s", ANSI_COLOR_NULL, ANSI_COLOR_RESET); break; default: xdebug_str_add_fmt(str, "%sNFC%s", ANSI_COLOR_NULL, ANSI_COLOR_RESET); break; } xdebug_str_addc(str, '\n'); } xdebug_str* xdebug_get_zval_value_text_ansi(zval *val, int mode, int debug_zval, xdebug_var_export_options *options) { xdebug_str *str = xdebug_str_new(); int default_options = 0; if (!options) { options = xdebug_var_export_options_from_ini(); default_options = 1; } if (options->show_location && !debug_zval) { char *formatted_filename; xdebug_format_filename(&formatted_filename, "%f", zend_get_executed_filename_ex()); xdebug_str_add_fmt(str, "%s%s%s:%s%d%s:\n", ANSI_COLOR_BOLD, formatted_filename, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_BOLD, zend_get_executed_lineno(), ANSI_COLOR_BOLD_OFF); xdfree(formatted_filename); } xdebug_var_export_text_ansi(&val, str, mode, 1, debug_zval, options); if (default_options) { xdfree(options->runtime); xdfree(options); } return str; } static void xdebug_var_synopsis_text_ansi(zval **struc, xdebug_str *str, int mode, int level, int debug_zval, xdebug_var_export_options *options) { HashTable *myht; zval *tmpz; int z_type; if (!struc || !(*struc)) { return; } z_type = Z_TYPE_P(*struc); if (debug_zval) { xdebug_add_variable_attributes(str, *struc, XDEBUG_VAR_ATTR_TEXT); } if (z_type == IS_REFERENCE) { tmpz = &((*struc)->value.ref->val); struc = &tmpz; z_type = Z_TYPE_P(*struc); } switch (z_type) { case IS_TRUE: xdebug_str_add_fmt(str, "%strue%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF); break; case IS_FALSE: xdebug_str_add_fmt(str, "%sfalse%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF); break; case IS_NULL: xdebug_str_add_fmt(str, "%snull%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF); break; case IS_LONG: xdebug_str_add_fmt(str, "%sint%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF); break; case IS_DOUBLE: xdebug_str_add_fmt(str, "%sdouble%s", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF); break; case IS_STRING: xdebug_str_add_fmt(str, "%sstring%s(%s%d%s)", ANSI_COLOR_BOLD, ANSI_COLOR_BOLD_OFF, ANSI_COLOR_LONG, Z_STRLEN_P(*struc), ANSI_COLOR_RESET); break; case IS_ARRAY: myht = Z_ARRVAL_P(*struc); xdebug_str_add_fmt(str, "array(%s%d%s)", ANSI_COLOR_LONG, myht->nNumOfElements, ANSI_COLOR_RESET); break; case IS_OBJECT: xdebug_str_add_fmt(str, "class %s", ZSTR_VAL(Z_OBJCE_P(*struc)->name)); break; case IS_RESOURCE: { char *type_name; type_name = (char *) zend_rsrc_list_get_rsrc_type(Z_RES_P(*struc)); xdebug_str_add_fmt(str, "resource(%s%ld%s) of type (%s)", ANSI_COLOR_LONG, Z_RES_P(*struc)->handle, ANSI_COLOR_RESET, type_name ? type_name : "Unknown"); break; } case IS_UNDEF: xdebug_str_add_fmt(str, "%s*uninitialized*%s", ANSI_COLOR_NULL, ANSI_COLOR_RESET); break; default: xdebug_str_add_fmt(str, "%sNFC%s", ANSI_COLOR_NULL, ANSI_COLOR_RESET); break; } } xdebug_str* xdebug_get_zval_synopsis_text_ansi(zval *val, int mode, int debug_zval, xdebug_var_export_options *options) { xdebug_str *str = xdebug_str_new(); int default_options = 0; if (!options) { options = xdebug_var_export_options_from_ini(); default_options = 1; } if (options->show_location && !debug_zval) { xdebug_str_add_fmt(str, "%s%s: %d%s\n", ANSI_COLOR_BOLD, zend_get_executed_filename(), zend_get_executed_lineno(), ANSI_COLOR_BOLD_OFF); } xdebug_var_synopsis_text_ansi(&val, str, mode, 1, debug_zval, options); if (default_options) { xdfree(options->runtime); xdfree(options); } return str; } xdebug-3.4.3/src/lib/var_export_text.h0000664000175000017500000000276315011062311017226 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_LIB_VAR_EXPORT_TEXT_H__ #define __HAVE_XDEBUG_LIB_VAR_EXPORT_TEXT_H__ #include "var.h" xdebug_str* xdebug_get_zval_value_text_ansi(zval *val, int mode, int debug_zval, xdebug_var_export_options *options); #define xdebug_get_zval_value_text(v,d,o) xdebug_get_zval_value_text_ansi(v,0,d,o); #define xdebug_get_zval_value_ansi(v,d,o) xdebug_get_zval_value_text_ansi(v,1,d,o); xdebug_str* xdebug_get_zval_synopsis_text_ansi(zval *val, int mode, int debug_zval, xdebug_var_export_options *options); #endif xdebug-3.4.3/src/lib/var_export_xml.c0000664000175000017500000007131415011062311017033 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2025 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "var_export_xml.h" #include "Zend/zend_closures.h" static xdebug_str *prepare_variable_name(xdebug_str *name) { xdebug_str *tmp_name; if (name->d[0] == '$' || name->d[0] == ':') { tmp_name = xdebug_str_copy(name); } else { tmp_name = xdebug_str_new(); xdebug_str_addc(tmp_name, '$'); xdebug_str_add_str(tmp_name, name); } if (tmp_name->d[tmp_name->l - 2] == ':' && tmp_name->d[tmp_name->l - 1] == ':') { xdebug_str_chop(tmp_name, 2); } return tmp_name; } /* * Returns whether we should attempt to encode name, fullname, and value as XML * elements instead of attribute values, because XML doesn't support nearly all * characters under ASCII 0x32. */ static int encoding_requested(char *value, size_t value_len) { size_t i; for (i = 0; i < value_len; i++) { if (value[i] < 0x20) { return 1; } } return 0; } static void check_if_extended_properties_are_needed(xdebug_var_export_options *options, xdebug_str *name, xdebug_str *fullname, zval *value) { if (!options->extended_properties || options->encode_as_extended_property) { return; } /* Checking name */ if (name && encoding_requested(name->d, name->l)) { options->encode_as_extended_property = 1; return; } /* Checking full name */ if (fullname && encoding_requested(fullname->d, fullname->l)) { options->encode_as_extended_property = 1; return; } /* Checking for the value portion */ if (!value) { return; } if (Z_TYPE_P(value) == IS_STRING) { if (encoding_requested(Z_STRVAL_P(value), Z_STRLEN_P(value))) { options->encode_as_extended_property = 1; return; } } if (Z_TYPE_P(value) == IS_OBJECT) { if (encoding_requested(STR_NAME_VAL(Z_OBJCE_P(value)->name), STR_NAME_LEN(Z_OBJCE_P(value)->name))) { options->encode_as_extended_property = 1; return; } } } static void add_xml_attribute_or_element(xdebug_var_export_options *options, xdebug_xml_node *node, const char *field, int field_len, xdebug_str *value) { if (options->encode_as_extended_property || (encoding_requested(value->d, value->l) && options->extended_properties)) { xdebug_xml_node *element; unsigned char *tmp_base64; size_t new_len; options->encode_as_extended_property = 1; element = xdebug_xml_node_init(field); xdebug_xml_add_attribute(element, "encoding", "base64"); tmp_base64 = xdebug_base64_encode((unsigned char*) value->d, value->l, &new_len); xdebug_xml_add_text_ex(element, (char*) tmp_base64, new_len, 1, 0); xdebug_xml_add_child(node, element); } else { xdebug_xml_add_attribute_exl(node, field, field_len, xdstrndup(value->d, value->l), value->l, 0, 1); } } void xdebug_var_xml_attach_uninitialized_var(xdebug_var_export_options *options, xdebug_xml_node *node, xdebug_str *name) { xdebug_xml_node *contents = NULL; xdebug_str *tmp_name; contents = xdebug_xml_node_init("property"); options->encode_as_extended_property = 0; tmp_name = prepare_variable_name(name); add_xml_attribute_or_element(options, contents, "name", 4, tmp_name); add_xml_attribute_or_element(options, contents, "fullname", 8, tmp_name); xdebug_str_free(tmp_name); xdebug_xml_add_attribute(contents, "type", "uninitialized"); xdebug_xml_add_child(node, contents); } /***************************************************************************** ** XML node printing routines */ #define XDEBUG_OBJECT_ITEM_TYPE_PROPERTY 0 #define XDEBUG_OBJECT_ITEM_TYPE_STATIC_PROPERTY 1<<0 #define XDEBUG_OBJECT_ITEM_TYPE_READONLY 1<<1 typedef struct { char type; char *name; int name_len; unsigned long index_key; zval *zv; zend_object *zobj; } xdebug_object_item; static void merged_hash_object_item_dtor(zval *data) { xdebug_object_item *item = Z_PTR_P(data); xdfree(item); } static int object_item_add_to_merged_hash(zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, HashTable *merged, int object_type, zend_object *zobj) { zval **zv = &zv_nptr; xdebug_object_item *item; item = xdcalloc(1, sizeof(xdebug_object_item)); item->type = object_type; item->zv = *zv; item->zobj = zobj; if (hash_key) { item->name = (char*) HASH_APPLY_KEY_VAL(hash_key); item->name_len = HASH_APPLY_KEY_LEN(hash_key) - 1; item->index_key = hash_key->h; } else { item->name = xdebug_sprintf(XDEBUG_INT_FMT, index_key); item->name_len = strlen(item->name); } zend_hash_next_index_insert_ptr(merged, item); return 0; } static int object_item_add_zend_prop_to_merged_hash(zend_property_info *zpp, HashTable *merged, int object_type, zend_object *zobj, zend_class_entry *ce) { xdebug_object_item *item; if ((zpp->flags & ZEND_ACC_STATIC) == 0) { return 0; } item = xdmalloc(sizeof(xdebug_object_item)); item->type = object_type; item->zv = &CE_STATIC_MEMBERS(ce)[zpp->offset]; item->zobj = zobj; item->name = (char*) STR_NAME_VAL(zpp->name); item->name_len = STR_NAME_LEN(zpp->name); zend_hash_next_index_insert_ptr(merged, item); return 0; } static void add_unencoded_text_value_attribute_or_element(xdebug_var_export_options *options, xdebug_xml_node *node, char *value) { if (options->encode_as_extended_property) { xdebug_xml_node *element; unsigned char *tmp_base64; size_t new_len; element = xdebug_xml_node_init("value"); xdebug_xml_add_attribute(element, "encoding", "base64"); tmp_base64 = xdebug_base64_encode((unsigned char*) value, strlen(value), &new_len); xdebug_xml_add_text_ex(element, (char*) tmp_base64, new_len, 1, 0); xdebug_xml_add_child(node, element); } else { xdebug_xml_add_text(node, value); } } static void add_encoded_text_value_attribute_or_element(xdebug_var_export_options *options, xdebug_xml_node *node, char *value, size_t value_len) { if (options->encode_as_extended_property) { xdebug_xml_node *element; unsigned char *tmp_base64; size_t new_len; element = xdebug_xml_node_init("value"); xdebug_xml_add_attribute(element, "encoding", "base64"); tmp_base64 = xdebug_base64_encode((unsigned char*) value, value_len, &new_len); xdebug_xml_add_text_ex(element, (char*) tmp_base64, new_len, 1, 0); xdebug_xml_add_child(node, element); xdfree(value); } else { xdebug_xml_add_text_encodel(node, value, value_len); } } static int xdebug_array_element_export_xml_node(zval *zv_nptr, zend_ulong index_key, zend_string *hash_key, int level, xdebug_xml_node *parent, xdebug_str *parent_name, xdebug_var_export_options *options) { zval **zv = &zv_nptr; xdebug_xml_node *node; xdebug_str *name; xdebug_str full_name = XDEBUG_STR_INITIALIZER; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { node = xdebug_xml_node_init("property"); options->encode_as_extended_property = 0; if (!HASH_KEY_IS_NUMERIC(hash_key)) { /* string key */ zend_string *i_string = zend_string_init(HASH_APPLY_KEY_VAL(hash_key), HASH_APPLY_KEY_LEN(hash_key) - 1, 0); zend_string *tmp_fullname_zstr; tmp_fullname_zstr = xdebug_addslashes(i_string); name = xdebug_str_create(HASH_APPLY_KEY_VAL(hash_key), HASH_APPLY_KEY_LEN(hash_key) - 1); if (parent_name) { xdebug_str_add_str(&full_name, parent_name); xdebug_str_add_literal(&full_name, "[\""); xdebug_str_addl(&full_name, tmp_fullname_zstr->val, tmp_fullname_zstr->len, 0); xdebug_str_add_literal(&full_name, "\"]"); } zend_string_release(tmp_fullname_zstr); zend_string_release(i_string); } else { char *tmp_idx = xdebug_sprintf(XDEBUG_INT_FMT, index_key); name = xdebug_str_create(tmp_idx, strlen(tmp_idx)); if (parent_name) { xdebug_str_add_str(&full_name, parent_name); xdebug_str_addc(&full_name, '['); xdebug_str_add_str(&full_name, name); xdebug_str_addc(&full_name, ']'); } xdfree(tmp_idx); } check_if_extended_properties_are_needed(options, name, full_name.l ? &full_name : NULL, *zv); add_xml_attribute_or_element(options, node, "name", 4, name); if (full_name.l) { add_xml_attribute_or_element(options, node, "fullname", 8, &full_name); } xdebug_xml_add_child(parent, node); xdebug_var_export_xml_node(zv, &full_name, node, options, level + 1); xdebug_str_destroy(&full_name); xdebug_str_free(name); } options->runtime[level].current_element_nr++; return 0; } static int xdebug_object_element_export_xml_node(xdebug_object_item *item_nptr, int level, xdebug_xml_node *parent, xdebug_str *parent_name, xdebug_var_export_options *options, char *class_name) { xdebug_object_item **item = &item_nptr; xdebug_xml_node *node; if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { const char *modifier; xdebug_str *tmp_name = NULL; xdebug_str *tmp_fullname = NULL; node = xdebug_xml_node_init("property"); options->encode_as_extended_property = 0; if ((*item)->name != NULL) { char *prop_class_name; xdebug_str *property_name; property_name = xdebug_get_property_info((*item)->name, (*item)->name_len + 1, &modifier, &prop_class_name); if (strcmp(modifier, "private") != 0 || strcmp(class_name, prop_class_name) == 0) { tmp_name = xdebug_str_copy(property_name); } else { tmp_name = xdebug_str_new(); xdebug_str_addc(tmp_name, '*'); xdebug_str_add(tmp_name, prop_class_name, 0); xdebug_str_addc(tmp_name, '*'); xdebug_str_add_str(tmp_name, property_name); } if (parent_name) { tmp_fullname = xdebug_str_new(); xdebug_str_add_str(tmp_fullname, parent_name); if ((*item)->type & XDEBUG_OBJECT_ITEM_TYPE_STATIC_PROPERTY) { xdebug_str_add_literal(tmp_fullname, "::"); } else { xdebug_str_add_literal(tmp_fullname, "->"); } /* Only in dynamic and *public* properties can we have non-standard characters */ if (strcmp(modifier, "private") != 0 || strcmp(class_name, prop_class_name) == 0) { if (property_name->l == 0) { xdebug_str_add_literal(tmp_fullname, "{\"\"}"); } else { if (memchr(property_name->d, '-', property_name->l) == NULL && memchr(property_name->d, '[', property_name->l) == NULL && memchr(property_name->d, '{', property_name->l) == NULL) { xdebug_str_add_str(tmp_fullname, property_name); } else { zend_string *tmp_string = zend_string_init(property_name->d, property_name->l, 0); zend_string *tmp_slashed_string; tmp_slashed_string = xdebug_addslashes(tmp_string); xdebug_str_add_literal(tmp_fullname, "{\""); xdebug_str_add_zstr(tmp_fullname, tmp_slashed_string); xdebug_str_add_literal(tmp_fullname, "\"}"); zend_string_release(tmp_slashed_string); zend_string_release(tmp_string); } } } else { xdebug_str_addc(tmp_fullname, '*'); xdebug_str_add(tmp_fullname, prop_class_name, 0); xdebug_str_addc(tmp_fullname, '*'); xdebug_str_add_str(tmp_fullname, property_name); } } xdebug_str_free(property_name); xdfree(prop_class_name); } else { /* Numerical property name */ modifier = "public"; { char *tmp_formatted_prop; tmp_formatted_prop = xdebug_sprintf(XDEBUG_INT_FMT, (*item)->index_key); tmp_name = xdebug_str_create_from_char(tmp_formatted_prop); add_xml_attribute_or_element(options, node, "name", 4, tmp_name); xdfree(tmp_formatted_prop); } if (parent_name) { char *tmp_formatted_prop; tmp_formatted_prop = xdebug_sprintf("%s%s" XDEBUG_INT_FMT, parent_name, (*item)->type & XDEBUG_OBJECT_ITEM_TYPE_STATIC_PROPERTY ? "::" : "->", (*item)->index_key); tmp_fullname = xdebug_str_create_from_char(tmp_formatted_prop); xdfree(tmp_formatted_prop); } } check_if_extended_properties_are_needed(options, tmp_name, tmp_fullname, (*item)->zv); add_xml_attribute_or_element(options, node, "name", 4, tmp_name); if (tmp_fullname) { add_xml_attribute_or_element(options, node, "fullname", 8, tmp_fullname); } if ((*item)->type & XDEBUG_OBJECT_ITEM_TYPE_STATIC_PROPERTY) { xdebug_xml_expand_attribute_value(node, "facet", "static"); } xdebug_xml_expand_attribute_value(node, "facet", modifier); if ((*item)->type & XDEBUG_OBJECT_ITEM_TYPE_READONLY) { xdebug_xml_expand_attribute_value(node, "facet", "readonly"); } xdebug_xml_add_child(parent, node); #if PHP_VERSION_ID >= 80400 { zval tmp_for_is_ptr; zval *tmp_value_for_ptr = NULL; if (Z_TYPE_P((*item)->zv) == IS_PTR) { // IS_PTR is for properties with hooks zend_property_info *prop_info = Z_PTR_P((*item)->zv); const char *unmangled_name_cstr; zend_string *unmangled_name; if ((prop_info->flags & ZEND_ACC_VIRTUAL) && !prop_info->hooks[ZEND_PROPERTY_HOOK_GET]) { return 0; } unmangled_name_cstr = zend_get_unmangled_property_name(prop_info->name); unmangled_name = zend_string_init(unmangled_name_cstr, strlen(unmangled_name_cstr), false); tmp_value_for_ptr = zend_read_property_ex(prop_info->ce, (*item)->zobj, unmangled_name, /* silent */ true, &tmp_for_is_ptr); zend_string_release_ex(unmangled_name, false); if (EG(exception)) { return 0; } xdebug_var_export_xml_node(&tmp_value_for_ptr, tmp_fullname ? tmp_fullname : NULL, node, options, level + 1); if (tmp_value_for_ptr == &tmp_for_is_ptr) { zval_ptr_dtor(tmp_value_for_ptr); } } else { xdebug_var_export_xml_node(&((*item)->zv), tmp_fullname ? tmp_fullname : NULL, node, options, level + 1); } } #else xdebug_var_export_xml_node(&((*item)->zv), tmp_fullname ? tmp_fullname : NULL, node, options, level + 1); #endif if (tmp_name) { xdebug_str_free(tmp_name); } if (tmp_fullname) { xdebug_str_free(tmp_fullname); } } options->runtime[level].current_element_nr++; return 0; } static void xdebug_var_xml_attach_property_with_contents(zend_property_info *prop_info, xdebug_xml_node *node, xdebug_var_export_options *options, zend_class_entry *class_entry, char *class_name, int *children_count) { const char *modifier; xdebug_xml_node *contents = NULL; char *prop_class_name; xdebug_str *property_name; if ((prop_info->flags & ZEND_ACC_STATIC) == 0) { return; } (*children_count)++; property_name = xdebug_get_property_info(STR_NAME_VAL(prop_info->name), STR_NAME_LEN(prop_info->name) + 1, &modifier, &prop_class_name); if (strcmp(modifier, "private") != 0 || strcmp(class_name, prop_class_name) == 0) { contents = xdebug_get_zval_value_xml_node_ex(property_name, &CE_STATIC_MEMBERS(class_entry)[prop_info->offset], XDEBUG_VAR_TYPE_STATIC, options); } else{ xdebug_str *priv_name = xdebug_str_new(); xdebug_str_addc(priv_name, '*'); xdebug_str_add(priv_name, prop_class_name, 0); xdebug_str_addc(priv_name, '*'); xdebug_str_add_str(priv_name, property_name); contents = xdebug_get_zval_value_xml_node_ex(priv_name, &CE_STATIC_MEMBERS(class_entry)[prop_info->offset], XDEBUG_VAR_TYPE_STATIC, options); xdebug_str_free(priv_name); } xdebug_str_free(property_name); xdfree(prop_class_name); if (contents) { xdebug_xml_expand_attribute_value(contents, "facet", "static"); xdebug_xml_expand_attribute_value(contents, "facet", modifier); xdebug_xml_add_child(node, contents); } else { xdebug_var_xml_attach_uninitialized_var(options, node, xdebug_str_create(ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name))); } } void xdebug_var_xml_attach_static_vars(xdebug_xml_node *node, xdebug_var_export_options *options, zend_class_entry *ce) { HashTable *static_members = &ce->properties_info; int children = 0; xdebug_xml_node *static_container; zend_property_info *zpi; static_container = xdebug_xml_node_init("property"); options->encode_as_extended_property = 0; xdebug_xml_add_attribute(static_container, "name", "::"); xdebug_xml_add_attribute(static_container, "fullname", "::"); xdebug_xml_add_attribute(static_container, "type", "object"); xdebug_xml_add_attribute_ex(static_container, "classname", xdstrdup(STR_NAME_VAL(ce->name)), 0, 1); xdebug_zend_hash_apply_protection_begin(static_members); #if PHP_VERSION_ID >= 80100 if (ce->default_static_members_count && !CE_STATIC_MEMBERS(ce)) { zend_class_init_statics(ce); } #endif ZEND_HASH_FOREACH_PTR(static_members, zpi) { xdebug_var_xml_attach_property_with_contents(zpi, static_container, options, ce, STR_NAME_VAL(ce->name), &children); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(static_members); xdebug_xml_add_attribute(static_container, "children", children > 0 ? "1" : "0"); xdebug_xml_add_attribute_ex(static_container, "numchildren", xdebug_sprintf("%d", children), 0, 1); xdebug_xml_add_child(node, static_container); } void xdebug_var_export_xml_node(zval **struc, xdebug_str *name, xdebug_xml_node *node, xdebug_var_export_options *options, int level) { HashTable *myht; zend_ulong num; zend_string *key; zval *z_val; xdebug_object_item *xoi_val; zval *tmpz; if (!*struc) { xdebug_xml_add_attribute(node, "type", "uninitialized"); return; } if (Z_TYPE_P(*struc) == IS_INDIRECT) { tmpz = ((*struc)->value.zv); struc = &tmpz; } if (Z_TYPE_P(*struc) == IS_REFERENCE) { tmpz = &((*struc)->value.ref->val); struc = &tmpz; } switch (Z_TYPE_P(*struc)) { case IS_TRUE: case IS_FALSE: xdebug_xml_add_attribute(node, "type", "bool"); add_unencoded_text_value_attribute_or_element(options, node, xdebug_sprintf("%d", Z_TYPE_P(*struc) == IS_TRUE ? 1 : 0)); break; case IS_NULL: xdebug_xml_add_attribute(node, "type", "null"); break; case IS_LONG: xdebug_xml_add_attribute(node, "type", "int"); add_unencoded_text_value_attribute_or_element(options, node, xdebug_sprintf(XDEBUG_INT_FMT, Z_LVAL_P(*struc))); break; case IS_DOUBLE: xdebug_xml_add_attribute(node, "type", "float"); add_unencoded_text_value_attribute_or_element(options, node, xdebug_sprintf("%.*H", (int) PG(serialize_precision), Z_DVAL_P(*struc))); break; case IS_STRING: xdebug_xml_add_attribute(node, "type", "string"); if (options->max_data == 0 || (size_t) Z_STRLEN_P(*struc) <= (size_t) options->max_data) { add_encoded_text_value_attribute_or_element(options, node, xdstrndup(Z_STRVAL_P(*struc), Z_STRLEN_P(*struc)), Z_STRLEN_P(*struc)); } else { add_encoded_text_value_attribute_or_element(options, node, xdstrndup(Z_STRVAL_P(*struc), options->max_data), options->max_data); } xdebug_xml_add_attribute_ex(node, "size", xdebug_sprintf("%d", Z_STRLEN_P(*struc)), 0, 1); break; case IS_ARRAY: myht = Z_ARRVAL_P(*struc); xdebug_xml_add_attribute(node, "type", "array"); xdebug_xml_add_attribute(node, "children", myht->nNumOfElements > 0?"1":"0"); if (!xdebug_zend_hash_is_recursive(myht)) { xdebug_xml_add_attribute_ex(node, "numchildren", xdebug_sprintf("%d", myht->nNumOfElements), 0, 1); if (level < options->max_depth) { xdebug_xml_add_attribute_ex(node, "page", xdebug_sprintf("%d", options->runtime[level].page), 0, 1); xdebug_xml_add_attribute_ex(node, "pagesize", xdebug_sprintf("%d", options->max_children), 0, 1); options->runtime[level].current_element_nr = 0; if (level == 0) { options->runtime[level].start_element_nr = options->max_children * options->runtime[level].page; options->runtime[level].end_element_nr = options->max_children * (options->runtime[level].page + 1); } else { options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; } xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, z_val) { xdebug_array_element_export_xml_node(z_val, num, key, level, node, name, options); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); } } else { xdebug_xml_add_attribute(node, "recursive", "1"); } break; case IS_OBJECT: { HashTable *merged_hash; zend_string *class_name; zend_class_entry *ce; int extra_children = 0; zend_property_info *zpi_val; ALLOC_HASHTABLE(merged_hash); zend_hash_init(merged_hash, 128, NULL, merged_hash_object_item_dtor, 0); class_name = Z_OBJCE_P(*struc)->name; ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_DEFAULT); /* Adding static properties */ xdebug_zend_hash_apply_protection_begin(&ce->properties_info); #if PHP_VERSION_ID >= 80100 zend_class_init_statics(ce); #else if (ce->type == ZEND_INTERNAL_CLASS || (ce->ce_flags & ZEND_ACC_IMMUTABLE)) { zend_class_init_statics(ce); } #endif ZEND_HASH_FOREACH_PTR(&ce->properties_info, zpi_val) { object_item_add_zend_prop_to_merged_hash(zpi_val, merged_hash, (int) XDEBUG_OBJECT_ITEM_TYPE_STATIC_PROPERTY, Z_OBJ_P(*struc), ce); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(&ce->properties_info); /* Adding normal properties */ myht = xdebug_objdebug_pp(struc, XDEBUG_VAR_OBJDEBUG_DEFAULT); if (myht) { zval *tmp_val; xdebug_zend_hash_apply_protection_begin(myht); ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, tmp_val) { int flags = XDEBUG_OBJECT_ITEM_TYPE_PROPERTY; #if PHP_VERSION_ID >= 80100 if (ce->type != ZEND_INTERNAL_CLASS && !HASH_KEY_IS_NUMERIC(key)) { const char *cls_name, *tmp_prop_name; size_t tmp_prop_name_len; zend_string *unmangled; zend_property_info *info; zend_unmangle_property_name_ex(key, &cls_name, &tmp_prop_name, &tmp_prop_name_len); unmangled = zend_string_init_interned(tmp_prop_name, tmp_prop_name_len, 0); info = zend_get_property_info(Z_OBJCE_P(*struc), unmangled, 1); zend_string_release(unmangled); if (info && info != ZEND_WRONG_PROPERTY_INFO) { if (info->flags & ZEND_ACC_READONLY) { flags |= XDEBUG_OBJECT_ITEM_TYPE_READONLY; } #if PHP_VERSION_ID >= 80400 if (info->flags & ZEND_ACC_VIRTUAL) { continue; } #endif } } #endif object_item_add_to_merged_hash(tmp_val, num, key, merged_hash, flags, Z_OBJ_P(*struc)); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(myht); } xdebug_xml_add_attribute(node, "type", "object"); #if PHP_VERSION_ID >= 80100 // Enums { zend_class_entry *ce = Z_OBJCE_P(*struc); if (ce->ce_flags & ZEND_ACC_ENUM) { xdebug_xml_expand_attribute_value(node, "facet", "enum"); } } #endif if (instanceof_function(Z_OBJCE_P(*struc), zend_ce_closure)) { #if PHP_VERSION_ID < 80200 xdebug_xml_node *closure_cont, *closure_func; const zend_function *closure_function = zend_get_closure_method_def(Z_OBJ_P(*struc)); closure_cont = xdebug_xml_node_init("property"); xdebug_xml_add_attribute(closure_cont, "facet", "virtual readonly"); xdebug_xml_add_attribute(closure_cont, "name", "{closure}"); xdebug_xml_add_attribute(closure_cont, "type", "array"); xdebug_xml_add_attribute(closure_cont, "children", "1"); xdebug_xml_add_attribute(closure_cont, "page", "0"); xdebug_xml_add_attribute(closure_cont, "pagesize", "2"); if (closure_function->common.scope) { xdebug_xml_node *closure_scope = xdebug_xml_node_init("property"); xdebug_xml_add_attribute(closure_scope, "facet", "readonly"); xdebug_xml_add_attribute(closure_scope, "name", "scope"); xdebug_xml_add_attribute(closure_scope, "type", "string"); if (closure_function->common.fn_flags & ZEND_ACC_STATIC) { xdebug_xml_add_text_ex( closure_scope, ZSTR_VAL(closure_function->common.scope->name), ZSTR_LEN(closure_function->common.scope->name), 0, 0 ); } else { xdebug_xml_add_text_ex(closure_scope, (char*) "$this", 6, 0, 0); } xdebug_xml_add_child(closure_cont, closure_scope); xdebug_xml_add_attribute(closure_cont, "numchildren", "2"); } else { xdebug_xml_add_attribute(closure_cont, "numchildren", "1"); } closure_func = xdebug_xml_node_init("property"); xdebug_xml_add_attribute(closure_func, "facet", "readonly"); xdebug_xml_add_attribute(closure_func, "name", "function"); xdebug_xml_add_attribute(closure_func, "type", "string"); xdebug_xml_add_text_ex( closure_func, ZSTR_VAL(closure_function->common.function_name), ZSTR_LEN(closure_function->common.function_name), 0, 0 ); xdebug_xml_add_child(closure_cont, closure_func); xdebug_xml_add_child(node, closure_cont); extra_children = 1; #endif xdebug_xml_expand_attribute_value(node, "facet", "closure"); } { xdebug_str tmp_str; tmp_str.d = ZSTR_VAL(class_name); tmp_str.l = ZSTR_LEN(class_name); add_xml_attribute_or_element(options, node, "classname", 9, &tmp_str); } xdebug_xml_add_attribute(node, "children", (merged_hash->nNumOfElements || extra_children) ? "1" : "0"); if (!myht || !xdebug_zend_hash_is_recursive(myht)) { xdebug_xml_add_attribute_ex( node, "numchildren", xdebug_sprintf("%d", zend_hash_num_elements(merged_hash) + extra_children), 0, 1 ); if (level < options->max_depth) { xdebug_xml_add_attribute_ex(node, "page", xdebug_sprintf("%d", options->runtime[level].page), 0, 1); xdebug_xml_add_attribute_ex(node, "pagesize", xdebug_sprintf("%d", options->max_children), 0, 1); options->runtime[level].current_element_nr = 0; if (level == 0) { options->runtime[level].start_element_nr = options->max_children * options->runtime[level].page; options->runtime[level].end_element_nr = options->max_children * (options->runtime[level].page + 1); } else { options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; } xdebug_zend_hash_apply_protection_begin(merged_hash); ZEND_HASH_FOREACH_KEY_PTR(merged_hash, num, key, xoi_val) { xdebug_object_element_export_xml_node(xoi_val, level, node, name, options, ZSTR_VAL(class_name)); } ZEND_HASH_FOREACH_END(); xdebug_zend_hash_apply_protection_end(merged_hash); } } zend_hash_destroy(merged_hash); FREE_HASHTABLE(merged_hash); zend_release_properties(myht); break; } case IS_RESOURCE: { char *type_name; xdebug_xml_add_attribute(node, "type", "resource"); type_name = (char *) zend_rsrc_list_get_rsrc_type(Z_RES_P(*struc)); xdebug_xml_add_text(node, xdebug_sprintf("resource id='%ld' type='%s'", Z_RES_P(*struc)->handle, type_name ? type_name : "Unknown")); break; } case IS_UNDEF: xdebug_xml_add_attribute(node, "type", "uninitialized"); break; default: xdebug_xml_add_attribute(node, "type", "null"); break; } } xdebug_xml_node* xdebug_get_zval_value_xml_node_ex(xdebug_str *name, zval *val, int var_type, xdebug_var_export_options *options) { xdebug_xml_node *node; xdebug_str *short_name = NULL; xdebug_str *full_name = NULL; node = xdebug_xml_node_init("property"); options->encode_as_extended_property = 0; if (name) { switch (var_type) { case XDEBUG_VAR_TYPE_NORMAL: { short_name = prepare_variable_name(name); full_name = xdebug_str_copy(short_name); } break; case XDEBUG_VAR_TYPE_STATIC: { xdebug_str tmp_formatted_name = XDEBUG_STR_INITIALIZER; xdebug_str_add_literal(&tmp_formatted_name, "::"); xdebug_str_add_str(&tmp_formatted_name, name); short_name = xdebug_str_copy(&tmp_formatted_name); full_name = xdebug_str_copy(&tmp_formatted_name); xdebug_str_destroy(&tmp_formatted_name); } break; case XDEBUG_VAR_TYPE_CONSTANT: short_name = xdebug_str_copy(name); full_name = xdebug_str_copy(name); break; } check_if_extended_properties_are_needed(options, short_name, full_name, val); add_xml_attribute_or_element(options, node, "name", 4, short_name); add_xml_attribute_or_element(options, node, "fullname", 8, full_name); } xdebug_var_export_xml_node(&val, full_name ? full_name : NULL, node, options, 0); if (short_name) { xdebug_str_free(short_name); } if (full_name) { xdebug_str_free(full_name); } return node; } xdebug-3.4.3/src/lib/var_export_xml.h0000664000175000017500000000355015011062311017035 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_LIB_VAR_EXPORT_XML_H__ #define __HAVE_XDEBUG_LIB_VAR_EXPORT_XML_H__ #include "var.h" #include "xml.h" void xdebug_var_xml_attach_static_vars(xdebug_xml_node *node, xdebug_var_export_options *options, zend_class_entry *ce); void xdebug_var_xml_attach_static_var_with_contents(zval **zv, int num_args, va_list args, zend_hash_key *hash_key); void xdebug_var_xml_attach_uninitialized_var(xdebug_var_export_options *options, xdebug_xml_node *node, xdebug_str *name); #define xdebug_get_zval_value_xml_node(name, val, options) xdebug_get_zval_value_xml_node_ex(name, val, XDEBUG_VAR_TYPE_NORMAL, options) xdebug_xml_node* xdebug_get_zval_value_xml_node_ex(xdebug_str *name, zval *val, int var_type, xdebug_var_export_options *options); void xdebug_var_export_xml_node(zval **struc, xdebug_str *name, xdebug_xml_node *node, xdebug_var_export_options *options, int level); #endif xdebug-3.4.3/src/lib/vector.h0000664000175000017500000000600515011062311015264 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_VECTOR_H__ #define __XDEBUG_VECTOR_H__ #include #include "mm.h" typedef void (*xdebug_vector_dtor)(void *); typedef struct _xdebug_vector { size_t capacity; size_t count; size_t element_size; void *data; xdebug_vector_dtor dtor; } xdebug_vector; static void __xdebug_grow_vector_if_needed(xdebug_vector *v) { if (v->count + 1 > v->capacity) { if (v->capacity == 0) { v->capacity = 32; } else { v->capacity = v->capacity * 3 / 2; } v->data = xdrealloc(v->data, v->capacity * v->element_size); } } static inline void *xdebug_vector_push(xdebug_vector *v) { void *tmp_top = NULL; __xdebug_grow_vector_if_needed(v); v->count++; tmp_top = ((char*) v->data + ((v->count - 1) * v->element_size)); memset(tmp_top, '\0', v->element_size); return tmp_top; } static inline void xdebug_vector_pop(xdebug_vector *v) { v->dtor(((char*) v->data + ((v->count - 1) * v->element_size))); v->count--; } static inline void *xdebug_vector_element_get(xdebug_vector *v, size_t index) { if (!v || index >= v->count) { return NULL; } return ((char*) v->data + (index * (v)->element_size)); } static inline int xdebug_vector_element_is_valid(xdebug_vector *v, void *element) { if ((char*) element < (char*) v->data) { return 0; } if ((char*) element > ((char*) v->data + ((v->count - 1) * v->element_size))) { return 0; } return 1; } #define XDEBUG_VECTOR_HEAD(v) xdebug_vector_element_get((v), 0) #define XDEBUG_VECTOR_TAIL(v) xdebug_vector_element_get((v), (v)->count-1) #define XDEBUG_VECTOR_COUNT(v) (v)->count static inline xdebug_vector *xdebug_vector_alloc(size_t element_size, xdebug_vector_dtor dtor) { xdebug_vector *tmp; tmp = xdmalloc(sizeof(xdebug_vector)); tmp->capacity = 0; tmp->count = 0; tmp->data = NULL; tmp->dtor = dtor; tmp->element_size = element_size; return tmp; } static inline void xdebug_vector_destroy(xdebug_vector *v) { while (XDEBUG_VECTOR_COUNT(v)) { xdebug_vector_pop(v); } xdfree(v->data); xdfree(v); } #endif /* __XDEBUG_VECTOR_H__ */ xdebug-3.4.3/src/lib/xml.c0000664000175000017500000001327115011062311014560 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include "mm.h" #include "str.h" #include "var.h" #include "xml.h" #include "compat.h" static void xdebug_xml_return_attribute(xdebug_xml_attribute* attr, xdebug_str* output) { char *tmp; size_t newlen; xdebug_str_addc(output, ' '); /* attribute name */ tmp = xdebug_xmlize(attr->name, attr->name_len, &newlen); xdebug_str_addl(output, tmp, newlen, 0); efree(tmp); /* attribute value */ xdebug_str_add_literal(output, "=\""); if (attr->value) { tmp = xdebug_xmlize(attr->value->d, attr->value->l, &newlen); xdebug_str_add(output, tmp, 0); efree(tmp); } xdebug_str_addc(output, '\"'); if (attr->next) { xdebug_xml_return_attribute(attr->next, output); } } static void xdebug_xml_return_text_node(xdebug_xml_text_node* node, xdebug_str* output) { xdebug_str_add_literal(output, "encode) { /* if cdata tags are in the text, then we must base64 encode */ size_t new_len = 0; unsigned char *encoded_text; encoded_text = xdebug_base64_encode((unsigned char*) node->text, node->text_len, &new_len); xdebug_str_add(output, (char*) encoded_text, 0); xdfree(encoded_text); } else { xdebug_str_add(output, node->text, 0); } xdebug_str_add_literal(output, "]]>"); } void xdebug_xml_return_node(xdebug_xml_node* node, struct xdebug_str *output) { xdebug_str_addc(output, '<'); xdebug_str_add(output, node->tag, 0); if (node->text && node->text->encode) { xdebug_xml_add_attribute_ex(node, "encoding", "base64", 0, 0); } if (node->attribute) { xdebug_xml_return_attribute(node->attribute, output); } xdebug_str_addc(output, '>'); if (node->child) { xdebug_xml_return_node(node->child, output); } if (node->text) { xdebug_xml_return_text_node(node->text, output); } xdebug_str_add_literal(output, "tag, 0); xdebug_str_addc(output, '>'); if (node->next) { xdebug_xml_return_node(node->next, output); } } xdebug_xml_node *xdebug_xml_node_init_ex(const char *tag, int free_tag) { xdebug_xml_node *xml = xdmalloc(sizeof (xdebug_xml_node)); xml->tag = (char*) tag; xml->text = NULL; xml->child = NULL; xml->attribute = NULL; xml->next = NULL; xml->free_tag = free_tag; return xml; } void xdebug_xml_add_attribute_exl(xdebug_xml_node* xml, const char *attribute, size_t attribute_len, const char *value, size_t value_len, int free_name, int free_value) { xdebug_xml_attribute *attr = xdmalloc(sizeof (xdebug_xml_attribute)); xdebug_xml_attribute **ptr; /* Init structure */ attr->name = (char*) attribute; attr->name_len = attribute_len; attr->value = xdebug_str_create(value, value_len); attr->next = NULL; attr->free_name = free_name; if (free_value) { xdfree((char*) value); /* This is ugly, but it's to preserve BC */ } /* Find last attribute in node */ ptr = &xml->attribute; while (*ptr != NULL) { ptr = &(*ptr)->next; } *ptr = attr; } xdebug_str *xdebug_xml_get_attribute_value(xdebug_xml_node *xml, const char *attribute) { xdebug_xml_attribute **ptr = &xml->attribute; while (*ptr != NULL) { if (strcmp((*ptr)->name, attribute) == 0) { return (*ptr)->value; } ptr = &(*ptr)->next; } return NULL; } void xdebug_xml_add_child(xdebug_xml_node *xml, xdebug_xml_node *child) { xdebug_xml_node **ptr; ptr = &xml->child; while (*ptr != NULL) { ptr = &((*ptr)->next); } *ptr = child; } static void xdebug_xml_text_node_dtor(xdebug_xml_text_node* node) { if (node->free_value && node->text) { xdfree(node->text); } xdfree(node); } void xdebug_xml_add_text(xdebug_xml_node *xml, char *text) { xdebug_xml_add_text_ex(xml, text, strlen(text), 1, 0); } void xdebug_xml_add_text_encode(xdebug_xml_node *xml, char *text) { xdebug_xml_add_text_ex(xml, text, strlen(text), 1, 1); } void xdebug_xml_add_text_ex(xdebug_xml_node *xml, char *text, int length, int free_text, int encode) { xdebug_xml_text_node *node = xdmalloc(sizeof (xdebug_xml_text_node)); node->free_value = free_text; node->encode = encode; if (xml->text) { xdebug_xml_text_node_dtor(xml->text); } node->text = text; node->text_len = length; xml->text = node; if (!encode && strstr(node->text, "]]>")) { node->encode = 1; } } static void xdebug_xml_attribute_dtor(xdebug_xml_attribute *attr) { if (attr->next) { xdebug_xml_attribute_dtor(attr->next); } if (attr->free_name) { xdfree(attr->name); } xdebug_str_free(attr->value); xdfree(attr); } void xdebug_xml_node_dtor(xdebug_xml_node* xml) { if (xml->next) { xdebug_xml_node_dtor(xml->next); } if (xml->child) { xdebug_xml_node_dtor(xml->child); } if (xml->attribute) { xdebug_xml_attribute_dtor(xml->attribute); } if (xml->free_tag) { xdfree(xml->tag); } if (xml->text) { xdebug_xml_text_node_dtor(xml->text); } xdfree(xml); } xdebug-3.4.3/src/lib/xml.h0000664000175000017500000000652515011062311014571 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/str.h" #ifndef __HAVE_XDEBUG_XML_H__ #define __HAVE_XDEBUG_XML_H__ typedef struct _xdebug_xml_attribute xdebug_xml_attribute; typedef struct _xdebug_xml_text_node xdebug_xml_text_node; typedef struct _xdebug_xml_node xdebug_xml_node; struct _xdebug_xml_attribute { char *name; int name_len; xdebug_str *value; struct _xdebug_xml_attribute *next; int free_name; }; /* todo: support multiple text nodes inside an element */ struct _xdebug_xml_text_node { char *text; int free_value; int encode; int text_len; }; struct _xdebug_xml_node { char *tag; struct _xdebug_xml_text_node *text; struct _xdebug_xml_attribute *attribute; struct _xdebug_xml_node *child; struct _xdebug_xml_node *next; int free_tag; }; #define xdebug_xml_node_init(t) xdebug_xml_node_init_ex((t), 0) #define xdebug_xml_add_attribute_ex(x,a,v,fa,fv) { const char *ta = (a), *tv = (v); xdebug_xml_add_attribute_exl((x), (ta), strlen((ta)), (tv), strlen((tv)), fa, fv); } #define xdebug_xml_add_attribute(x,a,v) xdebug_xml_add_attribute_ex((x), (a), (v), 0, 0); xdebug_xml_node *xdebug_xml_node_init_ex(const char *tag, int free_tag); void xdebug_xml_add_attribute_exl(xdebug_xml_node* xml, const char *attribute, size_t attribute_len, const char *value, size_t value_len, int free_name, int free_value); void xdebug_xml_add_child(xdebug_xml_node *xml, xdebug_xml_node *child); void xdebug_xml_add_text_ex(xdebug_xml_node *xml, char *text, int length, int free_text, int encode); void xdebug_xml_add_text(xdebug_xml_node *xml, char *text); void xdebug_xml_add_text_encode(xdebug_xml_node *xml, char *text); #define xdebug_xml_add_textl(x,t,l) xdebug_xml_add_text_ex((x), (t), (l), 1, 0) #define xdebug_xml_add_text_encodel(x,t,l) xdebug_xml_add_text_ex((x), (t), (l), 1, 1) xdebug_str *xdebug_xml_get_attribute_value(xdebug_xml_node *xml, const char *attribute); #define xdebug_xml_expand_attribute_value(n,a,v) { \ xdebug_str *orig_value = xdebug_xml_get_attribute_value((n), (a)); \ if (orig_value) { \ xdebug_str_addc(orig_value, ' '); \ xdebug_str_add(orig_value, v, 0); \ } else { \ xdebug_xml_add_attribute((n), (a), (v)); \ } \ } void xdebug_xml_return_node(xdebug_xml_node* node, struct xdebug_str *output); void xdebug_xml_node_dtor(xdebug_xml_node* xml); #endif xdebug-3.4.3/src/coverage/branch_info.c0000664000175000017500000003351615011062311017261 0ustar derickderick/* +----------------------------------------------------------------------+ | Copyright (c) 1997-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to the 2-Clause BSD license which is | | available through the LICENSE file, or online at | | http://opensource.org/licenses/bsd-license.php | +----------------------------------------------------------------------+ */ #include #include #include "php_xdebug.h" #include "code_coverage_private.h" #include "lib/hash.h" #include "lib/log.h" #include "lib/str.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) xdebug_branch_info *xdebug_branch_info_create(unsigned int size) { xdebug_branch_info *tmp; tmp = calloc(1, sizeof(xdebug_branch_info)); tmp->size = size; tmp->branches = calloc(size, sizeof(xdebug_branch)); tmp->entry_points = xdebug_set_create(size); tmp->starts = xdebug_set_create(size); tmp->ends = xdebug_set_create(size); tmp->path_info.paths_count = 0; tmp->path_info.paths_size = 0; tmp->path_info.paths = NULL; return tmp; } void xdebug_branch_info_free(xdebug_branch_info *branch_info) { unsigned int i; for (i = 0; i < branch_info->path_info.paths_count; i++) { free(branch_info->path_info.paths[i]->elements); free(branch_info->path_info.paths[i]); } free(branch_info->path_info.paths); xdebug_hash_destroy(branch_info->path_info.path_hash); free(branch_info->branches); xdebug_set_free(branch_info->entry_points); xdebug_set_free(branch_info->starts); xdebug_set_free(branch_info->ends); free(branch_info); } void xdebug_branch_info_update(xdebug_branch_info *branch_info, unsigned int pos, unsigned int lineno, unsigned int outidx, unsigned int jump_pos) { xdebug_set_add(branch_info->ends, pos); if (outidx < XDEBUG_BRANCH_MAX_OUTS) { branch_info->branches[pos].outs[outidx] = jump_pos; if (outidx + 1 > branch_info->branches[pos].outs_count) { branch_info->branches[pos].outs_count = outidx + 1; } } branch_info->branches[pos].start_lineno = lineno; } static void only_leave_first_catch(zend_op_array *opa, xdebug_branch_info *branch_info, int position) { unsigned int exit_jmp; #if ZEND_USE_ABS_JMP_ADDR zend_op *base_address = &(opa->opcodes[0]); #endif if (opa->opcodes[position].opcode == ZEND_FETCH_CLASS) { position++; } if (opa->opcodes[position].opcode != ZEND_CATCH) { return; } xdebug_set_remove(branch_info->entry_points, position); if (opa->opcodes[position].extended_value & ZEND_LAST_CATCH) { return; } exit_jmp = XDEBUG_ZNODE_JMP_LINE(opa->opcodes[position].op2, position, base_address); if (opa->opcodes[exit_jmp].opcode == ZEND_FETCH_CLASS) { exit_jmp++; } if (opa->opcodes[exit_jmp].opcode == ZEND_CATCH) { only_leave_first_catch(opa, branch_info, exit_jmp); } } void xdebug_branch_post_process(zend_op_array *opa, xdebug_branch_info *branch_info) { unsigned int i; int in_branch = 0, last_start = -1; #if ZEND_USE_ABS_JMP_ADDR zend_op *base_address = &(opa->opcodes[0]); #endif /* Figure out which CATCHes are chained, and hence which ones should be * considered entry points */ for (i = 0; i < branch_info->entry_points->size; i++) { if (xdebug_set_in(branch_info->entry_points, i) && opa->opcodes[i].opcode == ZEND_CATCH) { #if ZEND_USE_ABS_JMP_ADDR # if PHP_VERSION_ID >= 80200 if (opa->opcodes[i].op2.jmp_addr != (void*) -1) { # else if (opa->opcodes[i].op2.jmp_addr != NULL) { # endif #else if (opa->opcodes[i].op2.jmp_offset != 0) { #endif only_leave_first_catch(opa, branch_info, XDEBUG_ZNODE_JMP_LINE(opa->opcodes[i].op2, i, base_address)); } } } for (i = 0; i < branch_info->starts->size; i++) { if (xdebug_set_in(branch_info->starts, i)) { if (in_branch) { branch_info->branches[last_start].outs_count = 1; branch_info->branches[last_start].outs[0] = i; branch_info->branches[last_start].end_op = i-1; branch_info->branches[last_start].end_lineno = branch_info->branches[i].start_lineno; } last_start = i; in_branch = 1; } if (xdebug_set_in(branch_info->ends, i)) { size_t j; for (j = 0; j < branch_info->branches[i].outs_count; j++) { branch_info->branches[last_start].outs[j] = branch_info->branches[i].outs[j]; } branch_info->branches[last_start].outs_count = branch_info->branches[i].outs_count; branch_info->branches[last_start].end_op = i; branch_info->branches[last_start].end_lineno = branch_info->branches[i].start_lineno; in_branch = 0; } } } static void xdebug_path_add(xdebug_path *path, unsigned int nr) { if (!path) { return; } if (path->elements_count == path->elements_size) { path->elements_size += 32; path->elements = realloc(path->elements, sizeof(unsigned int) * path->elements_size); } path->elements[path->elements_count] = nr; path->elements_count++; } static void xdebug_path_info_add_path(xdebug_path_info *path_info, xdebug_path *path) { if (path_info->paths_count == path_info->paths_size) { path_info->paths_size += 32; path_info->paths = realloc(path_info->paths, sizeof(xdebug_path*) * path_info->paths_size); } path_info->paths[path_info->paths_count] = path; path_info->paths_count++; } static void xdebug_path_info_make_sure_level_exists(xdebug_path_info *path_info, unsigned int level) { unsigned int i = 0, orig_size; orig_size = path_info->paths_size; if (level >= path_info->paths_size) { path_info->paths_size = level + 32; path_info->paths = realloc(path_info->paths, sizeof(xdebug_path*) * path_info->paths_size); for (i = orig_size; i < XG_COV(branches).size; i++) { XG_COV(branches).last_branch_nr[i] = -1; } for (i = orig_size; i < path_info->paths_size; i++) { path_info->paths[i] = NULL; } } } void xdebug_path_info_add_path_for_level(xdebug_path_info *path_info, xdebug_path *path, unsigned int level) { xdebug_path_info_make_sure_level_exists(path_info, level); path_info->paths[level] = path; } xdebug_path *xdebug_path_info_get_path_for_level(xdebug_path_info *path_info, unsigned int level) { xdebug_path_info_make_sure_level_exists(path_info, level); return path_info->paths[level]; } xdebug_path *xdebug_path_new(xdebug_path *old_path) { xdebug_path *tmp; tmp = calloc(1, sizeof(xdebug_path)); if (old_path) { unsigned i; for (i = 0; i < old_path->elements_count; i++) { xdebug_path_add(tmp, old_path->elements[i]); } } return tmp; } void xdebug_path_free(xdebug_path *path) { if (path->elements) { free(path->elements); } free(path); } static unsigned int xdebug_branch_find_last_element(xdebug_path *path) { return path->elements[path->elements_count-1]; } static int xdebug_path_exists(xdebug_path *path, unsigned int elem1, unsigned int elem2) { unsigned int i; for (i = 0; i < path->elements_count - 1; i++) { if (path->elements[i] == elem1 && path->elements[i + 1] == elem2) { return 1; } } return 0; } static void xdebug_branch_find_path(unsigned int nr, xdebug_branch_info *branch_info, xdebug_path *prev_path) { unsigned int last; xdebug_path *new_path; int found = 0; size_t i = 0; if (branch_info->path_info.paths_count > 4095) { return; } new_path = xdebug_path_new(prev_path); xdebug_path_add(new_path, nr); last = xdebug_branch_find_last_element(new_path); for (i = 0; i < branch_info->branches[nr].outs_count; i++) { int out = branch_info->branches[nr].outs[i]; if (out != 0 && out != XDEBUG_JMP_EXIT && !xdebug_path_exists(new_path, last, out)) { xdebug_branch_find_path(out, branch_info, new_path); found = 1; } } if (found) { xdebug_path_free(new_path); return; } xdebug_path_info_add_path(&(branch_info->path_info), new_path); } xdebug_path_info *xdebug_path_info_ctor(void) { xdebug_path_info *tmp; tmp = xdmalloc(sizeof(xdebug_path_info)); tmp->paths_count = 0; tmp->paths_size = 0; tmp->paths = NULL; tmp->path_hash = NULL; return tmp; } void xdebug_path_info_dtor(xdebug_path_info *path_info) { unsigned int i; for (i = 0; i < path_info->paths_count; i++) { xdebug_path_free(path_info->paths[i]); } xdfree(path_info->paths); path_info->paths = NULL; if (path_info->path_hash) { xdebug_hash_destroy(path_info->path_hash); path_info->path_hash = NULL; } xdfree(path_info); } void xdebug_create_key_for_path(xdebug_path *path, xdebug_str *str) { unsigned int i; char temp_nr[16]; for (i = 0; i < path->elements_count; i++) { snprintf(temp_nr, 15, "%u:", path->elements[i]); xdebug_str_add(str, temp_nr, 0); } } void xdebug_branch_find_paths(xdebug_branch_info *branch_info) { unsigned int i; for (i = 0; i < branch_info->entry_points->size; i++) { if (xdebug_set_in(branch_info->entry_points, i)) { xdebug_branch_find_path(i, branch_info, NULL); } } branch_info->path_info.path_hash = xdebug_hash_alloc(128, NULL); for (i = 0; i < branch_info->path_info.paths_count; i++) { xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_create_key_for_path(branch_info->path_info.paths[i], &str); xdebug_hash_add(branch_info->path_info.path_hash, str.d, str.l, branch_info->path_info.paths[i]); xdfree(str.d); } } void xdebug_branch_info_mark_reached(zend_string *filename, char *function_name, zend_op_array *op_array, long opcode_nr) { xdebug_coverage_file *file; xdebug_coverage_function *function; xdebug_branch_info *branch_info; if (XG_COV(previous_mark_filename) && zend_string_equals(XG_COV(previous_mark_filename), filename)) { file = XG_COV(previous_mark_file); } else { if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) { return; } if (XG_COV(previous_mark_filename)) { zend_string_release(XG_COV(previous_mark_filename)); } XG_COV(previous_mark_filename) = zend_string_copy(file->name); XG_COV(previous_mark_file) = file; } /* If there is no branch info, we don't have to do more */ if (!file->has_branch_info) { return; } /* Check if the function already exists in the hash */ if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) { return; } branch_info = function->branch_info; if (opcode_nr != 0 && xdebug_set_in(branch_info->entry_points, opcode_nr)) { xdebug_code_coverage_end_of_function(op_array, filename, function_name); xdebug_code_coverage_start_of_function(op_array, function_name); } if (xdebug_set_in(branch_info->starts, opcode_nr)) { char *key; void *dummy; function_stack_entry *tail_fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); /* Mark out for previous branch, if one is set */ if (XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))] != -1) { size_t i = 0; for (i = 0; i < branch_info->branches[XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))]].outs_count; i++) { if (branch_info->branches[XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))]].outs[i] == opcode_nr) { branch_info->branches[XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))]].outs_hit[i] = 1; } } } key = xdebug_sprintf("%d:%d:%d", opcode_nr, XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))], tail_fse->function_nr); if (!xdebug_hash_find(XG_COV(visited_branches), key, strlen(key), (void*) &dummy)) { xdebug_path_add(XG_COV(paths_stack)->paths[XDEBUG_VECTOR_COUNT(XG_BASE(stack))], opcode_nr); xdebug_hash_add(XG_COV(visited_branches), key, strlen(key), NULL); } xdfree(key); branch_info->branches[opcode_nr].hit = 1; XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))] = opcode_nr; } } void xdebug_branch_info_mark_end_of_function_reached(zend_string *filename, char *function_name, char *key, int key_len) { xdebug_coverage_file *file; xdebug_coverage_function *function; xdebug_branch_info *branch_info; xdebug_path *path; if (XG_COV(previous_mark_filename) && zend_string_equals(XG_COV(previous_mark_filename), filename) == 0) { file = XG_COV(previous_mark_file); } else { if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) { return; } zend_string_release(XG_COV(previous_mark_filename)); XG_COV(previous_mark_filename) = zend_string_copy(file->name); XG_COV(previous_mark_file) = file; } /* If there is no branch info, we don't have to do more */ if (!file->has_branch_info) { return; } /* Check if the function already exists in the hash */ if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) { return; } branch_info = function->branch_info; if (!xdebug_hash_find(branch_info->path_info.path_hash, key, key_len, (void *) &path)) { return; } path->hit = 1; } void xdebug_branch_info_add_branches_and_paths(zend_string *filename, char *function_name, xdebug_branch_info *branch_info) { xdebug_coverage_file *file; xdebug_coverage_function *function; if (XG_COV(previous_filename) && zend_string_equals(XG_COV(previous_filename), filename) == 0) { file = XG_COV(previous_file); } else { /* Check if the file already exists in the hash */ if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) { /* The file does not exist, so we add it to the hash */ file = xdebug_coverage_file_ctor(filename); xdebug_hash_add(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), file); } zend_string_release(XG_COV(previous_filename)); XG_COV(previous_filename) = zend_string_copy(file->name); XG_COV(previous_file) = file; } /* Check if the function already exists in the hash */ if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) { /* The file does not exist, so we add it to the hash */ function = xdebug_coverage_function_ctor(function_name); xdebug_hash_add(file->functions, function_name, strlen(function_name), function); } if (branch_info) { file->has_branch_info = 1; } function->branch_info = branch_info; } xdebug-3.4.3/src/coverage/branch_info.h0000664000175000017500000001057615011062311017267 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_BRANCH_INFO_H__ #define __HAVE_XDEBUG_BRANCH_INFO_H__ #include "zend_compile.h" #include "lib/hash.h" #include "lib/set.h" #include "lib/str.h" #if ZEND_USE_ABS_JMP_ADDR # define XDEBUG_ZNODE_JMP_LINE(node, opline, base) (int32_t)(((long)((node).jmp_addr) - (long)(base_address)) / sizeof(zend_op)) #else # define XDEBUG_ZNODE_JMP_LINE(node, opline, base) (int32_t)(((int32_t)((node).jmp_offset) / sizeof(zend_op)) + (opline)) #endif #define XDEBUG_JMP_NOT_SET (INT_MAX-1) #define XDEBUG_JMP_EXIT (INT_MAX-2) #define XDEBUG_BRANCH_MAX_OUTS 64 typedef struct _xdebug_branch { unsigned int start_lineno; unsigned int end_lineno; unsigned int end_op; unsigned char hit; unsigned int outs_count; int outs[XDEBUG_BRANCH_MAX_OUTS]; unsigned char outs_hit[XDEBUG_BRANCH_MAX_OUTS]; } xdebug_branch; typedef struct _xdebug_path { unsigned int elements_count; unsigned int elements_size; unsigned int *elements; unsigned char hit; } xdebug_path; /* Contains information for paths that belong to a set of branches (as stored in xdebug_branch_info) */ typedef struct _xdebug_path_info { unsigned int paths_count; /* The number of collected paths */ unsigned int paths_size; /* The amount of slots allocated for storing paths */ xdebug_path **paths; /* An array of possible paths */ xdebug_hash *path_hash; /* A hash where each path's key is the sequence of followed branches, pointing to a path in the paths array */ } xdebug_path_info; /* Contains all the branch information for a specific function */ typedef struct _xdebug_branch_info { unsigned int size; /* The number of stored branches */ xdebug_set *entry_points; /* A set that contains all the entry points into the function */ xdebug_set *starts; /* A set of opcodes nrs where each branch starts */ xdebug_set *ends; /* A set of opcodes nrs where each ends starts */ xdebug_branch *branches; /* Information about each branch */ xdebug_path_info path_info; /* The paths that can be created out of these branches */ } xdebug_branch_info; xdebug_branch_info *xdebug_branch_info_create(unsigned int size); void xdebug_branch_info_update(xdebug_branch_info *branch_info, unsigned int pos, unsigned int lineno, unsigned int outidx, unsigned int jump_pos); void xdebug_branch_post_process(zend_op_array *opa, xdebug_branch_info *branch_info); void xdebug_branch_find_paths(xdebug_branch_info *branch_info); void xdebug_branch_info_dump(zend_op_array *opa, xdebug_branch_info *branch_info); void xdebug_branch_info_add_branches_and_paths(zend_string *filename, char *function_name, xdebug_branch_info *branch_info); void xdebug_branch_info_free(xdebug_branch_info *branch_info); xdebug_path *xdebug_path_new(xdebug_path *old_path); void xdebug_path_free(xdebug_path *path); xdebug_path_info *xdebug_path_info_ctor(void); void xdebug_path_info_dtor(xdebug_path_info *path_info); void xdebug_path_info_add_path_for_level(xdebug_path_info *path_info, xdebug_path *path, unsigned int level); xdebug_path *xdebug_path_info_get_path_for_level(xdebug_path_info *path_info, unsigned int level); void xdebug_create_key_for_path(xdebug_path *path, xdebug_str *str); void xdebug_branch_info_mark_reached(zend_string *filename, char *function_name, zend_op_array *op_array, long opcode_nr); void xdebug_branch_info_mark_end_of_function_reached(zend_string *filename, char *function_name, char *key, int key_len); #endif xdebug-3.4.3/src/coverage/code_coverage.c0000664000175000017500000011742215011062311017575 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "zend_extensions.h" #include "branch_info.h" #include "code_coverage_private.h" #include "base/base.h" #include "base/filter.h" #include "lib/compat.h" #include "lib/set.h" #include "lib/var.h" #include "tracing/tracing.h" /* True globals */ int zend_xdebug_filter_offset = -1; int zend_xdebug_cc_run_offset = -1; extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); static void xdebug_coverage_line_dtor(void *data) { xdebug_coverage_line *line = (xdebug_coverage_line *) data; xdfree(line); } xdebug_coverage_file *xdebug_coverage_file_ctor(zend_string *filename) { xdebug_coverage_file *file; file = xdmalloc(sizeof(xdebug_coverage_file)); file->name = zend_string_copy(filename); file->lines = xdebug_hash_alloc(128, xdebug_coverage_line_dtor); file->functions = xdebug_hash_alloc(128, xdebug_coverage_function_dtor); file->has_branch_info = 0; return file; } static void xdebug_coverage_file_dtor(void *data) { xdebug_coverage_file *file = (xdebug_coverage_file *) data; xdebug_hash_destroy(file->lines); xdebug_hash_destroy(file->functions); zend_string_release(file->name); xdfree(file); } xdebug_coverage_function *xdebug_coverage_function_ctor(char *function_name) { xdebug_coverage_function *function; function = xdmalloc(sizeof(xdebug_coverage_function)); function->name = xdstrdup(function_name); function->branch_info = NULL; return function; } void xdebug_coverage_function_dtor(void *data) { xdebug_coverage_function *function = (xdebug_coverage_function *) data; if (function->branch_info) { xdebug_branch_info_free(function->branch_info); } xdfree(function->name); xdfree(function); } static void xdebug_func_format(char *buffer, size_t buffer_size, xdebug_func *func) { if (func->type == XFUNC_NORMAL) { int len = ZSTR_LEN(func->function); if (len + 1 > buffer_size) { goto error; } memcpy(buffer, ZSTR_VAL(func->function), len); buffer[len] = '\0'; return; } if (func->type == XFUNC_MEMBER) { int func_len = ZSTR_LEN(func->function); int len = ZSTR_LEN(func->object_class) + 2 + func_len; if (len + 1 > buffer_size) { goto error; } memcpy(buffer, ZSTR_VAL(func->object_class), ZSTR_LEN(func->object_class)); memcpy(buffer + ZSTR_LEN(func->object_class), "->", 2); memcpy(buffer + ZSTR_LEN(func->object_class) + 2, ZSTR_VAL(func->function), func_len); buffer[len] = '\0'; return; } error: memcpy(buffer, "?", 1); buffer[1] = '\0'; } static void xdebug_build_fname_from_oparray(xdebug_func *tmp, zend_op_array *opa) { int wrapped = 0; memset(tmp, 0, sizeof(xdebug_func)); if (opa->function_name) { if (opa->fn_flags & ZEND_ACC_CLOSURE) { tmp->function = xdebug_wrap_closure_location_around_function_name(opa, opa->function_name); wrapped = 1; } else if ( opa->fn_flags & ZEND_ACC_TRAIT_CLONE || (opa->scope && opa->scope->ce_flags & ZEND_ACC_TRAIT) ) { tmp->function = xdebug_wrap_location_around_function_name("trait-method", opa, opa->function_name); wrapped = 1; } else { tmp->function = zend_string_copy(opa->function_name); } } else { tmp->function = ZSTR_INIT_LITERAL("{main}", false); tmp->type = XFUNC_MAIN; } if (opa->scope && !wrapped) { tmp->type = XFUNC_MEMBER; tmp->object_class = zend_string_copy(opa->scope->name); } else { tmp->type = XFUNC_NORMAL; } } static void xdebug_print_opcode_info(zend_execute_data *execute_data, const zend_op *cur_opcode) { zend_op_array *op_array = &execute_data->func->op_array; xdebug_func func_info; char function_name[1024]; long opnr = execute_data->opline - execute_data->func->op_array.opcodes; xdebug_build_fname_from_oparray(&func_info, op_array); xdebug_func_format(function_name, sizeof(function_name), &func_info); if (func_info.object_class) { zend_string_release(func_info.object_class); } if (func_info.scope_class) { zend_string_release(func_info.scope_class); } if (func_info.function) { zend_string_release(func_info.function); } xdebug_branch_info_mark_reached(op_array->filename, function_name, op_array, opnr); } static int xdebug_check_branch_entry_handler(XDEBUG_OPCODE_HANDLER_ARGS) { zend_op_array *op_array = &execute_data->func->op_array; const zend_op *cur_opcode = execute_data->opline; if (!op_array->reserved[XG_COV(code_coverage_filter_offset)] && XG_COV(code_coverage_active)) { xdebug_print_opcode_info(execute_data, cur_opcode); } return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } static void xdebug_count_line(zend_string *filename, int lineno, int executable, int deadcode) { xdebug_coverage_file *file; xdebug_coverage_line *line; if (XG_COV(previous_filename) && zend_string_equals(XG_COV(previous_filename), filename)) { file = XG_COV(previous_file); } else { /* Check if the file already exists in the hash */ if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) { /* The file does not exist, so we add it to the hash */ file = xdebug_coverage_file_ctor(filename); xdebug_hash_add(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), file); } if (XG_COV(previous_filename)) { zend_string_release(XG_COV(previous_filename)); } XG_COV(previous_filename) = zend_string_copy(file->name); XG_COV(previous_file) = file; } /* Check if the line already exists in the hash */ if (!xdebug_hash_index_find(file->lines, lineno, (void *) &line)) { line = xdmalloc(sizeof(xdebug_coverage_line)); line->lineno = lineno; line->count = 0; line->executable = 0; xdebug_hash_index_add(file->lines, lineno, line); } if (executable) { if (line->executable != 1 && deadcode) { line->executable = 2; } else { line->executable = 1; } } else { line->count++; } } static int xdebug_common_override_handler(XDEBUG_OPCODE_HANDLER_ARGS) { zend_op_array *op_array = &execute_data->func->op_array; const zend_op *cur_opcode = execute_data->opline; if (!op_array->reserved[XG_COV(code_coverage_filter_offset)] && XG_COV(code_coverage_active)) { int lineno; lineno = cur_opcode->lineno; xdebug_print_opcode_info(execute_data, cur_opcode); xdebug_count_line(op_array->filename, lineno, 0, 0); } return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } static int xdebug_coverage_include_or_eval_handler(XDEBUG_OPCODE_HANDLER_ARGS) { zend_op_array *op_array = &execute_data->func->op_array; const zend_op *opline = execute_data->opline; xdebug_coverage_record_if_active(execute_data, op_array); return xdebug_call_original_opcode_handler_if_set(opline->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } static void prefill_from_opcode(zend_string *filename, zend_op opcode, int deadcode) { if ( opcode.opcode != ZEND_NOP && opcode.opcode != ZEND_EXT_NOP && opcode.opcode != ZEND_RECV && opcode.opcode != ZEND_RECV_INIT && opcode.opcode != ZEND_OP_DATA && opcode.opcode != ZEND_TICKS && opcode.opcode != ZEND_FAST_CALL && opcode.opcode != ZEND_RECV_VARIADIC #if PHP_VERSION_ID >= 80400 && opcode.opcode != ZEND_FREE #endif ) { xdebug_count_line(filename, opcode.lineno, 1, deadcode); } } #define XDEBUG_ZNODE_ELEM(node,var) node.var #if PHP_VERSION_ID < 80200 static zend_always_inline bool xdebug_string_equals_cstr(const zend_string *s1, const char *s2, size_t s2_length) { return ZSTR_LEN(s1) == s2_length && !memcmp(ZSTR_VAL(s1), s2, s2_length); } # define xdebug_string_equals_literal(str, literal) xdebug_string_equals_cstr(str, "" literal, sizeof(literal) - 1) #else # define xdebug_string_equals_literal zend_string_equals_literal #endif static int xdebug_find_jumps(zend_op_array *opa, unsigned int position, size_t *jump_count, int *jumps) { #if ZEND_USE_ABS_JMP_ADDR zend_op *base_address = &(opa->opcodes[0]); #endif zend_op opcode = opa->opcodes[position]; if (opcode.opcode == ZEND_JMP) { jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op1, position, base_address); *jump_count = 1; return 1; } else if ( opcode.opcode == ZEND_JMPZ || opcode.opcode == ZEND_JMPNZ || opcode.opcode == ZEND_JMPZ_EX || opcode.opcode == ZEND_JMPNZ_EX ) { jumps[0] = position + 1; jumps[1] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address); *jump_count = 2; return 1; #if PHP_VERSION_ID < 80200 } else if (opcode.opcode == ZEND_JMPZNZ) { jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address); jumps[1] = position + ((int32_t) opcode.extended_value / (int32_t) sizeof(zend_op)); *jump_count = 2; return 1; #endif } else if (opcode.opcode == ZEND_FE_FETCH_R || opcode.opcode == ZEND_FE_FETCH_RW) { jumps[0] = position + 1; jumps[1] = position + (opcode.extended_value / sizeof(zend_op)); *jump_count = 2; return 1; } else if (opcode.opcode == ZEND_FE_RESET_R || opcode.opcode == ZEND_FE_RESET_RW) { jumps[0] = position + 1; jumps[1] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address); *jump_count = 2; return 1; } else if (opcode.opcode == ZEND_CATCH) { *jump_count = 2; jumps[0] = position + 1; if (!(opcode.extended_value & ZEND_LAST_CATCH)) { jumps[1] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address); if (jumps[1] == jumps[0]) { jumps[1] = XDEBUG_JMP_NOT_SET; *jump_count = 1; } } else { jumps[1] = XDEBUG_JMP_EXIT; } return 1; } else if (opcode.opcode == ZEND_GOTO) { jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op1, position, base_address); *jump_count = 1; return 1; } else if (opcode.opcode == ZEND_FAST_CALL) { jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op1, position, base_address); jumps[1] = position + 1; *jump_count = 2; return 1; } else if (opcode.opcode == ZEND_FAST_RET) { jumps[0] = XDEBUG_JMP_EXIT; *jump_count = 1; return 1; #if PHP_VERSION_ID >= 80400 } else if (opcode.opcode == ZEND_JMP_FRAMELESS) { jumps[0] = position + 1; jumps[1] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address); *jump_count = 2; return 1; #endif } else if ( opcode.opcode == ZEND_GENERATOR_RETURN || #if PHP_VERSION_ID < 80400 opcode.opcode == ZEND_EXIT || #endif opcode.opcode == ZEND_THROW || opcode.opcode == ZEND_MATCH_ERROR || opcode.opcode == ZEND_RETURN ) { jumps[0] = XDEBUG_JMP_EXIT; *jump_count = 1; return 1; } else if ( opcode.opcode == ZEND_INIT_FCALL ) { zval *func_name = RT_CONSTANT(&opa->opcodes[position], opcode.op2); if (xdebug_string_equals_literal(Z_PTR_P(func_name), "exit")) { int level = 0; uint32_t start = position + 1; for (;;) { switch (opa->opcodes[start].opcode) { case ZEND_INIT_FCALL: case ZEND_INIT_FCALL_BY_NAME: case ZEND_INIT_NS_FCALL_BY_NAME: case ZEND_INIT_DYNAMIC_CALL: case ZEND_INIT_USER_CALL: case ZEND_INIT_METHOD_CALL: case ZEND_INIT_STATIC_METHOD_CALL: #if PHP_VERSION_ID >= 80400 case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: #endif case ZEND_NEW: level++; break; case ZEND_DO_FCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_ICALL: case ZEND_DO_UCALL: if (level == 0) { goto done; } level--; break; } start++; } done: ZEND_ASSERT(opa->opcodes[start].opcode == ZEND_DO_ICALL); jumps[0] = XDEBUG_JMP_EXIT; *jump_count = 1; return 1; } } else if ( opcode.opcode == ZEND_MATCH || opcode.opcode == ZEND_SWITCH_LONG || opcode.opcode == ZEND_SWITCH_STRING ) { zval *array_value; HashTable *myht; zval *val; array_value = RT_CONSTANT(&opa->opcodes[position], opcode.op2); myht = Z_ARRVAL_P(array_value); /* All 'case' statements */ ZEND_HASH_FOREACH_VAL_IND(myht, val) { if (*jump_count < XDEBUG_BRANCH_MAX_OUTS - 2) { jumps[*jump_count] = position + (val->value.lval / sizeof(zend_op)); (*jump_count)++; } } ZEND_HASH_FOREACH_END(); /* The 'default' case */ jumps[*jump_count] = position + (opcode.extended_value / sizeof(zend_op)); (*jump_count)++; if (opcode.opcode != ZEND_MATCH) { /* The 'next' opcode */ jumps[*jump_count] = position + 1; (*jump_count)++; } return 1; } return 0; } static void xdebug_analyse_branch(zend_op_array *opa, unsigned int position, xdebug_set *set, xdebug_branch_info *branch_info) { /* fprintf(stderr, "Branch analysis from position: %d\n", position); */ if (branch_info) { xdebug_set_add(branch_info->starts, position); branch_info->branches[position].start_lineno = opa->opcodes[position].lineno; } /* First we see if the branch has been visited, if so we bail out. */ if (xdebug_set_in(set, position)) { return; } /* fprintf(stderr, "XDEBUG Adding %d\n", position); */ /* Loop over the opcodes until the end of the array, or until a jump point has been found */ xdebug_set_add(set, position); while (position < opa->last) { size_t jump_count = 0; int jumps[XDEBUG_BRANCH_MAX_OUTS]; size_t i; /* See if we have a jump instruction */ if (xdebug_find_jumps(opa, position, &jump_count, jumps)) { for (i = 0; i < jump_count; i++) { if (jumps[i] == XDEBUG_JMP_EXIT || jumps[i] != XDEBUG_JMP_NOT_SET) { if (branch_info) { xdebug_branch_info_update(branch_info, position, opa->opcodes[position].lineno, i, jumps[i]); } if (jumps[i] != XDEBUG_JMP_EXIT) { xdebug_analyse_branch(opa, jumps[i], set, branch_info); } } } break; } /* See if we have a throw instruction */ if (opa->opcodes[position].opcode == ZEND_THROW) { /* fprintf(stderr, "Throw found at %d\n", position); */ if (branch_info) { xdebug_set_add(branch_info->ends, position); branch_info->branches[position].start_lineno = opa->opcodes[position].lineno; } break; } #if PHP_VERSION_ID < 80400 /* See if we have an exit instruction */ if (opa->opcodes[position].opcode == ZEND_EXIT) { /* fprintf(stderr, "X* Return found\n"); */ if (branch_info) { xdebug_set_add(branch_info->ends, position); branch_info->branches[position].start_lineno = opa->opcodes[position].lineno; } break; } #endif /* See if we have a return instruction */ if ( opa->opcodes[position].opcode == ZEND_RETURN || opa->opcodes[position].opcode == ZEND_RETURN_BY_REF ) { /*(fprintf(stderr, "XDEBUG Return found\n");)*/ if (branch_info) { xdebug_set_add(branch_info->ends, position); branch_info->branches[position].start_lineno = opa->opcodes[position].lineno; } break; } position++; /*(fprintf(stderr, "XDEBUG Adding %d\n", position);)*/ xdebug_set_add(set, position); } } static void xdebug_analyse_oparray(zend_op_array *opa, xdebug_set *set, xdebug_branch_info *branch_info) { unsigned int position = 0; while (position < opa->last) { if (position == 0) { xdebug_analyse_branch(opa, position, set, branch_info); if (branch_info) { xdebug_set_add(branch_info->entry_points, position); } } else if (opa->opcodes[position].opcode == ZEND_CATCH) { xdebug_analyse_branch(opa, position, set, branch_info); if (branch_info) { xdebug_set_add(branch_info->entry_points, position); } } position++; } if (branch_info) { xdebug_set_add(branch_info->ends, opa->last-1); branch_info->branches[opa->last-1].start_lineno = opa->opcodes[opa->last-1].lineno; } } static void prefill_from_oparray(zend_string *filename, zend_op_array *op_array) { unsigned int i; xdebug_set *set = NULL; xdebug_branch_info *branch_info = NULL; op_array->reserved[XG_COV(dead_code_analysis_tracker_offset)] = (void*) XG_COV(dead_code_last_start_id); /* Check for abstract methods and simply return from this function in those * cases. */ if (op_array->fn_flags & ZEND_ACC_ABSTRACT) { return; } /* Check whether this function should be filtered out */ { /* function_stack_entry tmp_fse; tmp_fse.filename = STR_NAME_VAL(op_array->filename); xdebug_build_fname_from_oparray(&tmp_fse.function, op_array); printf(" - PREFIL FILTERED FOR %s (%s::%s): %s\n", tmp_fse.filename, tmp_fse.function.class, tmp_fse.function.function, op_array->reserved[XG_COV(code_coverage_filter_offset)] ? "YES" : "NO"); */ if (op_array->reserved[XG_COV(code_coverage_filter_offset)]) { return; } } /* Run dead code analysis if requested */ if (XG_COV(code_coverage_dead_code_analysis) && (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) { set = xdebug_set_create(op_array->last); if (XG_COV(code_coverage_branch_check)) { branch_info = xdebug_branch_info_create(op_array->last); } xdebug_analyse_oparray(op_array, set, branch_info); } /* The normal loop then finally */ for (i = 0; i < op_array->last; i++) { zend_op opcode = op_array->opcodes[i]; prefill_from_opcode(filename, opcode, set ? !xdebug_set_in(set, i) : 0); } if (set) { xdebug_set_free(set); } if (branch_info) { char function_name[1024]; xdebug_func func_info; xdebug_build_fname_from_oparray(&func_info, op_array); xdebug_func_format(function_name, sizeof(function_name), &func_info); if (func_info.object_class) { zend_string_release(func_info.object_class); } if (func_info.scope_class) { zend_string_release(func_info.scope_class); } if (func_info.function) { zend_string_release(func_info.function); } xdebug_branch_post_process(op_array, branch_info); xdebug_branch_find_paths(branch_info); xdebug_branch_info_add_branches_and_paths(filename, (char*) function_name, branch_info); } #if PHP_VERSION_ID >= 80100 if (!op_array->num_dynamic_func_defs) { return; } for (i = 0; i < op_array->num_dynamic_func_defs; i++) { prefill_from_oparray(filename, op_array->dynamic_func_defs[i]); } #endif } static int prefill_from_function_table(zend_op_array *opa) { if (opa->type == ZEND_USER_FUNCTION) { if ((long) opa->reserved[XG_COV(dead_code_analysis_tracker_offset)] < XG_COV(dead_code_last_start_id)) { prefill_from_oparray(opa->filename, opa); } } return ZEND_HASH_APPLY_KEEP; } /* Set correct int format to use */ #if SIZEOF_ZEND_LONG == 4 # define XDEBUG_PTR_KEY_LEN 8 # define XDEBUG_PTR_KEY_FMT "%08X" #else # define XDEBUG_PTR_KEY_LEN 16 # define XDEBUG_PTR_KEY_FMT "%016lX" #endif static int prefill_from_class_table(zend_class_entry *ce) { if (ce->type == ZEND_USER_CLASS) { zend_op_array *val; ZEND_HASH_FOREACH_PTR(&ce->function_table, val) { prefill_from_function_table(val); } ZEND_HASH_FOREACH_END(); } return ZEND_HASH_APPLY_KEEP; } static void xdebug_prefill_code_coverage(zend_op_array *op_array) { zend_op_array *function_op_array; zend_class_entry *class_entry; if ((long) op_array->reserved[XG_COV(dead_code_analysis_tracker_offset)] < XG_COV(dead_code_last_start_id)) { prefill_from_oparray(op_array->filename, op_array); } ZEND_HASH_REVERSE_FOREACH_PTR(CG(function_table), function_op_array) { if (_idx == XG_COV(prefill_function_count)) { break; } prefill_from_function_table(function_op_array); } ZEND_HASH_FOREACH_END(); XG_COV(prefill_function_count) = CG(function_table)->nNumUsed; ZEND_HASH_REVERSE_FOREACH_PTR(CG(class_table), class_entry) { if (_idx == XG_COV(prefill_class_count)) { break; } prefill_from_class_table(class_entry); } ZEND_HASH_FOREACH_END(); XG_COV(prefill_class_count) = CG(class_table)->nNumUsed; } void xdebug_code_coverage_start_of_function(zend_op_array *op_array, char *function_name) { xdebug_path *path = xdebug_path_new(NULL); int orig_size = XG_COV(branches).size; xdebug_prefill_code_coverage(op_array); xdebug_path_info_add_path_for_level(XG_COV(paths_stack), path, XDEBUG_VECTOR_COUNT(XG_BASE(stack))); if (orig_size == 0 || XDEBUG_VECTOR_COUNT(XG_BASE(stack)) >= orig_size) { size_t i = 0; XG_COV(branches).size = XDEBUG_VECTOR_COUNT(XG_BASE(stack)) + 32; XG_COV(branches).last_branch_nr = realloc(XG_COV(branches).last_branch_nr, sizeof(int) * XG_COV(branches.size)); for (i = orig_size; i < XG_COV(branches).size; i++) { XG_COV(branches).last_branch_nr[i] = -1; } } XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))] = -1; } void xdebug_code_coverage_end_of_function(zend_op_array *op_array, zend_string *filename, char *function_name) { xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_path *path = xdebug_path_info_get_path_for_level(XG_COV(paths_stack), XDEBUG_VECTOR_COUNT(XG_BASE(stack))); if (!path) { return; } if (path->elements) { xdebug_create_key_for_path(path, &str); xdebug_branch_info_mark_end_of_function_reached(filename, function_name, str.d, str.l); xdfree(str.d); } xdebug_path_free(path); } PHP_FUNCTION(xdebug_start_code_coverage) { zend_long options = 0; if (!XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { php_error(E_WARNING, "Code coverage needs to be enabled in php.ini by setting 'xdebug.mode' to 'coverage'"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &options) == FAILURE) { return; } XG_COV(code_coverage_unused) = (options & XDEBUG_CC_OPTION_UNUSED); XG_COV(code_coverage_dead_code_analysis) = (options & XDEBUG_CC_OPTION_DEAD_CODE); XG_COV(code_coverage_branch_check) = (options & XDEBUG_CC_OPTION_BRANCH_CHECK); XG_COV(code_coverage_active) = 1; RETURN_TRUE; } PHP_FUNCTION(xdebug_stop_code_coverage) { zend_bool cleanup = 1; if (!XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &cleanup) == FAILURE) { return; } if (!XG_COV(code_coverage_active)) { RETURN_FALSE; } if (cleanup) { if (XG_COV(previous_filename)) { zend_string_release(XG_COV(previous_filename)); } XG_COV(previous_filename) = NULL; XG_COV(previous_file) = NULL; if (XG_COV(previous_mark_filename)) { zend_string_release(XG_COV(previous_mark_filename)); } XG_COV(previous_mark_filename) = NULL; XG_COV(previous_mark_file) = NULL; xdebug_hash_destroy(XG_COV(code_coverage_info)); XG_COV(code_coverage_info) = xdebug_hash_alloc(32, xdebug_coverage_file_dtor); XG_COV(dead_code_last_start_id)++; xdebug_path_info_dtor(XG_COV(paths_stack)); XG_COV(paths_stack) = xdebug_path_info_ctor(); } XG_COV(code_coverage_active) = 0; RETURN_TRUE; } static int xdebug_lineno_cmp(Bucket *f, Bucket *s) { if (f->h < s->h) { return -1; } else if (f->h > s->h) { return 1; } else { return 0; } } static void add_line(void *ret, xdebug_hash_element *e) { xdebug_coverage_line *line = (xdebug_coverage_line*) e->ptr; zval *retval = (zval*) ret; if (line->executable && (line->count == 0)) { add_index_long(retval, line->lineno, -line->executable); } else { add_index_long(retval, line->lineno, 1); } } static void add_branches(zval *retval, xdebug_branch_info *branch_info) { zval *branches, *branch, *out, *out_hit; unsigned int i; XDEBUG_MAKE_STD_ZVAL(branches); array_init(branches); for (i = 0; i < branch_info->starts->size; i++) { if (xdebug_set_in(branch_info->starts, i)) { size_t j = 0; XDEBUG_MAKE_STD_ZVAL(branch); array_init(branch); add_assoc_long(branch, "op_start", i); add_assoc_long(branch, "op_end", branch_info->branches[i].end_op); add_assoc_long(branch, "line_start", branch_info->branches[i].start_lineno); add_assoc_long(branch, "line_end", branch_info->branches[i].end_lineno); add_assoc_long(branch, "hit", branch_info->branches[i].hit); XDEBUG_MAKE_STD_ZVAL(out); array_init(out); for (j = 0; j < branch_info->branches[i].outs_count; j++) { if (branch_info->branches[i].outs[j]) { add_index_long(out, j, branch_info->branches[i].outs[j]); } } add_assoc_zval(branch, "out", out); XDEBUG_MAKE_STD_ZVAL(out_hit); array_init(out_hit); for (j = 0; j < branch_info->branches[i].outs_count; j++) { if (branch_info->branches[i].outs[j]) { add_index_long(out_hit, j, branch_info->branches[i].outs_hit[j]); } } add_assoc_zval(branch, "out_hit", out_hit); add_index_zval(branches, i, branch); efree(out_hit); efree(out); efree(branch); } } add_assoc_zval_ex(retval, "branches", HASH_KEY_SIZEOF("branches"), branches); efree(branches); } static void add_paths(zval *retval, xdebug_branch_info *branch_info) { zval *paths, *path, *path_container; unsigned int i, j; XDEBUG_MAKE_STD_ZVAL(paths); array_init(paths); for (i = 0; i < branch_info->path_info.paths_count; i++) { XDEBUG_MAKE_STD_ZVAL(path); array_init(path); XDEBUG_MAKE_STD_ZVAL(path_container); array_init(path_container); for (j = 0; j < branch_info->path_info.paths[i]->elements_count; j++) { add_next_index_long(path, branch_info->path_info.paths[i]->elements[j]); } add_assoc_zval(path_container, "path", path); add_assoc_long(path_container, "hit", branch_info->path_info.paths[i]->hit); add_next_index_zval(paths, path_container); efree(path_container); efree(path); } add_assoc_zval_ex(retval, "paths", HASH_KEY_SIZEOF("paths"), paths); efree(paths); } static void add_cc_function(void *ret, xdebug_hash_element *e) { xdebug_coverage_function *function = (xdebug_coverage_function*) e->ptr; zval *retval = (zval*) ret; zval *function_info; zend_string *trait_scope = NULL; XDEBUG_MAKE_STD_ZVAL(function_info); array_init(function_info); if (function->branch_info) { add_branches(function_info, function->branch_info); add_paths(function_info, function->branch_info); } if ((trait_scope = xdebug_get_trait_scope(function->name)) != NULL) { char *with_scope = xdebug_sprintf("%s->%s", ZSTR_VAL(trait_scope), function->name); add_assoc_zval_ex(retval, with_scope, strlen(with_scope), function_info); } else { add_assoc_zval_ex(retval, function->name, HASH_KEY_STRLEN(function->name), function_info); } efree(function_info); } static void add_file(void *ret, xdebug_hash_element *e) { xdebug_coverage_file *file = (xdebug_coverage_file*) e->ptr; zval *retval = (zval*) ret; zval *lines, *functions, *file_info; HashTable *target_hash; /* Add all the lines */ XDEBUG_MAKE_STD_ZVAL(lines); array_init(lines); xdebug_hash_apply(file->lines, (void *) lines, add_line); /* Sort on linenumber */ target_hash = HASH_OF(lines); zend_hash_sort(target_hash, xdebug_lineno_cmp, 0); /* Add the branch and path info */ if (XG_COV(code_coverage_branch_check)) { XDEBUG_MAKE_STD_ZVAL(file_info); array_init(file_info); XDEBUG_MAKE_STD_ZVAL(functions); array_init(functions); xdebug_hash_apply(file->functions, (void *) functions, add_cc_function); add_assoc_zval_ex(file_info, "lines", HASH_KEY_SIZEOF("lines"), lines); add_assoc_zval_ex(file_info, "functions", HASH_KEY_SIZEOF("functions"), functions); add_assoc_zval_ex(retval, ZSTR_VAL(file->name), ZSTR_LEN(file->name), file_info); efree(functions); efree(file_info); } else { add_assoc_zval_ex(retval, ZSTR_VAL(file->name), ZSTR_LEN(file->name), lines); } efree(lines); } PHP_FUNCTION(xdebug_get_code_coverage) { array_init(return_value); if (!XG_COV(code_coverage_info)) { return; } xdebug_hash_apply(XG_COV(code_coverage_info), (void *) return_value, add_file); } PHP_FUNCTION(xdebug_get_function_count) { RETURN_LONG(XG_BASE(function_count)); } PHP_FUNCTION(xdebug_code_coverage_started) { RETURN_BOOL(XG_COV(code_coverage_active)); } void xdebug_init_coverage_globals(xdebug_coverage_globals_t *xg) { xg->previous_filename = NULL; xg->previous_file = NULL; xg->previous_mark_filename = NULL; xg->previous_mark_file = NULL; xg->paths_stack = NULL; xg->branches.size = 0; xg->branches.last_branch_nr = NULL; xg->code_coverage_active = 0; /* Get reserved offset */ xg->dead_code_analysis_tracker_offset = zend_xdebug_cc_run_offset; xg->dead_code_last_start_id = 1; xg->code_coverage_filter_offset = zend_xdebug_filter_offset; } void xdebug_coverage_count_line_if_active(zend_op_array *op_array, zend_string *file, int lineno) { if (XG_COV(code_coverage_active) && !op_array->reserved[XG_COV(code_coverage_filter_offset)]) { xdebug_count_line(file, lineno, 0, 0); } } void xdebug_coverage_count_line_if_branch_check_active(zend_op_array *op_array, zend_string *file, int lineno) { if (XG_COV(code_coverage_active) && XG_COV(code_coverage_branch_check)) { xdebug_coverage_count_line_if_active(op_array, file, lineno); } } void xdebug_coverage_record_if_active(zend_execute_data *execute_data, zend_op_array *op_array) { if (!op_array->reserved[XG_COV(code_coverage_filter_offset)] && XG_COV(code_coverage_active)) { xdebug_print_opcode_info(execute_data, execute_data->opline); } } void xdebug_coverage_compile_file(zend_op_array *op_array) { if (XG_COV(code_coverage_active) && XG_COV(code_coverage_unused) && (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) { xdebug_prefill_code_coverage(op_array); } } int xdebug_coverage_execute_ex(function_stack_entry *fse, zend_op_array *op_array, zend_string **tmp_filename, char **tmp_function_name) { xdebug_func func_info; if (!fse->filtered_code_coverage && XG_COV(code_coverage_active) && XG_COV(code_coverage_unused)) { char buffer[1024]; *tmp_filename = zend_string_copy(op_array->filename); xdebug_build_fname_from_oparray(&func_info, op_array); xdebug_func_format(buffer, sizeof(buffer), &func_info); *tmp_function_name = xdstrdup(buffer); xdebug_code_coverage_start_of_function(op_array, *tmp_function_name); if (func_info.object_class) { zend_string_release(func_info.object_class); } if (func_info.scope_class) { zend_string_release(func_info.scope_class); } if (func_info.function) { zend_string_release(func_info.function); } return 1; } return 0; } void xdebug_coverage_execute_ex_end(function_stack_entry *fse, zend_op_array *op_array, zend_string *tmp_filename, char *tmp_function_name) { /* Check which path has been used */ if (!fse->filtered_code_coverage && XG_COV(code_coverage_active) && XG_COV(code_coverage_unused)) { xdebug_code_coverage_end_of_function(op_array, tmp_filename, tmp_function_name); } xdfree(tmp_function_name); zend_string_release(tmp_filename); } void xdebug_coverage_init_oparray(zend_op_array *op_array) { function_stack_entry tmp_fse; if (XG_BASE(filter_type_code_coverage) == XDEBUG_FILTER_NONE) { op_array->reserved[XG_COV(dead_code_analysis_tracker_offset)] = 0; return; } tmp_fse.filename = op_array->filename; xdebug_build_fname_from_oparray(&tmp_fse.function, op_array); xdebug_filter_run_internal(&tmp_fse, XDEBUG_FILTER_CODE_COVERAGE, &tmp_fse.filtered_code_coverage, XG_BASE(filter_type_code_coverage), XG_BASE(filters_code_coverage)); xdebug_func_dtor_by_ref(&tmp_fse.function); op_array->reserved[XG_COV(code_coverage_filter_offset)] = (void*) (size_t) tmp_fse.filtered_code_coverage; } static int xdebug_switch_handler(XDEBUG_OPCODE_HANDLER_ARGS) { const zend_op *cur_opcode = execute_data->opline; if (!XG_COV(code_coverage_active)) { return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } execute_data->opline++; return ZEND_USER_OPCODE_CONTINUE; } void xdebug_coverage_minit(INIT_FUNC_ARGS) { int i; /* Get reserved offsets */ zend_xdebug_cc_run_offset = zend_get_resource_handle(XDEBUG_NAME); zend_xdebug_filter_offset = zend_get_resource_handle(XDEBUG_NAME); xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN, xdebug_common_override_handler); xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_DIM, xdebug_common_override_handler); xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_OBJ, xdebug_common_override_handler); xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_STATIC_PROP, xdebug_common_override_handler); xdebug_register_with_opcode_multi_handler(ZEND_QM_ASSIGN, xdebug_common_override_handler); xdebug_register_with_opcode_multi_handler(ZEND_INCLUDE_OR_EVAL, xdebug_coverage_include_or_eval_handler); /* Overload opcodes for code coverage */ xdebug_set_opcode_handler(ZEND_JMP, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_JMPZ, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_JMPZ_EX, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_JMPNZ, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_IS_IDENTICAL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_IS_NOT_IDENTICAL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_IS_EQUAL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_IS_NOT_EQUAL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_IS_SMALLER, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_IS_SMALLER_OR_EQUAL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_BOOL_NOT, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_ADD, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SUB, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_MUL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_DIV, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_ADD_ARRAY_ELEMENT, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_RETURN, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_RETURN_BY_REF, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_EXT_STMT, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SEND_VAR, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SEND_VAR_NO_REF, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SEND_VAR_NO_REF_EX, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SEND_REF, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SEND_VAL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SEND_VAL_EX, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SEND_VAR_EX, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_NEW, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_EXT_FCALL_BEGIN, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_INIT_METHOD_CALL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_INIT_STATIC_METHOD_CALL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_INIT_FCALL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_INIT_NS_FCALL_BY_NAME, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_CATCH, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_BOOL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_INIT_ARRAY, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_DIM_R, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_DIM_W, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_OBJ_R, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_OBJ_W, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_OBJ_FUNC_ARG, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_DIM_FUNC_ARG, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_STATIC_PROP_FUNC_ARG, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_DIM_UNSET, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_OBJ_UNSET, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_CLASS, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_CONSTANT, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FETCH_CLASS_CONSTANT, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_CONCAT, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FAST_CONCAT, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_ISSET_ISEMPTY_DIM_OBJ, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_ISSET_ISEMPTY_PROP_OBJ, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_CASE, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_DECLARE_LAMBDA_FUNCTION, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_INSTANCEOF, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FAST_RET, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_ROPE_ADD, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_ROPE_END, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_COALESCE, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_TYPE_CHECK, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_GENERATOR_CREATE, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_BIND_STATIC, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_BIND_LEXICAL, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_DECLARE_CLASS, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_DECLARE_CLASS_DELAYED, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_SWITCH_STRING, xdebug_switch_handler); xdebug_set_opcode_handler(ZEND_SWITCH_LONG, xdebug_switch_handler); #if PHP_VERSION_ID >= 80400 xdebug_set_opcode_handler(ZEND_FRAMELESS_ICALL_0, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FRAMELESS_ICALL_1, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FRAMELESS_ICALL_2, xdebug_common_override_handler); xdebug_set_opcode_handler(ZEND_FRAMELESS_ICALL_3, xdebug_common_override_handler); #endif /* Override all the other opcodes so that we can mark when we hit a branch * start one */ for (i = 0; i < 256; i++) { if (i == ZEND_HANDLE_EXCEPTION) { continue; } if (!xdebug_isset_opcode_handler(i)) { xdebug_set_opcode_handler(i, xdebug_check_branch_entry_handler); } } } void xdebug_coverage_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("XDEBUG_CC_UNUSED", XDEBUG_CC_OPTION_UNUSED, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_CC_DEAD_CODE", XDEBUG_CC_OPTION_DEAD_CODE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_CC_BRANCH_CHECK", XDEBUG_CC_OPTION_BRANCH_CHECK, CONST_CS | CONST_PERSISTENT); } void xdebug_coverage_rinit(void) { XG_COV(code_coverage_active) = 0; XG_COV(code_coverage_info) = xdebug_hash_alloc(32, xdebug_coverage_file_dtor); XG_COV(dead_code_analysis_tracker_offset) = zend_xdebug_cc_run_offset; XG_COV(dead_code_last_start_id) = 1; XG_COV(code_coverage_filter_offset) = zend_xdebug_filter_offset; XG_COV(previous_filename) = NULL; XG_COV(previous_file) = NULL; XG_COV(prefill_function_count) = 0; XG_COV(prefill_class_count) = 0; /* Initialize visited classes and branches hash */ XG_COV(visited_branches) = xdebug_hash_alloc(2048, NULL); XG_COV(paths_stack) = xdebug_path_info_ctor(); XG_COV(branches).size = 0; XG_COV(branches).last_branch_nr = NULL; } void xdebug_coverage_post_deactivate(void) { XG_COV(code_coverage_active) = 0; xdebug_hash_destroy(XG_COV(code_coverage_info)); XG_COV(code_coverage_info) = NULL; xdebug_hash_destroy(XG_COV(visited_branches)); XG_COV(visited_branches) = NULL; /* Clean up path coverage array */ if (XG_COV(paths_stack)) { xdebug_path_info_dtor(XG_COV(paths_stack)); XG_COV(paths_stack) = NULL; } if (XG_COV(branches).last_branch_nr) { free(XG_COV(branches).last_branch_nr); XG_COV(branches).last_branch_nr = NULL; XG_COV(branches).size = 0; } if (XG_COV(previous_filename)) { zend_string_release(XG_COV(previous_filename)); XG_COV(previous_filename) = NULL; } if (XG_COV(previous_mark_filename)) { zend_string_release(XG_COV(previous_mark_filename)); XG_COV(previous_mark_filename) = NULL; } } xdebug-3.4.3/src/coverage/code_coverage.h0000664000175000017500000000665015011062311017602 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_CODE_COVERAGE_H__ #define __XDEBUG_CODE_COVERAGE_H__ #include "lib/lib.h" typedef struct xdebug_coverage_file { zend_string *name; xdebug_hash *lines; xdebug_hash *functions; /* Used for branch coverage */ int has_branch_info; } xdebug_coverage_file; typedef struct _xdebug_coverage_globals_t { zend_bool code_coverage_active; /* Whether code coverage is currently running */ xdebug_hash *code_coverage_info; /* Stores code coverage information */ zend_bool code_coverage_unused; zend_bool code_coverage_dead_code_analysis; zend_bool code_coverage_branch_check; int dead_code_analysis_tracker_offset; long dead_code_last_start_id; long code_coverage_filter_offset; size_t prefill_function_count; size_t prefill_class_count; zend_string *previous_filename; xdebug_coverage_file *previous_file; zend_string *previous_mark_filename; xdebug_coverage_file *previous_mark_file; xdebug_path_info *paths_stack; xdebug_hash *visited_branches; struct { unsigned int size; int *last_branch_nr; } branches; } xdebug_coverage_globals_t; typedef struct _xdebug_coverage_settings_t { int dummy; } xdebug_coverage_settings_t; void xdebug_init_coverage_globals(xdebug_coverage_globals_t *xg); void xdebug_coverage_count_line_if_active(zend_op_array *op_array, zend_string *file, int lineno); void xdebug_coverage_count_line_if_branch_check_active(zend_op_array *op_array, zend_string *file, int lineno); void xdebug_coverage_record_if_active(zend_execute_data *execute_data, zend_op_array *op_array); void xdebug_coverage_compile_file(zend_op_array *op_array); int xdebug_coverage_execute_ex(function_stack_entry *fse, zend_op_array *op_array, zend_string **tmp_filename, char **tmp_function_name); void xdebug_coverage_execute_ex_end(function_stack_entry *fse, zend_op_array *op_array, zend_string *tmp_filename, char *tmp_function_name); void xdebug_coverage_init_oparray(zend_op_array *op_array); void xdebug_coverage_minit(INIT_FUNC_ARGS); void xdebug_coverage_mshutdown(void); void xdebug_coverage_rinit(void); void xdebug_coverage_post_deactivate(void); void xdebug_coverage_register_constants(INIT_FUNC_ARGS); PHP_FUNCTION(xdebug_start_code_coverage); PHP_FUNCTION(xdebug_stop_code_coverage); PHP_FUNCTION(xdebug_get_code_coverage); PHP_FUNCTION(xdebug_code_coverage_started); PHP_FUNCTION(xdebug_get_function_count); #endif xdebug-3.4.3/src/coverage/code_coverage_private.h0000664000175000017500000000421515011062311021327 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_CODE_COVERAGE_PRIVATE_H__ #define __XDEBUG_CODE_COVERAGE_PRIVATE_H__ #include "lib/php-header.h" #include "branch_info.h" #include "code_coverage.h" #include "lib/compat.h" #include "lib/hash.h" #include "lib/mm.h" typedef struct xdebug_coverage_line { int lineno; int count; int executable; } xdebug_coverage_line; typedef struct xdebug_coverage_function { char *name; xdebug_branch_info *branch_info; } xdebug_coverage_function; #define XG_COV(v) (XG(globals.coverage.v)) #define XINI_COV(v) (XG(settings.coverage.v)) xdebug_coverage_file *xdebug_coverage_file_ctor(zend_string *filename); xdebug_coverage_function *xdebug_coverage_function_ctor(char *function_name); void xdebug_coverage_function_dtor(void *data); void xdebug_code_coverage_start_of_function(zend_op_array *op_array, char *function_name); void xdebug_code_coverage_end_of_function(zend_op_array *op_array, zend_string *file_name, char *function_name); PHP_FUNCTION(xdebug_start_code_coverage); PHP_FUNCTION(xdebug_stop_code_coverage); PHP_FUNCTION(xdebug_get_code_coverage); PHP_FUNCTION(xdebug_code_coverage_started); PHP_FUNCTION(xdebug_get_function_count); #endif xdebug-3.4.3/src/develop/develop.c0000664000175000017500000001157715011062311016315 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "zend_exceptions.h" #include "php_xdebug.h" #include "monitor.h" #include "stack.h" #include "superglobals.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) /* True global for overloaded var_dump */ zif_handler orig_var_dump_func; static int xdebug_silence_handler(XDEBUG_OPCODE_HANDLER_ARGS); static void xdebug_develop_overloaded_functions_setup(void) { zend_function *orig; /* Override var_dump with our own function */ orig = zend_hash_str_find_ptr(CG(function_table), "var_dump", sizeof("var_dump") - 1); orig_var_dump_func = orig->internal_function.handler; orig->internal_function.handler = zif_xdebug_var_dump; } void xdebug_init_develop_globals(xdebug_develop_globals_t *xg) { xg->do_monitor_functions = 0; xg->in_at = 0; /* scream */ xdebug_llist_init(&xg->server, xdebug_superglobals_dump_dtor); xdebug_llist_init(&xg->get, xdebug_superglobals_dump_dtor); xdebug_llist_init(&xg->post, xdebug_superglobals_dump_dtor); xdebug_llist_init(&xg->cookie, xdebug_superglobals_dump_dtor); xdebug_llist_init(&xg->files, xdebug_superglobals_dump_dtor); xdebug_llist_init(&xg->env, xdebug_superglobals_dump_dtor); xdebug_llist_init(&xg->request, xdebug_superglobals_dump_dtor); xdebug_llist_init(&xg->session, xdebug_superglobals_dump_dtor); } void xdebug_deinit_develop_globals(xdebug_develop_globals_t *xg) { xdebug_llist_empty(&xg->server, NULL); xdebug_llist_empty(&xg->get, NULL); xdebug_llist_empty(&xg->post, NULL); xdebug_llist_empty(&xg->cookie, NULL); xdebug_llist_empty(&xg->files, NULL); xdebug_llist_empty(&xg->env, NULL); xdebug_llist_empty(&xg->request, NULL); xdebug_llist_empty(&xg->session, NULL); } void xdebug_develop_minit(INIT_FUNC_ARGS) { /* Overload opcodes for 'scream' */ xdebug_set_opcode_handler(ZEND_BEGIN_SILENCE, xdebug_silence_handler); xdebug_set_opcode_handler(ZEND_END_SILENCE, xdebug_silence_handler); REGISTER_LONG_CONSTANT("XDEBUG_STACK_NO_DESC", XDEBUG_STACK_NO_DESC, CONST_CS | CONST_PERSISTENT); xdebug_develop_overloaded_functions_setup(); } void xdebug_develop_mshutdown() { } void xdebug_develop_rinit() { int i; XG_DEV(collected_errors) = xdebug_llist_alloc(xdebug_llist_string_dtor); /* Function monitoring */ XG_DEV(do_monitor_functions) = 0; XG_DEV(functions_to_monitor) = NULL; XG_DEV(monitored_functions_found) = xdebug_llist_alloc(xdebug_monitored_function_dtor); /* Admin for last exception trace */ XG_DEV(last_exception_trace).next_slot = 0; for (i = 0; i < XDEBUG_LAST_EXCEPTION_TRACE_SLOTS; i++) { XG_DEV(last_exception_trace).obj_ptr[i] = NULL; ZVAL_UNDEF(&XG_DEV(last_exception_trace).stack_trace[i]); } } void xdebug_develop_rshutdown() { int i; /* Admin for last exception trace */ XG_DEV(last_exception_trace).next_slot = 0; for (i = 0; i < XDEBUG_LAST_EXCEPTION_TRACE_SLOTS; i++) { if (XG_DEV(last_exception_trace).obj_ptr[i]) { XG_DEV(last_exception_trace).obj_ptr[i] = NULL; zval_ptr_dtor(&XG_DEV(last_exception_trace).stack_trace[i]); } } } void xdebug_develop_post_deactivate() { xdebug_llist_destroy(XG_DEV(collected_errors), NULL); XG_DEV(collected_errors) = NULL; xdebug_llist_destroy(XG_DEV(monitored_functions_found), NULL); XG_DEV(monitored_functions_found) = NULL; if (XG_DEV(functions_to_monitor)) { xdebug_hash_destroy(XG_DEV(functions_to_monitor)); XG_DEV(functions_to_monitor) = NULL; } } static int xdebug_silence_handler(XDEBUG_OPCODE_HANDLER_ARGS) { zend_op_array *op_array = &execute_data->func->op_array; const zend_op *cur_opcode = execute_data->opline; if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { xdebug_coverage_record_if_active(execute_data, op_array); } if (XINI_DEV(do_scream)) { execute_data->opline++; if (cur_opcode->opcode == ZEND_BEGIN_SILENCE) { XG_DEV(in_at) = 1; } else { XG_DEV(in_at) = 0; } return ZEND_USER_OPCODE_CONTINUE; } return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } xdebug-3.4.3/src/develop/develop.h0000664000175000017500000000544715011062311016321 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_DEVELOP_H__ #define __XDEBUG_DEVELOP_H__ #define XDEBUG_LAST_EXCEPTION_TRACE_SLOTS 8 typedef struct _xdebug_develop_globals_t { /* used for function monitoring */ zend_bool do_monitor_functions; xdebug_hash *functions_to_monitor; xdebug_llist *monitored_functions_found; /* List of functions found */ /* superglobals */ xdebug_llist server; xdebug_llist get; xdebug_llist post; xdebug_llist cookie; xdebug_llist files; xdebug_llist env; xdebug_llist request; xdebug_llist session; /* used for collection errors */ xdebug_llist *collected_errors; /* scream */ zend_bool in_at; /* last exception stack trace */ struct { int next_slot; zend_object *obj_ptr[XDEBUG_LAST_EXCEPTION_TRACE_SLOTS]; zval stack_trace[XDEBUG_LAST_EXCEPTION_TRACE_SLOTS]; } last_exception_trace; } xdebug_develop_globals_t; typedef struct _xdebug_develop_settings_t { zend_long max_stack_frames; zend_bool show_ex_trace; zend_bool show_error_trace; zend_bool show_local_vars; zend_bool force_display_errors; zend_long force_error_reporting; zend_long halt_level; zend_long cli_color; /* superglobals */ zend_bool dump_globals; zend_bool dump_once; zend_bool dump_undefined; /* scream */ zend_bool do_scream; } xdebug_develop_settings_t; void xdebug_init_develop_globals(xdebug_develop_globals_t *xg); void xdebug_deinit_develop_globals(xdebug_develop_globals_t *xg); void xdebug_develop_minit(INIT_FUNC_ARGS); void xdebug_develop_mshutdown(); void xdebug_develop_rinit(); void xdebug_develop_post_deactivate(); void xdebug_develop_rshutdown(); void xdebug_develop_throw_exception_hook(zend_object *exception, zval *file, zval *line, zval *code, char *code_str, zval *message); void xdebug_monitor_handler(function_stack_entry *fse); #endif xdebug-3.4.3/src/develop/develop_private.h0000664000175000017500000000226615011062311020047 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_DEVELOP_PRIVATE_H__ #define __XDEBUG_DEVELOP_PRIVATE_H__ PHP_FUNCTION(xdebug_var_dump); #define XG_DEV(v) (XG(globals.develop.v)) #define XINI_DEV(v) (XG(settings.develop.v)) #endif xdebug-3.4.3/src/develop/monitor.c0000664000175000017500000001270515011062311016340 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "php_xdebug.h" #include "develop_private.h" #include "monitor.h" #include "lib/compat.h" #include "lib/hash.h" #include "lib/var.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) static void init_function_monitor_hash(xdebug_hash *internal, HashTable *functions_to_monitor) { zval *val; ZEND_HASH_FOREACH_VAL(functions_to_monitor, val) { if (Z_TYPE_P(val) == IS_STRING) { xdebug_hash_add(internal, Z_STRVAL_P(val), Z_STRLEN_P(val), xdstrdup(Z_STRVAL_P(val))); } } ZEND_HASH_FOREACH_END(); } static void xdebug_hash_function_monitor_dtor(char *function) { xdfree(function); } static xdebug_monitored_function_entry *xdebug_monitored_function_init(char *func_name, zend_string *filename, int lineno) { xdebug_monitored_function_entry *tmp = xdmalloc(sizeof(xdebug_monitored_function_entry)); tmp->func_name = xdstrdup(func_name); tmp->filename = zend_string_copy(filename); tmp->lineno = lineno; return tmp; } void xdebug_monitored_function_dtor(void *dummy, void *elem) { xdebug_monitored_function_entry *mfe = (xdebug_monitored_function_entry*) elem; xdfree(mfe->func_name); zend_string_release(mfe->filename); xdfree(mfe); } void xdebug_function_monitor_record(char *func_name, zend_string *filename, int lineno) { xdebug_monitored_function_entry *record; record = xdebug_monitored_function_init(func_name, filename, lineno); xdebug_llist_insert_next(XG_DEV(monitored_functions_found), XDEBUG_LLIST_TAIL(XG_DEV(monitored_functions_found)), record); } void xdebug_monitor_handler(function_stack_entry *fse) { char *func_name = NULL; int func_name_len = 0; void *dummy = NULL; if (!XG_DEV(do_monitor_functions)) { return; } func_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT); func_name_len = strlen(func_name); if (xdebug_hash_find(XG_DEV(functions_to_monitor), func_name, func_name_len, (void *) &dummy)) { xdebug_function_monitor_record(func_name, fse->filename, fse->lineno); } xdfree(func_name); } PHP_FUNCTION(xdebug_start_function_monitor) { HashTable *functions_to_monitor; if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'"); return; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "H", &functions_to_monitor) == FAILURE) { return; } if (XG_DEV(do_monitor_functions) == 1) { php_error(E_NOTICE, "Function monitoring was already started"); } /* Clean and store list of functions to monitor */ if (XG_DEV(functions_to_monitor)) { xdebug_hash_destroy(XG_DEV(functions_to_monitor)); } /* We add "1" here so that we don't alloc a 0-slot hash table */ XG_DEV(functions_to_monitor) = xdebug_hash_alloc(zend_hash_num_elements(functions_to_monitor) + 1, (xdebug_hash_dtor_t) xdebug_hash_function_monitor_dtor); init_function_monitor_hash(XG_DEV(functions_to_monitor), functions_to_monitor); /* Disable opcache's optimizer as it prevents some optimized internal functions from being monitored */ xdebug_disable_opcache_optimizer(); XG_DEV(do_monitor_functions) = 1; } PHP_FUNCTION(xdebug_stop_function_monitor) { if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'"); return; } if (XG_DEV(do_monitor_functions) == 0) { php_error(E_NOTICE, "Function monitoring was not started"); } XG_DEV(do_monitor_functions) = 0; } PHP_FUNCTION(xdebug_get_monitored_functions) { xdebug_llist_element *le; zend_bool clear = 0; xdebug_monitored_function_entry *mfe; if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'"); array_init(return_value); return; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &clear) == FAILURE) { return; } array_init(return_value); for (le = XDEBUG_LLIST_HEAD(XG_DEV(monitored_functions_found)); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { zval *entry; mfe = XDEBUG_LLIST_VALP(le); XDEBUG_MAKE_STD_ZVAL(entry); array_init(entry); add_assoc_string_ex(entry, "function", HASH_KEY_SIZEOF("function"), mfe->func_name); add_assoc_string_ex(entry, "filename", HASH_KEY_SIZEOF("filename"), ZSTR_VAL(mfe->filename)); add_assoc_long(entry, "lineno", mfe->lineno); add_next_index_zval(return_value, entry); efree(entry); } if (clear) { xdebug_llist_destroy(XG_DEV(monitored_functions_found), NULL); XG_DEV(monitored_functions_found) = xdebug_llist_alloc(xdebug_monitored_function_dtor); } } /* }}} */ xdebug-3.4.3/src/develop/monitor.h0000664000175000017500000000241315011062311016340 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_MONITOR_H__ #define __HAVE_XDEBUG_MONITOR_H__ typedef struct xdebug_monitored_function_entry { char *func_name; zend_string *filename; int lineno; } xdebug_monitored_function_entry; void xdebug_monitored_function_dtor(void *dummy, void *elem); #endif xdebug-3.4.3/src/develop/php_functions.c0000664000175000017500000002331715011062311017531 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "php_xdebug.h" #include "develop_private.h" #include "stack.h" #include "lib/lib_private.h" #include "lib/var_export_html.h" #include "lib/var_export_line.h" #include "lib/var_export_text.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) #define MODE_MUST_BE(m,n) \ if (!XDEBUG_MODE_IS((m))) { \ php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to '%s'", (n)); \ return; \ } /* {{{ proto void xdebug_var_dump(mixed var [, ...] ) Outputs a fancy string representation of a variable */ PHP_FUNCTION(xdebug_var_dump) { zval *args; int argc; int i; xdebug_str *val; argc = ZEND_NUM_ARGS(); args = safe_emalloc(argc, sizeof(zval), 0); if (ZEND_NUM_ARGS() == 0 || zend_get_parameters_array_ex(argc, args) == FAILURE) { efree(args); WRONG_PARAM_COUNT; } for (i = 0; i < argc; i++) { if (PG(html_errors)) { val = xdebug_get_zval_value_html(NULL, (zval*) &args[i], 0, NULL); PHPWRITE(val->d, val->l); xdebug_str_free(val); } else if ((XINI_DEV(cli_color) == 1 && xdebug_is_output_tty()) || (XINI_DEV(cli_color) == 2)) { val = xdebug_get_zval_value_ansi((zval*) &args[i], 0, NULL); PHPWRITE(val->d, val->l); xdebug_str_free(val); } else { val = xdebug_get_zval_value_text((zval*) &args[i], 0, NULL); PHPWRITE(val->d, val->l); xdebug_str_free(val); } } efree(args); } /* }}} */ /* {{{ proto void xdebug_debug_zval(mixed var [, ...] ) Outputs a fancy string representation of a variable */ PHP_FUNCTION(xdebug_debug_zval) { zval *args; int argc; int i; xdebug_str *val; argc = ZEND_NUM_ARGS(); args = safe_emalloc(argc, sizeof(zval), 0); if (ZEND_NUM_ARGS() == 0 || zend_get_parameters_array_ex(argc, args) == FAILURE) { efree(args); WRONG_PARAM_COUNT; } if (!(ZEND_CALL_INFO(EG(current_execute_data)->prev_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_rebuild_symbol_table(); } for (i = 0; i < argc; i++) { if (Z_TYPE(args[i]) == IS_STRING) { zval debugzval; xdebug_str *tmp_name; xdebug_lib_set_active_symbol_table(EG(current_execute_data)->prev_execute_data->symbol_table); xdebug_lib_set_active_data(EG(current_execute_data)->prev_execute_data); tmp_name = xdebug_str_create(Z_STRVAL(args[i]), Z_STRLEN(args[i])); xdebug_get_php_symbol(&debugzval, tmp_name); xdebug_str_free(tmp_name); /* Reduce refcount for dumping */ Z_TRY_DELREF(debugzval); php_printf("%s: ", Z_STRVAL(args[i])); if (Z_TYPE(debugzval) != IS_UNDEF) { if (PG(html_errors)) { val = xdebug_get_zval_value_html(NULL, &debugzval, 1, NULL); PHPWRITE(val->d, val->l); } else if ((XINI_DEV(cli_color) == 1 && xdebug_is_output_tty()) || (XINI_DEV(cli_color) == 2)) { val = xdebug_get_zval_value_ansi(&debugzval, 1, NULL); PHPWRITE(val->d, val->l); } else { val = xdebug_get_zval_value_line(&debugzval, 1, NULL); PHPWRITE(val->d, val->l); } xdebug_str_free(val); PHPWRITE("\n", 1); } else { PHPWRITE("no such symbol\n", 15); } /* Restore original refcount */ Z_TRY_ADDREF(debugzval); zval_ptr_dtor_nogc(&debugzval); } } efree(args); } /* }}} */ /* {{{ proto void xdebug_debug_zval_stdout(mixed var [, ...] ) Outputs a fancy string representation of a variable */ PHP_FUNCTION(xdebug_debug_zval_stdout) { zval *args; int argc; int i; argc = ZEND_NUM_ARGS(); args = safe_emalloc(argc, sizeof(zval), 0); if (ZEND_NUM_ARGS() == 0 || zend_get_parameters_array_ex(argc, args) == FAILURE) { efree(args); WRONG_PARAM_COUNT; } if (!(ZEND_CALL_INFO(EG(current_execute_data)->prev_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_rebuild_symbol_table(); } for (i = 0; i < argc; i++) { if (Z_TYPE(args[i]) == IS_STRING) { zval debugzval; xdebug_str *tmp_name; xdebug_str *val; xdebug_lib_set_active_symbol_table(EG(current_execute_data)->prev_execute_data->symbol_table); xdebug_lib_set_active_data(EG(current_execute_data)->prev_execute_data); tmp_name = xdebug_str_create(Z_STRVAL(args[i]), Z_STRLEN(args[i])); xdebug_get_php_symbol(&debugzval, tmp_name); xdebug_str_free(tmp_name); /* Reduce refcount for dumping */ Z_TRY_DELREF(debugzval); printf("%s: ", Z_STRVAL(args[i])); if (Z_TYPE(debugzval) != IS_UNDEF) { val = xdebug_get_zval_value_line(&debugzval, 1, NULL); printf("%s(%zd)", val->d, val->l); xdebug_str_free(val); printf("\n"); } else { printf("no such symbol\n\n"); } /* Restore original refcount */ Z_TRY_ADDREF(debugzval); zval_ptr_dtor_nogc(&debugzval); } } efree(args); } /* }}} */ PHP_FUNCTION(xdebug_start_error_collection) { MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (XG_LIB(do_collect_errors) == 1) { php_error(E_NOTICE, "Error collection was already started"); } XG_LIB(do_collect_errors) = 1; } PHP_FUNCTION(xdebug_stop_error_collection) { MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (XG_LIB(do_collect_errors) == 0) { php_error(E_NOTICE, "Error collection was not started"); } XG_LIB(do_collect_errors) = 0; } PHP_FUNCTION(xdebug_memory_usage) { RETURN_LONG(zend_memory_usage(0)); } PHP_FUNCTION(xdebug_peak_memory_usage) { RETURN_LONG(zend_memory_peak_usage(0)); } PHP_FUNCTION(xdebug_time_index) { if (!XDEBUG_MODE_IS((XDEBUG_MODE_DEVELOP))) { php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'"); RETURN_DOUBLE(0.0); } RETURN_DOUBLE(XDEBUG_SECONDS_SINCE_START(xdebug_get_nanotime())); } /* {{{ proto void xdebug_print_function_stack([string message [, int options]) Displays a stack trace */ PHP_FUNCTION(xdebug_print_function_stack) { char *message = NULL; size_t message_len; function_stack_entry *fse; char *tmp; zend_long options = 0; MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sl", &message, &message_len, &options) == FAILURE) { return; } fse = xdebug_get_stack_frame(0); if (message) { tmp = xdebug_get_printable_stack(PG(html_errors), 0, message, ZSTR_VAL(fse->filename), fse->lineno, !(options & XDEBUG_STACK_NO_DESC)); } else { tmp = xdebug_get_printable_stack(PG(html_errors), 0, "user triggered", ZSTR_VAL(fse->filename), fse->lineno, !(options & XDEBUG_STACK_NO_DESC)); } php_printf("%s", tmp); xdfree(tmp); } /* }}} */ /* {{{ proto string xdebug_call_class() Returns the name of the calling class */ PHP_FUNCTION(xdebug_call_class) { function_stack_entry *fse; zend_long depth = 2; MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &depth) == FAILURE) { return; } fse = xdebug_get_stack_frame(depth); if (!fse) { return; } if (!fse->function.object_class) { RETURN_FALSE; } RETURN_STR_COPY(fse->function.object_class); } /* }}} */ /* {{{ proto string xdebug_call_function() Returns the function name from which the current function was called from. */ PHP_FUNCTION(xdebug_call_function) { function_stack_entry *fse; zend_long depth = 2; MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &depth) == FAILURE) { return; } fse = xdebug_get_stack_frame(depth); if (!fse) { return; } if (!fse->function.function) { RETURN_FALSE; } RETURN_STR_COPY(fse->function.function); } /* }}} */ /* {{{ proto int xdebug_call_line() Returns the line number where the current function was called from. */ PHP_FUNCTION(xdebug_call_line) { function_stack_entry *fse; zend_long depth = 2; MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &depth) == FAILURE) { return; } fse = xdebug_get_stack_frame(depth); if (!fse) { return; } RETURN_LONG(fse->lineno); } /* }}} */ /* {{{ proto string xdebug_call_file() Returns the filename where the current function was called from. */ PHP_FUNCTION(xdebug_call_file) { function_stack_entry *fse; zend_long depth = 2; MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &depth) == FAILURE) { return; } fse = xdebug_get_stack_frame(depth); if (!fse) { return; } RETURN_STR_COPY(fse->filename); } /* }}} */ PHP_FUNCTION(xdebug_get_collected_errors) { xdebug_llist_element *le; char *string; zend_bool clear = 0; MODE_MUST_BE(XDEBUG_MODE_DEVELOP, "develop"); if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &clear) == FAILURE) { return; } array_init(return_value); for (le = XDEBUG_LLIST_HEAD(XG_DEV(collected_errors)); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { string = XDEBUG_LLIST_VALP(le); add_next_index_string(return_value, string); } if (!clear) { return; } xdebug_llist_destroy(XG_DEV(collected_errors), NULL); XG_DEV(collected_errors) = xdebug_llist_alloc(xdebug_llist_string_dtor); } xdebug-3.4.3/src/develop/stack.c0000664000175000017500000012545215011062311015762 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "main/php_ini.h" #include "ext/standard/html.h" #include "ext/standard/php_smart_string.h" #include "zend_exceptions.h" #include "zend_generators.h" #include "monitor.h" #include "stack.h" #include "superglobals.h" #include "base/filter.h" #include "coverage/code_coverage.h" #include "lib/compat.h" #include "lib/lib_private.h" #include "lib/str.h" #include "lib/var_export_html.h" #include "lib/var_export_line.h" #include "profiler/profiler.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) static const char* text_formats[22] = { "\n", "%s: %s in %s on line %d\n", "\nCall Stack:\n", "%10.4F %10ld %3d. %s(", "'%s'", ") %s:%d\n", "\n\nVariables in local scope (#%d):\n", "\n", " $%s = %s\n", " $%s = *uninitialized*\n", "SCREAM: Error suppression ignored for\n", NULL, NULL, // 13+ (for xdebug_append_printable_stack_from_zval) "\n%sCall Stack:\n", "", "%sThe stack is empty or not available\n", "%s%10.4F %10ld %3d. %s() %s:%d\n", "\n%s", "\n%sNested Exceptions:\n", "", // nested exceptions footer NULL, // alternative to 16 for html only "\t" // indenter }; static const char* ansi_formats[22] = { "\n", "%s: %s in %s on line %d\n", "\nCall Stack:\n", "%10.4F %10ld %3d. %s(", "'%s'", ") %s:%d\n", "\n\nVariables in local scope (#%d):\n", "\n", " $%s = %s\n", " $%s = *uninitialized*\n", "SCREAM: Error suppression ignored for\n", NULL, NULL, // 13+ (for xdebug_append_printable_stack_from_zval) "\n%sCall Stack:\n", "", "%sThe stack is empty or not available\n", "%s%10.4F %10ld %3d. %s() %s:%d\n", "\n%s", "\n%sNested Exceptions:\n", "", // nested exceptions footer NULL, // alternative to 16 for html only "\t" // indenter }; static const char* html_formats[22] = { "
\n\n", "\n", "\n\n", "\n", "\n", "
( ! ) %s: %s in %s on line %d
Call Stack
#TimeMemoryFunctionLocation
%d%.4F%ld%s( ", "'%s'", " )%s:%d
Variables in local scope (#%d)
\n", "
$%s =
%s\n", "
$%s =
Undefined\n", " )%s:%d\n", "( ! ) %s: %s in %s on line %d\n", "( ! ) SCREAM: Error suppression ignored for\n", // 13+ (for xdebug_append_printable_stack_from_zval) "%sCall Stack\n#TimeMemoryFunctionLocation\n", "", "%sThe stack is empty or not available\n", "%s%d%.4F%ld%s()%s:%d\n", "%s\n", "%s
Nested Exceptions
\n", "
\n", // nested exceptions footer "%d%.4F%ld%s()%s:%d\n", "" // indenter (not used for HTML) }; static const char** select_formats(int html) { if (html) { return html_formats; } else if ((XINI_DEV(cli_color) == 1 && xdebug_is_output_tty()) || (XINI_DEV(cli_color) == 2)) { return ansi_formats; } else { return text_formats; } } void xdebug_log_stack(const char *error_type_str, char *buffer, const char *error_filename, const int error_lineno) { char *tmp_log_message; int i; function_stack_entry *fse; tmp_log_message = xdebug_sprintf( "PHP %s: %s in %s on line %d", error_type_str, buffer, error_filename, error_lineno); php_log_err(tmp_log_message); xdfree(tmp_log_message); if (!XG_BASE(stack) || XDEBUG_VECTOR_COUNT(XG_BASE(stack)) < 1) { return; } fse = XDEBUG_VECTOR_HEAD(XG_BASE(stack)); php_log_err((char*) "PHP Stack trace:"); for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)); i++, fse++) { int c = 0; /* Comma flag */ unsigned int j = 0; /* Counter */ char *tmp_name; xdebug_str log_buffer = XDEBUG_STR_INITIALIZER; int variadic_opened = 0; int sent_variables = fse->varc; if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) { sent_variables--; } tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT); xdebug_str_add_fmt(&log_buffer, "PHP %3d. %s(", fse->level, tmp_name); xdfree(tmp_name); /* Printing vars */ for (j = 0; j < sent_variables; j++) { xdebug_str *tmp_value; if (c) { xdebug_str_add_literal(&log_buffer, ", "); } else { c = 1; } if (fse->var[j].is_variadic) { xdebug_str_add_literal(&log_buffer, "..."); variadic_opened = 1; } if (fse->var[j].name) { xdebug_str_add_fmt(&log_buffer, "$%s = ", ZSTR_VAL(fse->var[j].name)); } if (fse->var[j].is_variadic) { xdebug_str_add_literal(&log_buffer, "variadic("); c = 0; continue; } if (!Z_ISUNDEF(fse->var[j].data)) { tmp_value = xdebug_get_zval_value_line(&fse->var[j].data, 0, NULL); xdebug_str_add_str(&log_buffer, tmp_value); xdebug_str_free(tmp_value); } else { xdebug_str_add_literal(&log_buffer, "*uninitialized*"); } } if (variadic_opened) { xdebug_str_add_literal(&log_buffer, ")"); } xdebug_str_add_fmt(&log_buffer, ") %s:%d", ZSTR_VAL(fse->filename), fse->lineno); php_log_err(log_buffer.d); xdebug_str_destroy(&log_buffer); } } void xdebug_append_error_head(xdebug_str *str, int html, const char *error_type_str) { const char **formats = select_formats(html); if (html) { xdebug_str_add_fmt(str, formats[0], error_type_str, XG_DEV(in_at) ? " xe-scream" : ""); if (XG_DEV(in_at)) { xdebug_str_add_const(str, formats[12]); } } else { xdebug_str_add_const(str, formats[0]); if (XG_DEV(in_at)) { xdebug_str_add_const(str, formats[10]); } } } void xdebug_append_error_description(xdebug_str *str, int html, const char *error_type_str, const char *buffer, const char *error_filename, const int error_lineno) { const char **formats = select_formats(html); char *escaped; if (!html) { escaped = estrdup(buffer); } else { zend_string *tmp; char *first_closing = strchr(buffer, ']'); /* We do need to escape HTML entities here, as HTML chars could be in * the error message. However, PHP in some circumstances also adds an * HTML link to a manual page. That bit, we don't need to escape. So * this bit of code finds the portion that doesn't need escaping, adds * it to a tmp string, and then adds an HTML escaped string for the * rest of the original buffer. */ if (first_closing && strstr(buffer, "() [val); zend_string_free(tmp); } } if (strlen(XINI_LIB(file_link_format)) > 0 && html && strcmp(error_filename, "Unknown") != 0) { char *file_link; xdebug_format_file_link(&file_link, error_filename, error_lineno); xdebug_str_add_fmt(str, formats[11], error_type_str, escaped, file_link, error_filename, error_lineno); xdfree(file_link); } else { xdebug_str_add_fmt(str, formats[1], error_type_str, escaped, error_filename, error_lineno); } efree(escaped); } static void xdebug_append_error_description_from_object(xdebug_str *str, int html, zval *exception_obj) { zval *message, *file, *line; zval dummy; if (Z_TYPE_P(exception_obj) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(exception_obj), zend_ce_throwable)) { return; } message = zend_read_property(Z_OBJCE_P(exception_obj), Z_OBJ_P(exception_obj), "message", sizeof("message")-1, 1, &dummy); file = zend_read_property(Z_OBJCE_P(exception_obj), Z_OBJ_P(exception_obj), "file", sizeof("file")-1, 1, &dummy); line = zend_read_property(Z_OBJCE_P(exception_obj), Z_OBJ_P(exception_obj), "line", sizeof("line")-1, 1, &dummy); if (!message || !file || !line || Z_TYPE_P(message) != IS_STRING || Z_TYPE_P(file) != IS_STRING || Z_TYPE_P(line) != IS_LONG) { return; } xdebug_append_error_description(str, html, STR_NAME_VAL(Z_OBJCE_P(exception_obj)->name), Z_STRVAL_P(message), Z_STRVAL_P(file), Z_LVAL_P(line)); } static void xdebug_append_sub_header(xdebug_str *str, int html) { const char **formats = select_formats(html); xdebug_str_add_fmt(str, formats[17], formats[21]); } static void add_single_value(xdebug_str *str, zval *zv, int html) { xdebug_str *tmp_value = NULL; char *tmp_html_value = NULL; size_t newlen; if (html) { tmp_value = xdebug_get_zval_value_line(zv, 0, NULL); tmp_html_value = xdebug_xmlize(tmp_value->d, tmp_value->l, &newlen); xdebug_str_add_literal(str, ""); xdebug_str_add(str, tmp_html_value, 0); xdebug_str_add_literal(str, ""); xdebug_str_free(tmp_value); efree(tmp_html_value); } else { tmp_value = xdebug_get_zval_value_line(zv, 0, NULL); if (tmp_value) { xdebug_str_add_str(str, tmp_value); xdebug_str_free(tmp_value); } else { xdebug_str_add_literal(str, "???"); } } } static void zval_from_stack_add_frame_parameters(zval *frame, function_stack_entry *fse, bool params_as_values) { unsigned int j; zval *params; int variadic_opened = 0; int sent_variables = fse->varc; if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) { sent_variables--; } XDEBUG_MAKE_STD_ZVAL(params); array_init(params); add_assoc_zval_ex(frame, "params", HASH_KEY_SIZEOF("params"), params); for (j = 0; j < sent_variables; j++) { if (fse->var[j].is_variadic) { zval *vparams; XDEBUG_MAKE_STD_ZVAL(vparams); array_init(vparams); if (fse->var[j].name) { add_assoc_zval_ex(params, ZSTR_VAL(fse->var[j].name), ZSTR_LEN(fse->var[j].name), vparams); } else { add_index_zval(params, j, vparams); } efree(params); params = vparams; variadic_opened = 1; continue; } if (params_as_values) { /* Named parameters */ if (fse->var[j].name && !variadic_opened) { if (Z_TYPE(fse->var[j].data) == IS_UNDEF) { add_assoc_null_ex(params, ZSTR_VAL(fse->var[j].name), ZSTR_LEN(fse->var[j].name)); } else { Z_TRY_ADDREF(fse->var[j].data); add_assoc_zval_ex(params, ZSTR_VAL(fse->var[j].name), ZSTR_LEN(fse->var[j].name), &fse->var[j].data); } continue; } /* Unnamed or Variadic parameters */ if (Z_TYPE(fse->var[j].data) == IS_UNDEF) { add_index_null(params, j - variadic_opened); } else { Z_TRY_ADDREF(fse->var[j].data); add_index_zval(params, j - variadic_opened, &fse->var[j].data); } continue; } else { xdebug_str *argument = NULL; if (!Z_ISUNDEF(fse->var[j].data)) { argument = xdebug_get_zval_value_line(&fse->var[j].data, 0, NULL); } else { argument = xdebug_str_create_from_char((char*) "???"); } if (fse->var[j].name && !variadic_opened && argument) { add_assoc_stringl_ex(params, ZSTR_VAL(fse->var[j].name), ZSTR_LEN(fse->var[j].name), argument->d, argument->l); } else { add_index_stringl(params, j - variadic_opened, argument->d, argument->l); } if (argument) { xdebug_str_free(argument); argument = NULL; } } } efree(params); } static void zval_from_stack_add_frame_variables(zval *frame, zend_execute_data *edata, HashTable *symbols, zend_op_array *opa) { unsigned int j; zval variables; array_init(&variables); add_assoc_zval_ex(frame, "variables", HASH_KEY_SIZEOF("variables"), &variables); xdebug_lib_set_active_data(edata); xdebug_lib_set_active_symbol_table(symbols); for (j = 0; j < (unsigned int) opa->last_var; j++) { xdebug_str *symbol_name; zval *symbol; symbol_name = xdebug_str_create_from_char(opa->vars[j]->val); symbol = ZEND_CALL_VAR_NUM(xdebug_lib_get_active_data(), j); xdebug_str_free(symbol_name); if (Z_TYPE_P(symbol) == IS_UNDEF) { add_assoc_null_ex(&variables, opa->vars[j]->val, opa->vars[j]->len); } else { Z_TRY_ADDREF_P(symbol); add_assoc_zval_ex(&variables, opa->vars[j]->val, opa->vars[j]->len, symbol); } } } static void zval_from_stack_add_frame(zval *output, function_stack_entry *fse, zend_execute_data *edata, bool add_local_vars, bool params_as_values) { zval *frame; /* Initialize frame array */ XDEBUG_MAKE_STD_ZVAL(frame); array_init(frame); /* Add data */ add_assoc_double_ex(frame, "time", HASH_KEY_SIZEOF("time"), XDEBUG_SECONDS_SINCE_START(fse->nanotime)); add_assoc_long_ex(frame, "memory", HASH_KEY_SIZEOF("memory"), fse->memory); if (fse->function.function) { add_assoc_str_ex(frame, "function", HASH_KEY_SIZEOF("function"), zend_string_copy(fse->function.function)); } if (fse->function.object_class) { add_assoc_string_ex(frame, "type", HASH_KEY_SIZEOF("type"), (char*) (fse->function.type == XFUNC_STATIC_MEMBER ? "static" : "dynamic")); add_assoc_str_ex(frame, "class", HASH_KEY_SIZEOF("class"), zend_string_copy(fse->function.object_class)); } add_assoc_str_ex(frame, "file", HASH_KEY_SIZEOF("file"), zend_string_copy(fse->filename)); add_assoc_long_ex(frame, "line", HASH_KEY_SIZEOF("line"), fse->lineno); zval_from_stack_add_frame_parameters(frame, fse, params_as_values); if (add_local_vars && fse->op_array && fse->op_array->vars && (fse->function.type & XFUNC_INCLUDES) != XFUNC_INCLUDES) { zval_from_stack_add_frame_variables(frame, edata, fse->symbol_table, fse->op_array); } if (fse->function.include_filename) { add_assoc_str_ex(frame, "include_filename", HASH_KEY_SIZEOF("include_filename"), zend_string_copy(fse->function.include_filename)); } add_next_index_zval(output, frame); efree(frame); } static bool zval_from_stack(zval *output, bool add_local_vars, bool params_as_values) { function_stack_entry *fse, *next_fse; unsigned int i; array_init(output); fse = XDEBUG_VECTOR_HEAD(XG_BASE(stack)); /* In some situations, it is possible that there is no stack yet. This * happens in some cases when Datadog's trace is used. */ if (!fse) { return false; } next_fse = fse + 1; for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)) - 1; i++, fse++, next_fse++) { zval_from_stack_add_frame(output, fse, next_fse->execute_data, add_local_vars, params_as_values); } return true; } /* Helpers for last_exception_trace slots */ static zval *last_exception_find_trace(zend_object *obj) { int i; for (i = 0; i < XDEBUG_LAST_EXCEPTION_TRACE_SLOTS; i++) { if (obj == XG_DEV(last_exception_trace).obj_ptr[i]) { return &XG_DEV(last_exception_trace).stack_trace[i]; } } return NULL; } static zval *last_exception_get_slot(zend_object *obj) { int slot = XG_DEV(last_exception_trace).next_slot; if (XG_DEV(last_exception_trace).obj_ptr[slot] != NULL) { zval_ptr_dtor(&XG_DEV(last_exception_trace).stack_trace[slot]); XG_DEV(last_exception_trace).obj_ptr[slot] = NULL; } XG_DEV(last_exception_trace).obj_ptr[slot] = obj; XG_DEV(last_exception_trace).next_slot = (slot + 1 == XDEBUG_LAST_EXCEPTION_TRACE_SLOTS ? 0 : slot + 1); return &XG_DEV(last_exception_trace).stack_trace[slot]; } /* Formatting variables */ #define XDEBUG_VAR_FORMAT_INITIALISED 0 #define XDEBUG_VAR_FORMAT_UNINITIALISED 1 static const char* text_var_formats[2] = { " $%s = %s\n", " $%s = *uninitialized*\n", }; static const char* ansi_var_formats[2] = { " $%s = %s\n", " $%s = *uninitialized*\n", }; static const char* html_var_formats[2] = { "
$%s =
%s\n", "
$%s =
Undefined\n", }; static const char** get_var_format_string(int html) { if (html) { return html_var_formats; } else if ((XINI_DEV(cli_color) == 1 && xdebug_is_output_tty()) || (XINI_DEV(cli_color) == 2)) { return ansi_var_formats; } else { return text_var_formats; } } static void xdebug_dump_used_var_with_contents(void *htmlq, xdebug_hash_element* he, void *argument) { int html = *(int*) htmlq; zval zvar; xdebug_str *contents; xdebug_str *name = (xdebug_str*) he->ptr; HashTable *tmp_ht; const char **formats; xdebug_str *str = (xdebug_str *) argument; if (!he->ptr) { return; } /* Bail out on $this and $GLOBALS */ if (strcmp(name->d, "this") == 0 || strcmp(name->d, "GLOBALS") == 0) { return; } if (EG(current_execute_data) && !(ZEND_CALL_INFO(EG(current_execute_data)) & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_rebuild_symbol_table(); } tmp_ht = xdebug_lib_get_active_symbol_table(); { zend_execute_data *ex = EG(current_execute_data); while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) { ex = ex->prev_execute_data; } if (ex) { xdebug_lib_set_active_data(ex); xdebug_lib_set_active_symbol_table(ex->symbol_table); } } xdebug_get_php_symbol(&zvar, name); xdebug_lib_set_active_symbol_table(tmp_ht); formats = get_var_format_string(PG(html_errors)); if (Z_TYPE(zvar) == IS_UNDEF) { xdebug_str_add_fmt(str, formats[XDEBUG_VAR_FORMAT_UNINITIALISED], name->d); return; } if (html) { contents = xdebug_get_zval_value_html(NULL, &zvar, 0, NULL); } else { contents = xdebug_get_zval_value_line(&zvar, 0, NULL); } if (contents) { xdebug_str_add_fmt(str, formats[XDEBUG_VAR_FORMAT_INITIALISED], name->d, contents->d); } else { xdebug_str_add_fmt(str, formats[XDEBUG_VAR_FORMAT_UNINITIALISED], name->d); } if (contents) { xdebug_str_free(contents); } zval_ptr_dtor_nogc(&zvar); } void xdebug_append_printable_stack_from_zval(xdebug_str *str, bool indent, zval *trace, int html) { const char **formats = select_formats(html); zval *frame; int counter = 0; xdebug_str_add_fmt(str, formats[13], indent ? formats[21] : ""); // header if (!trace || Z_TYPE_P(trace) != IS_ARRAY) { xdebug_str_add_fmt(str, formats[15], indent ? formats[21] : ""); // message xdebug_str_add_const(str, formats[14]); // footer return; } ZEND_HASH_FOREACH_VAL_IND(HASH_OF(trace), frame) { zval *time, *memory, *class, *type, *function, *file, *line; char *combined_function; counter++; if (Z_TYPE_P(frame) != IS_ARRAY) { continue; } time = zend_hash_str_find(HASH_OF(frame), "time", 4); memory = zend_hash_str_find(HASH_OF(frame), "memory", 6); class = zend_hash_str_find(HASH_OF(frame), "class", 5); type = zend_hash_str_find(HASH_OF(frame), "type", 4); function = zend_hash_str_find(HASH_OF(frame), "function", 8); file = zend_hash_str_find(HASH_OF(frame), "file", 4); line = zend_hash_str_find(HASH_OF(frame), "line", 4); if (!time || !memory || !function || !file || !line) { continue; } if (Z_TYPE_P(time) != IS_DOUBLE || Z_TYPE_P(memory) != IS_LONG || Z_TYPE_P(function) != IS_STRING || Z_TYPE_P(file) != IS_STRING || Z_TYPE_P(line) != IS_LONG) { continue; } if (class && type && Z_TYPE_P(class) == IS_STRING && Z_TYPE_P(type) == IS_STRING) { combined_function = xdebug_sprintf("%s%s%s", Z_STRVAL_P(class), strcmp(Z_STRVAL_P(type), "static") == 0 ? "::" : "->", Z_STRVAL_P(function)); } else { combined_function = xdstrdup(Z_STRVAL_P(function)); } if (html) { char *formatted_filename; xdebug_format_filename(&formatted_filename, "...%s%n", Z_STR_P(file)); if (strlen(XINI_LIB(file_link_format)) > 0 && strcmp(Z_STRVAL_P(file), "Unknown") != 0) { char *file_link; xdebug_format_file_link(&file_link, Z_STRVAL_P(file), Z_LVAL_P(line)); xdebug_str_add_fmt(str, formats[16], formats[21], counter, Z_DVAL_P(time), Z_LVAL_P(memory), combined_function, Z_STRVAL_P(file), file_link, formatted_filename, Z_LVAL_P(line)); xdfree(file_link); } else { xdebug_str_add_fmt(str, formats[20], counter, Z_DVAL_P(time), Z_LVAL_P(memory), combined_function, Z_STRVAL_P(file), formatted_filename, Z_LVAL_P(line)); } xdfree(formatted_filename); } else { xdebug_str_add_fmt(str, formats[16], indent ? formats[21] : "", Z_DVAL_P(time), Z_LVAL_P(memory), counter, combined_function, Z_STRVAL_P(file), Z_LVAL_P(line)); } xdfree(combined_function); } ZEND_HASH_FOREACH_END(); xdebug_str_add_const(str, formats[14]); // footer } static void xdebug_append_nested_section_header(xdebug_str *str, bool indent, int html) { const char **formats = select_formats(html); xdebug_str_add_fmt(str, formats[18], indent ? formats[21] : ""); } static void xdebug_append_nested_section_footer(xdebug_str *str, int html) { const char **formats = select_formats(html); xdebug_str_add_const(str, formats[19]); } void xdebug_append_printable_stack(xdebug_str *str, int html) { int printed_frames = 0; const char **formats = select_formats(html); int i; function_stack_entry *fse; if (!XG_BASE(stack) || XDEBUG_VECTOR_COUNT(XG_BASE(stack)) < 1) { return; } fse = XDEBUG_VECTOR_HEAD(XG_BASE(stack)); xdebug_str_add_const(str, formats[2]); for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)); i++, fse++) { int c = 0; /* Comma flag */ unsigned int j = 0; /* Counter */ char *tmp_name; int variadic_opened = 0; int sent_variables = fse->varc; if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) { sent_variables--; } if (xdebug_is_stack_frame_filtered(XDEBUG_FILTER_STACK, fse)) { continue; } tmp_name = xdebug_show_fname(fse->function, html ? XDEBUG_SHOW_FNAME_ALLOW_HTML : XDEBUG_SHOW_FNAME_DEFAULT); if (html) { xdebug_str_add_fmt(str, formats[3], fse->level, XDEBUG_SECONDS_SINCE_START(fse->nanotime), fse->memory, tmp_name); } else { xdebug_str_add_fmt(str, formats[3], XDEBUG_SECONDS_SINCE_START(fse->nanotime), fse->memory, fse->level, tmp_name); } xdfree(tmp_name); /* Printing vars */ for (j = 0; j < sent_variables; j++) { if (c) { xdebug_str_add_literal(str, ", "); } else { c = 1; } if ( (fse->var[j].is_variadic && Z_ISUNDEF(fse->var[j].data)) ) { xdebug_str_add_literal(str, "..."); } if (fse->var[j].name) { if (html) { xdebug_str_add_literal(str, "$"); xdebug_str_add_zstr(str, fse->var[j].name); xdebug_str_add_literal(str, " = "); } else { xdebug_str_add_literal(str, "$"); xdebug_str_add_zstr(str, fse->var[j].name); xdebug_str_add_literal(str, " = "); } } if (!variadic_opened && fse->var[j].is_variadic && Z_ISUNDEF(fse->var[j].data)) { if (html) { xdebug_str_add_literal(str, "variadic("); } else { xdebug_str_add_literal(str, "variadic("); } c = 0; variadic_opened = 1; continue; } if (!Z_ISUNDEF(fse->var[j].data)) { add_single_value(str, &fse->var[j].data, html); } else { xdebug_str_add_literal(str, "???"); } } if (variadic_opened) { xdebug_str_add_literal(str, ")"); } if (fse->function.include_filename) { if (html) { xdebug_str_add_literal(str, "'"); xdebug_str_add_zstr(str, fse->function.include_filename); xdebug_str_add_literal(str, ""); } else { xdebug_str_addc(str, '\''); xdebug_str_add_zstr(str, fse->function.include_filename); xdebug_str_addc(str, '\''); } } if (html) { char *formatted_filename; xdebug_format_filename(&formatted_filename, "...%s%n", fse->filename); if (strlen(XINI_LIB(file_link_format)) > 0 && strcmp(ZSTR_VAL(fse->filename), "Unknown") != 0) { char *file_link; xdebug_format_file_link(&file_link, ZSTR_VAL(fse->filename), fse->lineno); xdebug_str_add_fmt(str, formats[10], ZSTR_VAL(fse->filename), file_link, formatted_filename, fse->lineno); xdfree(file_link); } else { xdebug_str_add_fmt(str, formats[5], ZSTR_VAL(fse->filename), formatted_filename, fse->lineno); } xdfree(formatted_filename); } else { xdebug_str_add_fmt(str, formats[5], ZSTR_VAL(fse->filename), fse->lineno); } printed_frames++; if (XINI_DEV(max_stack_frames) > 0 && printed_frames >= XINI_DEV(max_stack_frames)) { break; } } if (XINI_DEV(dump_globals) && !(XINI_DEV(dump_once) && XG_LIB(dumped))) { char *tmp = xdebug_get_printable_superglobals(html); if (tmp) { xdebug_str_add(str, tmp, 1); } XG_LIB(dumped) = 1; } if (XINI_DEV(show_local_vars) && XG_BASE(stack) && XDEBUG_VECTOR_TAIL(XG_BASE(stack))) { int scope_nr = XDEBUG_VECTOR_COUNT(XG_BASE(stack)); fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); if (fse->user_defined == XDEBUG_BUILT_IN && xdebug_vector_element_is_valid(XG_BASE(stack), fse -1)) { fse = fse - 1; scope_nr--; } xdebug_lib_register_compiled_variables(fse); if (fse->declared_vars && fse->declared_vars->size) { xdebug_hash *tmp_hash; xdebug_str_add_fmt(str, formats[6], scope_nr); tmp_hash = xdebug_declared_var_hash_from_llist(fse->declared_vars); xdebug_hash_apply_with_argument(tmp_hash, (void*) &html, xdebug_dump_used_var_with_contents, (void *) str); xdebug_hash_destroy(tmp_hash); } } } void xdebug_append_error_footer(xdebug_str *str, int html) { const char **formats = select_formats(html); xdebug_str_add_const(str, formats[7]); } char *xdebug_get_printable_stack(int html, int error_type, const char *buffer, const char *error_filename, const int error_lineno, int include_decription) { char *prepend_string; char *append_string; char *error_type_str = xdebug_error_type(error_type); char *error_type_str_simple = xdebug_error_type_simple(error_type); xdebug_str str = XDEBUG_STR_INITIALIZER; prepend_string = INI_STR((char*) "error_prepend_string"); append_string = INI_STR((char*) "error_append_string"); if (prepend_string) { xdebug_str_add(&str, prepend_string, 0); } xdebug_append_error_head(&str, html, error_type_str_simple); if (include_decription) { xdebug_append_error_description(&str, html, error_type_str, buffer, error_filename, error_lineno); } xdebug_append_printable_stack(&str, html); xdebug_append_error_footer(&str, html); if (append_string) { xdebug_str_add(&str, append_string, 0); } xdfree(error_type_str); xdfree(error_type_str_simple); return str.d; } static void php_output_error(const char *error) { #ifdef PHP_DISPLAY_ERRORS_STDERR if (PG(display_errors) == PHP_DISPLAY_ERRORS_STDERR) { fputs(error, stderr); fflush(stderr); return; } #endif php_printf("%s", error); } char *xdebug_strip_php_stack_trace(char *buffer) { char *tmp_buf, *p; if (strncmp(buffer, "Uncaught ", 9) != 0) { return NULL; } /* find first new line */ p = strchr(buffer, '\n'); if (!p) { p = buffer + strlen(buffer); } else { /* find the last " in ", which isn't great and might not work... but in most cases it will */ p = xdebug_strrstr(buffer, " in "); if (!p) { p = buffer + strlen(buffer); } } /* Create new buffer */ tmp_buf = calloc(p - buffer + 1, 1); strncpy(tmp_buf, buffer, p - buffer); return tmp_buf; } static char *xdebug_handle_stack_trace(int type, char *error_type_str, const char *error_filename, const unsigned int error_lineno, char *buffer) { char *printable_stack; char *tmp_buf; /* We need to see if we have an uncaught exception fatal error now */ if (type == E_ERROR && ((tmp_buf = xdebug_strip_php_stack_trace(buffer)) != NULL)) { xdebug_str str = XDEBUG_STR_INITIALIZER; /* Append error */ xdebug_append_error_head(&str, PG(html_errors), "uncaught-exception"); xdebug_append_error_description(&str, PG(html_errors), error_type_str, tmp_buf, error_filename, error_lineno); xdebug_append_printable_stack(&str, PG(html_errors)); if (XG_BASE(last_exception_trace)) { xdebug_str_add(&str, XG_BASE(last_exception_trace), 0); } xdebug_append_error_footer(&str, PG(html_errors)); free(tmp_buf); printable_stack = str.d; } else { printable_stack = xdebug_get_printable_stack(PG(html_errors), type, buffer, error_filename, error_lineno, 1); } return printable_stack; } static void clear_last_error() { if (PG(last_error_message)) { zend_string_release(PG(last_error_message)); PG(last_error_message) = NULL; } if (PG(last_error_file)) { # if PHP_VERSION_ID >= 80100 zend_string_release(PG(last_error_file)); # else free(PG(last_error_file)); # endif PG(last_error_file) = NULL; } } /* Error callback for formatting stack traces */ #if PHP_VERSION_ID >= 80100 void xdebug_develop_error_cb(int orig_type, zend_string *error_filename, const unsigned int error_lineno, zend_string *message) { #else void xdebug_develop_error_cb(int orig_type, const char *error_filename, const unsigned int error_lineno, zend_string *message) { #endif char *error_type_str; int display; int type = orig_type & E_ALL; error_handling_t error_handling; zend_class_entry *exception_class; error_type_str = xdebug_error_type(type); /* check for repeated errors to be ignored */ if (PG(ignore_repeated_errors) && PG(last_error_message)) { /* no check for PG(last_error_file) is needed since it cannot * be NULL if PG(last_error_message) is not NULL */ if (!zend_string_equals(PG(last_error_message), message) || (!PG(ignore_repeated_source) && ( (PG(last_error_lineno) != (int)error_lineno) || #if PHP_VERSION_ID >= 80100 !zend_string_equals(PG(last_error_file), error_filename) #else strcmp(PG(last_error_file), error_filename) != 0 #endif )) ) { display = 1; } else { display = 0; } } else { display = 1; } error_handling = EG(error_handling); exception_class = EG(exception_class); /* according to error handling mode, throw exception or show it */ if (error_handling == EH_THROW) { switch (type) { case E_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: case E_USER_ERROR: case E_PARSE: /* fatal errors are real errors and cannot be made exceptions */ break; case E_STRICT: case E_DEPRECATED: case E_USER_DEPRECATED: /* for the sake of BC to old damaged code */ break; case E_NOTICE: case E_USER_NOTICE: /* notices are no errors and are not treated as such like E_WARNINGS */ break; default: /* throw an exception if we are in EH_THROW mode * but DO NOT overwrite a pending exception */ if (!EG(exception)) { zend_throw_error_exception(exception_class, message, 0, type); } xdfree(error_type_str); return; } } /* Store last error message for error_get_last() */ if (display) { clear_last_error(); if (!error_filename) { #if PHP_VERSION_ID >= 80100 error_filename = zend_string_init(ZEND_STRL("Unknown"), 0); #else error_filename = "Unknown"; #endif } PG(last_error_type) = type; PG(last_error_message) = zend_string_copy(message); #if PHP_VERSION_ID >= 80100 PG(last_error_file) = zend_string_copy(error_filename); #else PG(last_error_file) = strdup(error_filename); #endif PG(last_error_lineno) = error_lineno; } if ((EG(error_reporting) | XINI_DEV(force_error_reporting)) & type) { /* Log to logger */ if (PG(log_errors)) { #ifdef PHP_WIN32 if (type==E_CORE_ERROR || type==E_CORE_WARNING) { php_syslog(LOG_ALERT, "PHP %s: %s (%s)", error_type_str, ZSTR_VAL(message), GetCommandLine()); } #endif #if PHP_VERSION_ID >= 80100 xdebug_log_stack(error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno); #else xdebug_log_stack(error_type_str, ZSTR_VAL(message), error_filename, error_lineno); #endif if (XINI_DEV(dump_globals) && !(XINI_DEV(dump_once) && XG_LIB(dumped))) { char *printable_stack = xdebug_get_printable_superglobals(0); if (printable_stack) { int pc; xdebug_arg *parts = xdebug_arg_ctor(); xdebug_explode("\n", printable_stack, parts, -1); for (pc = 0; pc < parts->c; pc++) { char *tmp_line = xdebug_sprintf("PHP %s", parts->args[pc]); php_log_err(tmp_line); xdfree(tmp_line); } xdebug_arg_dtor(parts); xdfree(printable_stack); php_log_err((char*) "PHP "); } } } /* Display errors */ if ((PG(display_errors) || XINI_DEV(force_display_errors)) && !PG(during_request_startup)) { char *printable_stack; #if PHP_VERSION_ID >= 80100 printable_stack = xdebug_handle_stack_trace(type, error_type_str, ZSTR_VAL(error_filename), error_lineno, ZSTR_VAL(message)); #else printable_stack = xdebug_handle_stack_trace(type, error_type_str, error_filename, error_lineno, ZSTR_VAL(message)); #endif if (XG_LIB(do_collect_errors) && (type != E_ERROR) && (type != E_COMPILE_ERROR) && (type != E_USER_ERROR)) { xdebug_llist_insert_next(XG_DEV(collected_errors), XDEBUG_LLIST_TAIL(XG_DEV(collected_errors)), printable_stack); } else { php_output_error(printable_stack); xdfree(printable_stack); } } else if (XG_LIB(do_collect_errors)) { char *printable_stack; #if PHP_VERSION_ID >= 80100 printable_stack = xdebug_get_printable_stack(PG(html_errors), type, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno, 1); #else printable_stack = xdebug_get_printable_stack(PG(html_errors), type, ZSTR_VAL(message), error_filename, error_lineno, 1); #endif xdebug_llist_insert_next(XG_DEV(collected_errors), XDEBUG_LLIST_TAIL(XG_DEV(collected_errors)), printable_stack); } } { #if PHP_VERSION_ID >= 80100 zend_string *tmp_error_filename = zend_string_copy(error_filename); #else zend_string *tmp_error_filename = zend_string_init(error_filename, strlen(error_filename), 0); #endif xdebug_debugger_error_cb(tmp_error_filename, error_lineno, type, error_type_str, ZSTR_VAL(message)); zend_string_release(tmp_error_filename); } xdfree(error_type_str); if (type & XINI_DEV(halt_level) & XDEBUG_ALLOWED_HALT_LEVELS) { type = E_USER_ERROR; } /* Bail out if we can't recover */ switch (type) { case E_CORE_ERROR: if (!php_get_module_initialized()) { /* bad error in module startup - no way we can live with this */ exit(-2); } XDEBUG_BREAK_INTENTIONALLY_MISSING case E_ERROR: case E_RECOVERABLE_ERROR: case E_PARSE: case E_COMPILE_ERROR: case E_USER_ERROR: EG(exit_status) = 255; if (php_get_module_initialized()) { if (!PG(display_errors) && !SG(headers_sent) && SG(sapi_headers).http_response_code == 200 ) { sapi_header_line ctr = { 0, 0, 0 }; ctr.line = (char*) "HTTP/1.0 500 Internal Server Error"; ctr.line_len = sizeof("HTTP/1.0 500 Internal Server Error") - 1; sapi_header_op(SAPI_HEADER_REPLACE, &ctr); } /* the parser would return 1 (failure), we can bail out nicely */ if (!(orig_type & E_DONT_BAIL)) { /* restore memory limit */ zend_set_memory_limit(PG(memory_limit)); zend_objects_store_mark_destructed(&EG(objects_store)); _zend_bailout((char*) __FILE__, __LINE__); return; } } break; } } void xdebug_develop_throw_exception_hook(zend_object *exception, zval *file, zval *line, zval *code, char *code_str, zval *message) { zend_class_entry *exception_ce = exception->ce; char *exception_trace; xdebug_str tmp_str = XDEBUG_STR_INITIALIZER; zval *z_previous_exception, *z_last_exception_slot, *z_previous_trace; zend_object *previous_exception_obj = exception; zval dummy; if (!PG(html_errors)) { xdebug_str_addc(&tmp_str, '\n'); } xdebug_append_error_description(&tmp_str, PG(html_errors), STR_NAME_VAL(exception_ce->name), message ? Z_STRVAL_P(message) : "", Z_STRVAL_P(file), Z_LVAL_P(line)); z_previous_trace = last_exception_find_trace(exception); if (z_previous_trace) { xdebug_append_printable_stack_from_zval(&tmp_str, false, z_previous_trace, PG(html_errors)); } else { xdebug_append_printable_stack(&tmp_str, PG(html_errors)); } /* Loop over previous exceptions until there are none left */ { bool first = true; bool found = false; do { z_previous_exception = zend_read_property(exception_ce, previous_exception_obj, "previous", sizeof("previous")-1, 1, &dummy); if (!z_previous_exception || Z_TYPE_P(z_previous_exception) != IS_OBJECT) { break; } if (first) { first = false; found = true; xdebug_append_nested_section_header(&tmp_str, true, PG(html_errors)); } xdebug_append_sub_header(&tmp_str, PG(html_errors)); xdebug_append_error_description_from_object(&tmp_str, PG(html_errors), z_previous_exception); z_previous_trace = last_exception_find_trace(Z_OBJ_P(z_previous_exception)); xdebug_append_printable_stack_from_zval(&tmp_str, true, z_previous_trace, PG(html_errors)); previous_exception_obj = Z_OBJ_P(z_previous_exception); } while (true); if (found) { xdebug_append_nested_section_footer(&tmp_str, PG(html_errors)); } } /* Remember last stack trace so it can be retrieved in an exception handler through * xdebug_get_function_stack(['from_exception' => $e]) */ z_last_exception_slot = last_exception_get_slot(exception); if (zval_from_stack(z_last_exception_slot, true, true)) { zval_from_stack_add_frame(z_last_exception_slot, XDEBUG_VECTOR_TAIL(XG_BASE(stack)), EG(current_execute_data), true, true); } exception_trace = tmp_str.d; /* Save */ if (XG_BASE(last_exception_trace)) { xdfree(XG_BASE(last_exception_trace)); } XG_BASE(last_exception_trace) = exception_trace; /* Display if expected */ if (XINI_DEV(show_ex_trace) || (instanceof_function(exception_ce, zend_ce_error) && XINI_DEV(show_error_trace))) { if (PG(log_errors)) { xdebug_log_stack(STR_NAME_VAL(exception_ce->name), Z_STRVAL_P(message), Z_STRVAL_P(file), Z_LVAL_P(line)); } if (PG(display_errors)) { xdebug_str displ_tmp_str = XDEBUG_STR_INITIALIZER; xdebug_append_error_head(&displ_tmp_str, PG(html_errors), "exception"); xdebug_str_add(&displ_tmp_str, exception_trace, 0); xdebug_append_error_footer(&displ_tmp_str, PG(html_errors)); php_printf("%s", displ_tmp_str.d); xdebug_str_dtor(displ_tmp_str); } } } /* {{{ proto int xdebug_get_stack_depth() Returns the stack depth */ PHP_FUNCTION(xdebug_get_stack_depth) { if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'"); RETURN_LONG(0); } /* We substract one so that the function call to xdebug_get_stack_depth() * is not part of the returned depth. */ RETURN_LONG(XDEBUG_VECTOR_COUNT(XG_BASE(stack)) - 1); } /* {{{ proto array xdebug_get_function_stack() Returns an array representing the current stack */ PHP_FUNCTION(xdebug_get_function_stack) { HashTable *options = NULL; bool add_local_vars = false; bool params_as_values = false; if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'"); array_init(return_value); return; } ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(options) ZEND_PARSE_PARAMETERS_END(); if (options) { zval *value; value = zend_hash_str_find(options, "from_exception", sizeof("from_exception") - 1); if (value && Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), zend_ce_throwable)) { zval *z_previous_exception = last_exception_find_trace(Z_OBJ_P(value)); if (z_previous_exception) { Z_TRY_ADDREF(*z_previous_exception); ZVAL_COPY_VALUE(return_value, z_previous_exception); } else { array_init(return_value); } if ( (zend_hash_str_find(options, "local_vars", sizeof("local_vars") - 1)) || (zend_hash_str_find(options, "params_as_values", sizeof("params_as_values") - 1)) ) { php_error(E_WARNING, "The 'local_vars' or 'params_as_values' options are ignored when used with the 'from_exception' option"); } return; } value = zend_hash_str_find(options, "local_vars", sizeof("local_vars") - 1); if (value) { add_local_vars = (Z_TYPE_P(value) == IS_TRUE); } value = zend_hash_str_find(options, "params_as_values", sizeof("params_as_values") - 1); if (value) { params_as_values = (Z_TYPE_P(value) == IS_TRUE); } } zval_from_stack(return_value, add_local_vars, params_as_values); } /* }}} */ xdebug-3.4.3/src/develop/stack.h0000664000175000017500000000406015011062311015756 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef XDEBUG_STACK_H #define XDEBUG_STACK_H #include "lib/str.h" #define XDEBUG_STACK_NO_DESC 0x01 void xdebug_append_error_head(xdebug_str *str, int html, const char *error_type_str); void xdebug_append_error_description(xdebug_str *str, int html, const char *error_type_str, const char *buffer, const char *error_filename, const int error_lineno); void xdebug_append_printable_stack(xdebug_str *str, int html); void xdebug_append_error_footer(xdebug_str *str, int html); void xdebug_log_stack(const char *error_type_str, char *buffer, const char *error_filename, const int error_lineno); char *xdebug_strip_php_stack_trace(char *buffer); char *xdebug_get_printable_stack(int html, int error_type, const char *buffer, const char *error_filename, const int error_lineno, int include_decription); #if PHP_VERSION_ID >= 80100 void xdebug_develop_error_cb(int orig_type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); #else void xdebug_develop_error_cb(int orig_type, const char *error_filename, const uint32_t error_lineno, zend_string *message); #endif #endif xdebug-3.4.3/src/develop/superglobals.c0000664000175000017500000001400415011062311017345 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2025 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "SAPI.h" #include "php_xdebug.h" #include "superglobals.h" #include "lib/compat.h" #include "lib/lib.h" #include "lib/var_export_html.h" #include "lib/var_export_line.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); void xdebug_superglobals_dump_dtor(void *user, void *ptr) { free(ptr); } static void dump_hash_elem(zval *z, const char *name, long index_key, const char *elem, int html, xdebug_str *str) { if (html) { if (elem) { xdebug_str_add_fmt(str, "
$%s['%s'] =
", name, elem); } else { xdebug_str_add_fmt(str, "
$%s[%ld] =
", name, index_key); } } if (z != NULL) { xdebug_str *val; if (html) { val = xdebug_get_zval_value_html(NULL, z, 0, NULL); xdebug_str_add_literal(str, ""); xdebug_str_add_str(str, val); xdebug_str_add_literal(str, ""); } else { val = xdebug_get_zval_value_line(z, 0, NULL); if (elem) { xdebug_str_add_fmt(str, "\n $%s['%s'] = ", name, elem); } else { xdebug_str_add_fmt(str, "\n $%s[%ld] = ", name, index_key); } xdebug_str_add_str(str, val); } xdebug_str_free(val); } else { /* not found */ if (html) { xdebug_str_add_literal(str, "undefined"); } else if (elem) { xdebug_str_add_fmt(str, "\n $%s['%s'] is undefined", name, elem); } else { xdebug_str_add_fmt(str, "\n $%s[%ld] is undefined", name, index_key); } } if (html) { xdebug_str_add_literal(str, "\n"); } } static int dump_hash_elem_va(zval *pDest, zend_ulong index_key, zend_string *hash_key, const char *name, int html, xdebug_str *str) { if (HASH_KEY_IS_NUMERIC(hash_key)) { dump_hash_elem(pDest, name, index_key, NULL, html, str); } else { dump_hash_elem(pDest, name, 0, HASH_APPLY_KEY_VAL(hash_key), html, str); } return SUCCESS; } static void dump_hash(xdebug_llist *l, const char *name, int name_len, int html, xdebug_str *str) { zval *z; zend_ulong num; zend_string *key; zval *val; HashTable *ht = NULL; xdebug_llist_element *elem; if (!XDEBUG_LLIST_COUNT(l)) { return; } { zend_string *s_name = zend_string_init(name, name_len, 0); if ((z = zend_hash_find(&EG(symbol_table), s_name))) { if (Z_TYPE_P(z) == IS_REFERENCE) { z = &z->value.ref->val; } if (Z_TYPE_P(z) == IS_ARRAY) { ht = Z_ARRVAL_P(z); } } zend_string_release(s_name); } if (html) { xdebug_str_add_fmt(str, "Dump $%s\n", name); } else { xdebug_str_add_fmt(str, "\nDump $%s", name); } elem = XDEBUG_LLIST_HEAD(l); while (elem != NULL) { zend_string *s; s = zend_string_init(elem->ptr, strlen(elem->ptr), 0); if (ht && (*((char *) (elem->ptr)) == '*')) { ZEND_HASH_FOREACH_KEY_VAL_IND(ht, num, key, val) { dump_hash_elem_va(val, num, key, name, html, str); } ZEND_HASH_FOREACH_END(); } else if (ht && (z = zend_hash_find(ht, s))) { dump_hash_elem(z, name, 0, elem->ptr, html, str); } else if (XINI_DEV(dump_undefined)) { dump_hash_elem(NULL, name, 0, elem->ptr, html, str); } elem = XDEBUG_LLIST_NEXT(elem); zend_string_release(s); } } char* xdebug_get_printable_superglobals(int html) { xdebug_str str = XDEBUG_STR_INITIALIZER; dump_hash(&XG_DEV(server), "_SERVER", HASH_KEY_SIZEOF("_SERVER"), html, &str); dump_hash(&XG_DEV(get), "_GET", HASH_KEY_SIZEOF("_GET"), html, &str); dump_hash(&XG_DEV(post), "_POST", HASH_KEY_SIZEOF("_POST"), html, &str); dump_hash(&XG_DEV(cookie), "_COOKIE", HASH_KEY_SIZEOF("_COOKIE"), html, &str); dump_hash(&XG_DEV(files), "_FILES", HASH_KEY_SIZEOF("_FILES"), html, &str); dump_hash(&XG_DEV(env), "_ENV", HASH_KEY_SIZEOF("_ENV"), html, &str); dump_hash(&XG_DEV(session), "_SESSION", HASH_KEY_SIZEOF("_SESSION"), html, &str); dump_hash(&XG_DEV(request), "_REQUEST", HASH_KEY_SIZEOF("_REQUEST"), html, &str); return str.d; } void xdebug_superglobals_dump_tok(xdebug_llist *l, char *str) { char *tok; const char *sep = ","; tok = strtok(str, sep); while (tok != NULL) { char *p = tok + strlen(tok) - 1; while ((*tok == ' ') || (*tok == '\t')) { tok++; } while ((p > tok) && ((*p == ' ') || (*p == '\t'))) { p--; } *(p+1) = 0; /* we need to strdup each element so that we can safely free it */ xdebug_llist_insert_next(l, NULL, strdup(tok)); tok = strtok(NULL, sep); } } PHP_FUNCTION(xdebug_dump_superglobals) { int html = PG(html_errors); char *superglobal_info = NULL; if (html) { php_printf("\n"); } superglobal_info = xdebug_get_printable_superglobals(html); if (superglobal_info) { php_printf("%s", superglobal_info); xdfree(superglobal_info); } else { php_printf("\n"); } if (html) { php_printf("
No information about superglobals is available or configured.
\n"); } } xdebug-3.4.3/src/develop/superglobals.h0000664000175000017500000000311415011062311017352 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_SUPERGLOBALS_H__ #define __HAVE_XDEBUG_SUPERGLOBALS_H__ #include "lib/php-header.h" #include "develop_private.h" void xdebug_superglobals_dump_dtor(void *, void*); char *xdebug_get_printable_superglobals(int html); void xdebug_superglobals_dump_tok(xdebug_llist *l, char *str); # define DUMP_TOK(__llist) \ xdebug_llist_empty(&XG_DEV(__llist), NULL); \ if (new_value && new_value->val) { \ char *str = estrndup(new_value->val, new_value->len); \ xdebug_superglobals_dump_tok(&XG_DEV(__llist), str); \ efree(str); \ } \ return SUCCESS; #endif /* __HAVE_XDEBUG_SUPERGLOBALS_H__ */ xdebug-3.4.3/src/debugger/com.c0000664000175000017500000006460015011062311015556 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include #include #include #include #include #ifndef PHP_WIN32 # if HAVE_POLL_H # include # elif HAVE_SYS_POLL_H # include # endif # include # include # include # include # if HAVE_NETINET_IN_H # include # endif # include #else # include # include # include "win32/time.h" # undef UNICODE # include # include # include # pragma comment (lib, "Ws2_32.lib") # define PATH_MAX MAX_PATH # define poll WSAPoll #endif #include "Zend/zend_extensions.h" #include "com.h" #include "debugger_private.h" #include "handler_dbgp.h" #include "ip_info.h" #include "lib/crc32.h" #include "lib/log.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) #if !WIN32 && !WINNT static int xdebug_create_socket_unix(const char *path) { struct sockaddr_un sa; int sockfd; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == SOCK_ERR) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "UNIX", "Creating socket for 'unix://%s', socket: %s.", path, strerror(errno)); return SOCK_ERR; } sa.sun_family = AF_UNIX; strncpy(sa.sun_path, path, sizeof(sa.sun_path) - 1); if (connect(sockfd, (struct sockaddr*)&sa, sizeof(sa)) < 0) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "UNIX", "Creating socket for 'unix://%s', connect: %s.", path, strerror(errno)); SCLOSE(sockfd); return (errno == EACCES) ? SOCK_ACCESS_ERR : SOCK_ERR; } /* Prevent the socket from being inherited by exec'd children */ if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) < 0) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "UNIX", "Creating socket for 'unix://%s', fcntl(FD_CLOEXEC): %s.", path, strerror(errno)); } return sockfd; } #endif #if !WIN32 && !WINNT /* For OSX and FreeBSD */ # if !defined(SOL_TCP) && defined(IPPROTO_TCP) # define SOL_TCP IPPROTO_TCP # endif # if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) # define TCP_KEEPIDLE TCP_KEEPALIVE # endif void set_keepalive_options(int fd) { int optval = 1; int optlen = sizeof(optval); int ret; ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); if (ret) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "KEEPALIVE", "Could not set SO_KEEPALIVE: %s.", strerror(errno)); return; } # if defined(TCP_KEEPIDLE) optval = 600; ret = setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen); if (ret) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "KEEPALIVE", "Could not set TCP_KEEPIDLE to %d: %s.", optval, strerror(errno)); return; } # endif # if defined(TCP_KEEPCNT) optval = 20; ret = setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &optval, optlen); if (ret) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "KEEPALIVE", "Could not set TCP_KEEPCNT to %d: %s.", optval, strerror(errno)); return; } # endif # if defined(TCP_KEEPINTVL) optval = 60; ret = setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, optlen); if (ret) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "KEEPALIVE", "Could not set TCP_KEEPINTVL to %d: %s.", optval, strerror(errno)); return; } # endif } #endif // !WIN32 && !WINNT static char* resolve_pseudo_hosts(const char *requested_hostname) { #if __linux__ /* Does it start with 'xdebug://' ? */ if (strncmp(requested_hostname, "xdebug://", strlen("xdebug://")) != 0) { return NULL; } /* Check for 'gateway' pseudo host */ if (strcmp(requested_hostname, "xdebug://gateway") == 0) { #if XDEBUG_GATEWAY_SUPPORT char *gateway = xdebug_get_gateway_ip(); if (!gateway) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "GATEWAY", "Could not find network gateway to use for 'gateway' pseudo-host."); return NULL; } xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Found 'gateway' pseudo-host, with IP address '%s'.", gateway); return gateway; #else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "PSEUDO-GW-NO-SUPPORT", "Pseudo-host: '%s' is not supported on this host.", requested_hostname + strlen("xdebug://")); return NULL; # endif } /* Check for 'nameserver' pseudo host */ if (strcmp(requested_hostname, "xdebug://nameserver") == 0) { #if XDEBUG_NAMESERVER_SUPPORT char *gateway = xdebug_get_private_nameserver(); if (!gateway) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "NAMESERVER", "Could not find a private network nameserver for 'nameserver' pseudo-host."); return NULL; } xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Found 'nameserver' pseudo-host, with IP address '%s'.", gateway); return gateway; # else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "PSEUDO-NS-NO-SUPPORT", "Pseudo-host: '%s' is not supported on this host.", requested_hostname + strlen("xdebug://")); return NULL; # endif } xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "PSEUDO-UNKNOWN", "Unknown pseudo-host: '%s', only 'gateway' or 'nameserver' are supported.", requested_hostname + strlen("xdebug://")); #endif return NULL; } static int xdebug_create_socket(const char *hostname, int dport, int timeout) { struct addrinfo hints; struct addrinfo *remote; struct addrinfo *ptr; int status; int sockfd = 0; int sockerror; char sport[10]; int actually_connected; struct sockaddr_in6 sa; socklen_t size = sizeof(sa); #if WIN32|WINNT WSAPOLLFD ufds[1] = {0}; WORD wVersionRequested; WSADATA wsaData; char optval = 1; u_long yes = 1; u_long no = 0; wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); #else struct pollfd ufds[1]; long optval = 1; #endif if (!strncmp(hostname, "unix://", strlen("unix://"))) { #if WIN32|WINNT xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "UNIX-WIN", "Creating Unix domain socket ('%s') on Windows is not supported.", hostname); return SOCK_ERR; #else return xdebug_create_socket_unix(hostname + strlen("unix://")); #endif } /* Make a string of the port number that can be used with getaddrinfo */ sprintf(sport, "%d", dport); /* Create hints for getaddrinfo saying that we want IPv4 and IPv6 TCP stream sockets */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; /* Call getaddrinfo and return SOCK_ERR if the call fails for some reason */ if ((status = getaddrinfo(hostname, sport, &hints, &remote)) != 0) { #if WIN32|WINNT xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK1", "Creating socket for '%s:%d', getaddrinfo: %d.", hostname, dport, WSAGetLastError()); #else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK1", "Creating socket for '%s:%d', getaddrinfo: %s.", hostname, dport, strerror(errno)); #endif return SOCK_ERR; } /* Go through every returned IP address */ for (ptr = remote; ptr != NULL; ptr = ptr->ai_next) { /* Try to create the socket. If the creation fails continue on with the * next IP address in the list */ if ((sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) == SOCK_ERR) { #if WIN32|WINNT xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK2", "Creating socket for '%s:%d', socket: %d.", hostname, dport, WSAGetLastError()); #else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK2", "Creating socket for '%s:%d', socket: %s.", hostname, dport, strerror(errno)); #endif continue; } /* Put socket in non-blocking mode so we can use poll for timeouts */ #ifdef WIN32 status = ioctlsocket(sockfd, FIONBIO, &yes); if (SOCKET_ERROR == status) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK2", "Creating socket for '%s:%d', FIONBIO: %d.", hostname, dport, WSAGetLastError()); } #else fcntl(sockfd, F_SETFL, O_NONBLOCK); #endif #if !WIN32 && !WINNT /* Prevent the socket from being inherited by exec'd children on *nix (not necessary on Win) */ if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) < 0) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK2", "Creating socket for '%s:%d', fcntl(FD_CLOEXEC): %s.", hostname, dport, strerror(errno)); } #endif /* Try to connect to the newly created socket */ /* Worth noting is that the port is set in the getaddrinfo call before */ status = connect(sockfd, ptr->ai_addr, ptr->ai_addrlen); /* Determine if we got a connection. If no connection could be made * we close the socket and continue with the next IP address in the list */ if (status < 0) { #ifdef WIN32 errno = WSAGetLastError(); if (errno != WSAEINPROGRESS && errno != WSAEWOULDBLOCK) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK3", "Creating socket for '%s:%d', connect: %d.", hostname, dport, errno); #else if (errno == EACCES) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK3", "Creating socket for '%s:%d', connect: %s.", hostname, dport, strerror(errno)); SCLOSE(sockfd); sockfd = SOCK_ACCESS_ERR; continue; } if (errno != EINPROGRESS) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK3", "Creating socket for '%s:%d', connect: %s.", hostname, dport, strerror(errno)); #endif SCLOSE(sockfd); sockfd = SOCK_ERR; continue; } ufds[0].fd = sockfd; #if WIN32|WINNT ufds[0].events = POLLIN | POLLOUT; #else ufds[0].events = POLLIN | POLLOUT | POLLPRI; #endif while (1) { sockerror = poll(ufds, 1, timeout); #if WIN32|WINNT errno = WSAGetLastError(); if (errno == WSAEINPROGRESS || errno == WSAEWOULDBLOCK) { /* XXX introduce retry count? */ continue; } #endif /* If an error occured when doing the poll */ if (sockerror == SOCK_ERR) { #if WIN32|WINNT xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK4", "Creating socket for '%s:%d', WSAPoll error: %d (%d, %d).", hostname, dport, WSAGetLastError(), sockerror, errno); #else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK4", "Creating socket for '%s:%d', poll error: %s (%d).", hostname, dport, strerror(errno), sockerror); #endif sockerror = SOCK_ERR; break; } /* A timeout occured when polling the socket */ if (sockerror == 0) { sockerror = SOCK_TIMEOUT_ERR; break; } /* If the poll was successful but an error occured */ if (ufds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { #if WIN32|WINNT xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK4", "Creating socket for '%s:%d', WSAPoll success, but error: %d (%d).", hostname, dport, WSAGetLastError(), ufds[0].revents); #else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK4", "Creating socket for '%s:%d', poll success, but error: %s (%d).", hostname, dport, strerror(errno), ufds[0].revents); #endif sockerror = SOCK_ERR; break; } /* If the poll was successful break out */ if (ufds[0].revents & (POLLIN | POLLOUT)) { sockerror = sockfd; break; } else { /* We should never get here, but added as a failsafe to break out from any loops */ #if WIN32|WINNT xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK4", "Creating socket for '%s:%d', WSAPoll: %d.", hostname, dport, WSAGetLastError()); #else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK4", "Creating socket for '%s:%d', poll: %s.", hostname, dport, strerror(errno)); #endif sockerror = SOCK_ERR; break; } } if (sockerror > 0) { actually_connected = getpeername(sockfd, (struct sockaddr *)&sa, &size); if (actually_connected == -1) { #if WIN32|WINNT xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK5", "Creating socket for '%s:%d', getpeername: %d.", hostname, dport, WSAGetLastError()); #else xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK5", "Creating socket for '%s:%d', getpeername: %s.", hostname, dport, strerror(errno)); #endif sockerror = SOCK_ERR; } } /* If there where some errors close the socket and continue with the next IP address */ if (sockerror < 0) { SCLOSE(sockfd); sockfd = sockerror; continue; } } break; } /* Free the result returned by getaddrinfo, as well as the duplicated hostname */ freeaddrinfo(remote); /* If we got a socket, set the option "No delay" to true (1) */ if (sockfd > 0) { #ifdef WIN32 status = ioctlsocket(sockfd, FIONBIO, &no); if (SOCKET_ERROR == status) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SOCK6", "Creating socket for '%s:%d', FIONBIO: %d.", hostname, dport, WSAGetLastError()); } #else fcntl(sockfd, F_SETFL, 0); #endif setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)); #if !WIN32 && !WINNT set_keepalive_options(sockfd); #endif /* Now we have a socket, update the last seen hostname and port */ if (XG_DBG(context).connected_hostname) { xdfree(XG_DBG(context).connected_hostname); } XG_DBG(context).connected_hostname = xdstrdup(hostname); XG_DBG(context).connected_port = dport; } return sockfd; } void xdebug_close_socket(int socketfd) { SCLOSE(socketfd); } static zval *get_client_discovery_address(char **header) { xdebug_arg *headers; int i; zval *remote_addr = NULL; xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Checking for client discovery headers: '%s'.", XINI_DBG(client_discovery_header)); headers = xdebug_arg_ctor(); xdebug_explode(",", XINI_DBG(client_discovery_header), headers, -1); for (i = 0; i < headers->c; ++i) { char *header_name = xdebug_trim(headers->args[i]); xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Checking header '%s'.", header_name); remote_addr = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), header_name, HASH_KEY_STRLEN(header_name)); if (remote_addr) { *header = header_name; xdebug_arg_dtor(headers); return remote_addr; } xdfree(header_name); } return NULL; } /* Starting the debugger */ static void xdebug_init_normal_debugger(xdebug_str *connection_attempts) { zval *remote_addr = NULL; char *cp = NULL; int cp_found = 0; char *header = NULL; if (!XINI_DBG(discover_client_host)) { char *pseudo_hostname = resolve_pseudo_hosts(XINI_DBG(client_host)); if (pseudo_hostname) { xdebug_str_add_fmt(connection_attempts, "%s:%ld (through xdebug.client_host/xdebug.client_port, from %s)", pseudo_hostname, XINI_DBG(client_port), XINI_DBG(client_host)); xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Connecting to resolved address/port: %s:%ld.", pseudo_hostname, (long int) XINI_DBG(client_port)); XG_DBG(context).socket = xdebug_create_socket(pseudo_hostname, XINI_DBG(client_port), XINI_DBG(connect_timeout_ms)); xdfree(pseudo_hostname); return; } xdebug_str_add_fmt(connection_attempts, "%s:%ld (through xdebug.client_host/xdebug.client_port)", XINI_DBG(client_host), XINI_DBG(client_port)); xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Connecting to configured address/port: %s:%ld.", XINI_DBG(client_host), (long int) XINI_DBG(client_port)); XG_DBG(context).socket = xdebug_create_socket(XINI_DBG(client_host), XINI_DBG(client_port), XINI_DBG(connect_timeout_ms)); return; } /* Discover client host section */ remote_addr = get_client_discovery_address(&header); if (remote_addr && strstr(Z_STRVAL_P(remote_addr), "://")) { header = NULL; xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "INVADDR", "Invalid remote address provided containing URI spec '%s'.", Z_STRVAL_P(remote_addr)); remote_addr = NULL; } if (!remote_addr) { xdebug_str_add_fmt(connection_attempts, "%s:%ld (fallback through xdebug.client_host/xdebug.client_port)", XINI_DBG(client_host), XINI_DBG(client_port)); xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "HDR", "Could not discover client host through HTTP headers, connecting to configured address/port: %s:%ld.", XINI_DBG(client_host), (long int) XINI_DBG(client_port)); XG_DBG(context).socket = xdebug_create_socket(XINI_DBG(client_host), XINI_DBG(client_port), XINI_DBG(connect_timeout_ms)); return; } /* Use first IP according to RFC 7239 */ cp = strchr(Z_STRVAL_P(remote_addr), ','); if (cp) { *cp = '\0'; cp_found = 1; } xdebug_str_add_fmt(connection_attempts, "%s:%ld (from %s HTTP header)", Z_STRVAL_P(remote_addr), XINI_DBG(client_port), header); xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Client host discovered through HTTP header, connecting to %s:%ld.", Z_STRVAL_P(remote_addr), (long int) XINI_DBG(client_port)); xdfree(header); XG_DBG(context).socket = xdebug_create_socket(Z_STRVAL_P(remote_addr), XINI_DBG(client_port), XINI_DBG(connect_timeout_ms)); if (XG_DBG(context).socket < 0) { xdebug_str_add_fmt(connection_attempts, ", %s:%ld (fallback through xdebug.client_host/xdebug.client_port)", XINI_DBG(client_host), XINI_DBG(client_port)); xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "CON", "Could not connect to client host discovered through HTTP headers, connecting to configured address/port: %s:%ld.", XINI_DBG(client_host), (long int) XINI_DBG(client_port)); XG_DBG(context).socket = xdebug_create_socket(XINI_DBG(client_host), XINI_DBG(client_port), XINI_DBG(connect_timeout_ms)); } /* Replace the ',', in case we had changed the original header due * to multiple values */ if (cp_found) { *cp = ','; } } static void xdebug_init_cloud_debugger(const char *cloud_id) { unsigned long crc = xdebug_crc32(cloud_id, strlen(cloud_id)); char *host; host = xdebug_sprintf("%c.cloud.xdebug.com", (crc & 0x0f) + 'a'); xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Connecting to configured address/port: %s:%ld.", host, XDEBUG_CLOUD_PORT); XG_DBG(context).socket = xdebug_create_socket(host, XDEBUG_CLOUD_PORT, XINI_DBG(connect_timeout_ms)); xdfree(host); } /** * dXXXXXXa-cXXa-4XX7-9XX3-fXXXXXXXXXX0 */ static int ide_key_is_cloud_id() { const char *k = XG_DBG(ide_key); if (strlen(k) != 36) { return 0; } if (k[8] != '-' || k[13] != '-' || k[18] != '-' || k[23] != '-') { return 0; } return 1; } static void warn_if_opcache_is_loaded_after_xdebug() { bool xdebug_loaded = false; zend_llist_element *ext_ptr = zend_extensions.head; do { zend_extension *zext = (zend_extension *)ext_ptr->data; if (strcmp(zext->name, "Xdebug") == 0) { xdebug_loaded = true; } if (strcmp(zext->name, "Zend OPcache") == 0) { if (xdebug_loaded) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "OPCACHE", "Debugger is not working optimally, as Xdebug is loaded before Zend OPcache"); } return; } ext_ptr = ext_ptr->next; } while (ext_ptr != NULL); } static void xdebug_init_debugger() { xdebug_str *connection_attempts = xdebug_str_new(); /* Get handler from mode */ XG_DBG(context).handler = &xdebug_handler_dbgp; warn_if_opcache_is_loaded_after_xdebug(); if (strcmp(XINI_DBG(cloud_id), "") != 0) { xdebug_init_cloud_debugger(XINI_DBG(cloud_id)); XG_DBG(context).host_type = XDEBUG_CLOUD; } else if (XG_DBG(ide_key) && ide_key_is_cloud_id()) { xdebug_init_cloud_debugger(XG_DBG(ide_key)); XG_DBG(context).host_type = XDEBUG_CLOUD_FROM_TRIGGER_VALUE; } else { xdebug_init_normal_debugger(connection_attempts); XG_DBG(context).host_type = XDEBUG_NORMAL; } /* Check whether we're connected, or why not */ if (XG_DBG(context).socket >= 0) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Connected to debugging client: %s.", connection_attempts->d); xdebug_mark_debug_connection_pending(); if (!XG_DBG(context).handler->remote_init(&(XG_DBG(context)), XDEBUG_REQ)) { /* The request could not be started, ignore it then */ xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_ERR, "SES-INIT", "The debug session could not be started. Tried: %s.", connection_attempts->d); } else { /* All is well, turn off script time outs */ zend_unset_timeout(); EG(timeout_seconds) = 0; zend_set_timeout(EG(timeout_seconds), 0); } } else if (XG_DBG(context).socket == -1) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_ERR, "NOCON", "Could not connect to debugging client. Tried: %s.", connection_attempts->d); } else if (XG_DBG(context).socket == -2) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_ERR, "TIMEOUT", "Time-out connecting to debugging client, waited: " ZEND_LONG_FMT " ms. Tried: %s.", XINI_DBG(connect_timeout_ms), connection_attempts->d); } else if (XG_DBG(context).socket == -3) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_ERR, "NOPERM", "No permission connecting to debugging client (%s). This could be SELinux related.", connection_attempts->d); } xdebug_str_free(connection_attempts); } void xdebug_abort_debugger() { if (XG_DBG(remote_connection_enabled)) { xdebug_mark_debug_connection_not_active(); } } void xdebug_restart_debugger() { xdebug_abort_debugger(); xdebug_init_debugger(); } void xdebug_mark_debug_connection_active() { XG_DBG(remote_connection_enabled) = 1; XG_DBG(remote_connection_pid) = xdebug_get_pid(); } void xdebug_mark_debug_connection_pending() { XG_DBG(remote_connection_enabled) = 0; XG_DBG(remote_connection_pid) = 0; } void xdebug_mark_debug_connection_not_active() { if (XG_DBG(remote_connection_enabled)) { xdebug_close_socket(XG_DBG(context).socket); } XG_DBG(remote_connection_enabled) = 0; XG_DBG(remote_connection_pid) = 0; } bool xdebug_should_ignore(void) { const char *ignore_value; const char *found_in_global; ignore_value = xdebug_lib_find_in_globals("XDEBUG_IGNORE", &found_in_global); if (!ignore_value) { return false; } if ((strcmp(ignore_value, "no") == 0) || (strcmp(ignore_value, "0") == 0)) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_INFO, "IGN", "Not ignoring present 'XDEBUG_IGNORE' %s variable, because the value is '%s'.", found_in_global, ignore_value); return false; } xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_DEBUG, "IGN", "Not activating because an 'XDEBUG_IGNORE' %s variable is present, with value '%s'.", found_in_global, ignore_value); return true; } void xdebug_debug_init_if_requested_on_connect_to_client() { RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_STEP_DEBUG); if (xdebug_should_ignore()) { return; } if (!xdebug_is_debug_connection_active()) { xdebug_init_debugger(); } } void xdebug_debug_init_if_requested_on_error() { RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_STEP_DEBUG); if (!xdebug_lib_start_upon_error()) { return; } if (!xdebug_is_debug_connection_active()) { xdebug_init_debugger(); } } void xdebug_debug_init_if_requested_on_xdebug_break() { RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_STEP_DEBUG); if (xdebug_is_debug_connection_active()) { return; } if (xdebug_lib_start_if_mode_is_trigger(XDEBUG_MODE_STEP_DEBUG)) { xdebug_init_debugger(); } } static void xdebug_update_ide_key(char *new_key) { if (XG_DBG(ide_key)) { xdfree(XG_DBG(ide_key)); } XG_DBG(ide_key) = xdstrdup(new_key); } static int xdebug_handle_start_session() { int activate_session = 0; zval *dummy; char *dummy_env; /* Set session cookie if requested */ if ( (( (dummy = zend_hash_str_find(Z_ARR(PG(http_globals)[TRACK_VARS_ENV]), "XDEBUG_SESSION_START", sizeof("XDEBUG_SESSION_START") - 1)) != NULL ) || ( (dummy = zend_hash_str_find(Z_ARR(PG(http_globals)[TRACK_VARS_GET]), "XDEBUG_SESSION_START", sizeof("XDEBUG_SESSION_START") - 1)) != NULL ) || ( (dummy = zend_hash_str_find(Z_ARR(PG(http_globals)[TRACK_VARS_POST]), "XDEBUG_SESSION_START", sizeof("XDEBUG_SESSION_START") - 1)) != NULL )) && !SG(headers_sent) ) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "Found 'XDEBUG_SESSION_START' HTTP variable, with value '%s'", Z_STRVAL_P(dummy)); convert_to_string_ex(dummy); xdebug_update_ide_key(Z_STRVAL_P(dummy)); xdebug_setcookie("XDEBUG_SESSION", sizeof("XDEBUG_SESSION") - 1, Z_STRVAL_P(dummy), Z_STRLEN_P(dummy), 0, "/", 1, NULL, 0, 0, 1, 0); activate_session = 1; } else if ( (dummy_env = getenv("XDEBUG_SESSION_START")) != NULL ) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "Found 'XDEBUG_SESSION_START' ENV variable, with value '%s'", dummy_env); xdebug_update_ide_key(dummy_env); if (!SG(headers_sent)) { xdebug_setcookie("XDEBUG_SESSION", sizeof("XDEBUG_SESSION") - 1, XG_DBG(ide_key), strlen(XG_DBG(ide_key)), 0, "/", 1, NULL, 0, 0, 1, 0); } activate_session = 1; } else if (getenv("XDEBUG_CONFIG")) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "Found 'XDEBUG_CONFIG' ENV variable"); if (XG_DBG(ide_key) && *XG_DBG(ide_key) && !SG(headers_sent)) { xdebug_setcookie("XDEBUG_SESSION", sizeof("XDEBUG_SESSION") - 1, XG_DBG(ide_key), strlen(XG_DBG(ide_key)), 0, "/", 1, NULL, 0, 0, 1, 0); activate_session = 1; } } /* Make sure that if we have a trigger value configured, we don't start the session unless it matches */ if (activate_session && xdebug_lib_has_shared_secret()) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_INFO, "TRGSEC-LEGACY", "Not activating through legacy method because xdebug.trigger_value is set"); activate_session = 0; } return activate_session; } static void xdebug_handle_stop_session() { /* Remove session cookie if requested */ if ( (( zend_hash_str_find(Z_ARR(PG(http_globals)[TRACK_VARS_GET]), "XDEBUG_SESSION_STOP", sizeof("XDEBUG_SESSION_STOP") - 1) != NULL ) || ( zend_hash_str_find(Z_ARR(PG(http_globals)[TRACK_VARS_POST]), "XDEBUG_SESSION_STOP", sizeof("XDEBUG_SESSION_STOP") - 1) != NULL )) && !SG(headers_sent) ) { xdebug_setcookie("XDEBUG_SESSION", sizeof("XDEBUG_SESSION") - 1, (char*) "", 0, 0, "/", 1, NULL, 0, 0, 1, 0); } } void xdebug_debug_init_if_requested_at_startup(void) { char *found_trigger_value = NULL; if (XG_DBG(detached)) { return; } if (xdebug_is_debug_connection_active()) { return; } if (xdebug_should_ignore()) { return; } if ( xdebug_lib_start_with_request(XDEBUG_MODE_STEP_DEBUG) || (!xdebug_lib_never_start_with_request() && xdebug_handle_start_session()) || xdebug_lib_start_with_trigger(XDEBUG_MODE_STEP_DEBUG, &found_trigger_value) ) { if (found_trigger_value) { xdebug_update_ide_key(found_trigger_value); } xdebug_init_debugger(); } if (found_trigger_value) { xdfree(found_trigger_value); } xdebug_handle_stop_session(); } xdebug-3.4.3/src/debugger/com.h0000664000175000017500000000477015011062311015565 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_COM_H__ #define __HAVE_XDEBUG_COM_H__ #include "handlers.h" #define XDEBUG_CLIENT_PORT_S "9003" #define XDEBUG_CLOUD_PORT 9020L #if WIN32|WINNT # define SOCK_ERR INVALID_SOCKET # define SOCK_CONN_ERR SOCKET_ERROR # define SOCK_RECV_ERR SOCKET_ERROR #else # define SOCK_ERR -1 # define SOCK_CONN_ERR -1 # define SOCK_RECV_ERR -1 #endif #define SOCK_TIMEOUT_ERR -2 #define SOCK_ACCESS_ERR -3 #if WIN32|WINNT #define SCLOSE(a) closesocket(a) #define SSENDL(a,b,c) send(a,b,c,0) #define SSEND(a,b) send(a,b,strlen(b),0) #define SREAD(a,b,c) recv(a,b,c,0) #else #define SCLOSE(a) close(a) #define SSENDL(a,b,c) write(a,b,c) #define SSEND(a,b) write(a,b,strlen(b)) #define SREAD(a,b,c) read(a,b,c) #endif #define SENDMSG(socket, str) { \ char *message_buffer; \ \ message_buffer = str; \ SSEND(socket, message_buffer); \ xdfree(message_buffer); \ } void xdebug_close_socket(int socket); /* Remote connection activation and house keeping */ #define xdebug_is_debug_connection_active() !!XG_DBG(remote_connection_enabled) void xdebug_abort_debugger(void); void xdebug_restart_debugger(void); void xdebug_mark_debug_connection_active(void); void xdebug_mark_debug_connection_not_active(void); void xdebug_mark_debug_connection_pending(void); void xdebug_debug_init_if_requested_at_startup(void); void xdebug_debug_init_if_requested_on_connect_to_client(void); void xdebug_debug_init_if_requested_on_error(void); void xdebug_debug_init_if_requested_on_xdebug_break(void); #endif xdebug-3.4.3/src/debugger/debugger.c0000664000175000017500000007456515011062311016577 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "ext/standard/info.h" #include "zend_exceptions.h" #include "debugger_private.h" #include "lib/log.h" #include "lib/var.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); static size_t (*xdebug_orig_ub_write)(const char *string, size_t len); static size_t xdebug_ub_write(const char *string, size_t length); static void xdebug_line_list_dtor(xdebug_lines_list *line_list); void xdebug_init_debugger_globals(xdebug_debugger_globals_t *xg) { xg->breakpoint_count = 0; xg->ide_key = NULL; xg->stdout_mode = 0; xg->no_exec = 0; xg->context.program_name = NULL; xg->context.list.last_filename = NULL; xg->context.list.last_line = 0; xg->context.do_break = 0; xg->context.pending_breakpoint = NULL; xg->context.do_step = 0; xg->context.do_next = 0; xg->context.do_finish = 0; xg->context.do_connect_to_client = 0; xg->remote_connection_enabled = 0; xg->remote_connection_pid = 0; xg->breakpoints_allowed = 0; xg->suppress_return_value_step = 0; /* Capturing output */ if (sapi_module.ub_write != xdebug_ub_write) { xdebug_orig_ub_write = sapi_module.ub_write; sapi_module.ub_write = xdebug_ub_write; } /* Statistics and diagnostics */ xg->context.connected_hostname = NULL; xg->context.connected_port = 0; xg->context.detached_message = NULL; } static char *xdebug_debugger_get_ide_key(void) { char *ide_key; ide_key = XINI_DBG(ide_key_setting); if (ide_key && *ide_key) { return ide_key; } ide_key = getenv("DBGP_IDEKEY"); if (ide_key && *ide_key) { return ide_key; } return NULL; } void xdebug_debugger_reset_ide_key(char *envval) { if (XG_DBG(ide_key)) { xdfree(XG_DBG(ide_key)); } XG_DBG(ide_key) = xdstrdup(envval); } int xdebug_debugger_bailout_if_no_exec_requested(void) { /* We need to do this first before the executable clauses are called */ if (XG_DBG(no_exec) == 1) { php_printf("DEBUG SESSION ENDED"); return 1; } return 0; } void xdebug_debugger_set_program_name(zend_string *filename) { if (!XG_DBG(context).program_name) { XG_DBG(context).program_name = zend_string_copy(filename); } } /* Remote debugger helper functions */ static int xdebug_handle_hit_value(xdebug_brk_info *brk_info) { /* If this is a temporary breakpoint, disable the breakpoint */ if (brk_info->temporary) { brk_info->disabled = 1; } /* Increase hit counter */ brk_info->hit_count++; /* If the hit_value is 0, the condition check is disabled */ if (!brk_info->hit_value) { return 1; } switch (brk_info->hit_condition) { case XDEBUG_HIT_GREATER_EQUAL: if (brk_info->hit_count >= brk_info->hit_value) { return 1; } break; case XDEBUG_HIT_EQUAL: if (brk_info->hit_count == brk_info->hit_value) { return 1; } break; case XDEBUG_HIT_MOD: if (brk_info->hit_count % brk_info->hit_value == 0) { return 1; } break; case XDEBUG_HIT_DISABLED: return 1; break; } return 0; } int xdebug_do_eval(char *eval_string, zval *ret_zval, zend_string **return_message) { volatile int res = 1; zend_execute_data *original_execute_data = EG(current_execute_data); int original_no_extensions = EG(no_extensions); zend_object *original_exception = EG(exception); JMP_BUF *original_bailout = EG(bailout); /* Remember error reporting level and track errors */ XG_BASE(error_reporting_override) = EG(error_reporting); XG_BASE(error_reporting_overridden) = 1; EG(error_reporting) = 0; XG_DBG(context).inhibit_notifications = 1; XG_DBG(breakpoints_allowed) = 0; /* Reset exception in case we're triggered while being in xdebug_throw_exception_hook */ EG(exception) = NULL; /* Do evaluation */ zend_first_try { res = (zend_eval_string(eval_string, ret_zval, (char*) "xdebug://debug-eval") == SUCCESS); } zend_end_try(); if (EG(exception)) { if (return_message != NULL) { zend_class_entry *base_ce; zval *prop, rv; *return_message = NULL; base_ce = zend_get_exception_base(EG(exception)); if (base_ce) { prop = zend_read_property_ex(base_ce, EG(exception), ZSTR_KNOWN(ZEND_STR_MESSAGE), 1, &rv); if (prop) { *return_message = zval_get_string(prop); } } } if (!res) { zend_clear_exception(); } res = 0; } /* Clean up */ EG(error_reporting) = XG_BASE(error_reporting_override); XG_BASE(error_reporting_overridden) = 0; XG_DBG(breakpoints_allowed) = 1; XG_DBG(suppress_return_value_step) = 0; XG_DBG(context).inhibit_notifications = 0; EG(current_execute_data) = original_execute_data; EG(no_extensions) = original_no_extensions; EG(exception) = original_exception; EG(bailout) = original_bailout; return res; } bool xdebug_debugger_check_evaled_code(zend_string *filename_in, zend_string **filename_out) { char *end_marker; xdebug_eval_info *ei; if (!filename_in) { return false; } end_marker = ZSTR_VAL(filename_in) + ZSTR_LEN(filename_in) - strlen("eval()'d code"); if (end_marker >= ZSTR_VAL(filename_in) && strcmp("eval()'d code", end_marker) == 0) { if (xdebug_hash_find(XG_DBG(context).eval_id_lookup, ZSTR_VAL(filename_in), ZSTR_LEN(filename_in), (void *) &ei)) { *filename_out = zend_strpprintf(0, "dbgp://%u", ei->id); return true; } } return false; } int next_condition_met(function_stack_entry *fse) { if (XG_DBG(context).next_stack != NULL) { if ((XG_DBG(context).next_stack == XG_BASE(stack)) && (XG_DBG(context).next_level >= fse->level)) { return 1; } } else { if (XG_DBG(context).next_level >= fse->level) { return 1; } } return 0; } int finish_condition_met(function_stack_entry *fse, int break_at_return_scope) { if (!break_at_return_scope && fse->level < XG_DBG(context).finish_level) { return 1; } if (break_at_return_scope && fse->level <= XG_DBG(context).finish_level) { return 1; } if ( (fse->level == XG_DBG(context).finish_level) && (fse->function_nr > XG_DBG(context).finish_func_nr) ) { return 1; } return 0; } void xdebug_debugger_statement_call(zend_string *filename, int lineno) { xdebug_llist_element *le; xdebug_brk_info *extra_brk_info; function_stack_entry *fse; if (XG_DBG(context).do_connect_to_client) { XG_DBG(context).do_connect_to_client = 0; xdebug_debug_init_if_requested_on_connect_to_client(); xdebug_debug_init_if_requested_on_xdebug_break(); } if (!xdebug_is_debug_connection_active()) { return; } if (!XG_BASE(stack) || !XDEBUG_VECTOR_TAIL(XG_BASE(stack))) { return; } /* Fetch top level fse */ fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); XG_DBG(suppress_return_value_step) = 0; if (XG_DBG(context).do_break) { xdebug_brk_info *brk_info = XG_DBG(context).pending_breakpoint; XG_DBG(context).do_break = 0; XG_DBG(context).pending_breakpoint = NULL; if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), filename, lineno, XDEBUG_BREAK, NULL, 0, NULL, brk_info, NULL)) { xdebug_mark_debug_connection_not_active(); return; } return; } /* Check for "finish" */ if (XG_DBG(context).do_finish && finish_condition_met(fse, 0)) { XG_DBG(context).do_finish = 0; if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), filename, lineno, XDEBUG_STEP, NULL, 0, NULL, NULL, NULL)) { xdebug_mark_debug_connection_not_active(); return; } return; } /* Check for "next" */ if (XG_DBG(context).do_next && next_condition_met(fse)) { XG_DBG(context).do_next = 0; if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), filename, lineno, XDEBUG_STEP, NULL, 0, NULL, NULL, NULL)) { xdebug_mark_debug_connection_not_active(); return; } return; } /* Check for "step" */ if (XG_DBG(context).do_step) { XG_DBG(context).do_step = 0; if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), filename, lineno, XDEBUG_STEP, NULL, 0, NULL, NULL, NULL)) { xdebug_mark_debug_connection_not_active(); return; } return; } if (fse->has_line_breakpoints && XG_DBG(context).line_breakpoints) { int break_ok, res; zval retval; for (le = XDEBUG_LLIST_HEAD(XG_DBG(context).line_breakpoints); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { extra_brk_info = XDEBUG_LLIST_VALP(le); if (XG_DBG(context).handler->break_on_line(&(XG_DBG(context)), extra_brk_info, filename, lineno)) { break_ok = 1; /* Breaking is allowed by default */ /* Check if we have a condition set for it */ if (extra_brk_info->condition) { /* If there is a condition, we disable breaking by * default and only enabled it when the code evaluates * to TRUE */ break_ok = 0; /* Remember error reporting level */ res = xdebug_do_eval(extra_brk_info->condition, &retval, NULL); if (res) { break_ok = Z_TYPE(retval) == IS_TRUE; zval_dtor(&retval); } } if (break_ok && xdebug_handle_hit_value(extra_brk_info)) { if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), filename, lineno, XDEBUG_BREAK, NULL, 0, NULL, extra_brk_info, NULL)) { xdebug_mark_debug_connection_not_active(); break; } return; } } } } } void xdebug_debugger_throw_exception_hook(zend_object *exception, zval *file, zval *line, zval *code, char *code_str, zval *message) { zend_class_entry *exception_ce = exception->ce; xdebug_brk_info *extra_brk_info; /* Start JIT if requested and not yet enabled */ xdebug_debug_init_if_requested_on_error(); if (!xdebug_is_debug_connection_active() || !XG_DBG(breakpoints_allowed)) { return; } { int exception_breakpoint_found = 0; XG_DBG(suppress_return_value_step) = 1; /* Check if we have a wild card exception breakpoint */ if (xdebug_hash_find(XG_DBG(context).exception_breakpoints, "*", 1, (void *) &extra_brk_info)) { exception_breakpoint_found = 1; } else { /* Check if we have a breakpoint on this exception or its parent classes */ zend_class_entry *ce_ptr = exception_ce; /* Check if we have a breakpoint on this exception or its parent classes */ do { if (xdebug_hash_find(XG_DBG(context).exception_breakpoints, (char *) STR_NAME_VAL(ce_ptr->name), STR_NAME_LEN(ce_ptr->name), (void *) &extra_brk_info)) { exception_breakpoint_found = 1; } ce_ptr = ce_ptr->parent; } while (!exception_breakpoint_found && ce_ptr); } #if 0 if (XG_DBG(context).resolved_breakpoints && exception_breakpoint_found) { XG_DBG(context).handler->resolve_breakpoints(&(XG_DBG(context)), extra_brk_info); } #endif if (exception_breakpoint_found && xdebug_handle_hit_value(extra_brk_info)) { if ( !XG_DBG(context).handler->remote_breakpoint( &(XG_DBG(context)), XG_BASE(stack), zend_get_executed_filename_ex(), zend_get_executed_lineno(), XDEBUG_BREAK, (char*) STR_NAME_VAL(exception_ce->name), code_str ? code_str : ((code && Z_TYPE_P(code) == IS_STRING) ? Z_STRVAL_P(code) : NULL), message ? Z_STRVAL_P(message) : "", extra_brk_info, NULL ) ) { xdebug_mark_debug_connection_not_active(); } } } } void xdebug_debugger_error_cb(zend_string *error_filename, int error_lineno, int type, char *error_type_str, char *buffer) { xdebug_brk_info *extra_brk_info = NULL; /* Start JIT if requested and not yet enabled */ xdebug_debug_init_if_requested_on_error(); if (!xdebug_is_debug_connection_active() || !XG_DBG(breakpoints_allowed)) { return; } /* Send notification with warning/notice/error information */ if (XG_DBG(context).send_notifications && !XG_DBG(context).inhibit_notifications) { if (!XG_DBG(context).handler->remote_notification(&(XG_DBG(context)), error_filename, error_lineno, type, error_type_str, buffer)) { xdebug_mark_debug_connection_not_active(); } } /* Check for the pseudo exceptions to allow breakpoints on PHP error statuses */ if ( xdebug_hash_find(XG_DBG(context).exception_breakpoints, error_type_str, strlen(error_type_str), (void *) &extra_brk_info) || xdebug_hash_find(XG_DBG(context).exception_breakpoints, "*", 1, (void *) &extra_brk_info) ) { if (xdebug_handle_hit_value(extra_brk_info)) { char *type_str = xdebug_sprintf("%ld", type); if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), error_filename, error_lineno, XDEBUG_BREAK, error_type_str, type_str, buffer, extra_brk_info, NULL)) { xdebug_mark_debug_connection_not_active(); } xdfree(type_str); } } } static bool handle_function_breakpoints(function_stack_entry *fse, int breakpoint_type, zval *return_value) { char *tmp_name = NULL; size_t tmp_len = 0; xdebug_brk_info *extra_brk_info = NULL; /* Short circuit if there are no function breakpoints */ if (!XG_DBG(context).function_breakpoints || XG_DBG(context).function_breakpoints->size == 0) { return true; } /* Function breakpoints */ if (fse->function.type == XFUNC_NORMAL) { tmp_len = 2 + ZSTR_LEN(fse->function.function) + 1; tmp_name = xdmalloc(tmp_len); /* We intentionally do not use xdebug_sprintf because it can create a bottleneck in large * codebases due to setlocale calls. We don't care about the locale here. */ snprintf( tmp_name, tmp_len, "%c/%s", (breakpoint_type & XDEBUG_BREAKPOINT_TYPE_CALL) ? 'C' : 'R', ZSTR_VAL(fse->function.function) ); } /* class->function breakpoints */ else if (fse->function.type == XFUNC_MEMBER || fse->function.type == XFUNC_STATIC_MEMBER) { /* Using strlen(ZSTR_VAL(...)) here to cut of the string at the first \0, which is needed * for anonymous classes, in combination with the snprintf() below */ tmp_len = 2 + ZSTR_LEN(fse->function.object_class) + 2 + ZSTR_LEN(fse->function.function) + 1; tmp_name = xdmalloc(tmp_len); /* We intentionally do not use xdebug_sprintf because it can create a bottleneck in large * codebases due to setlocale calls. We don't care about the locale here. */ snprintf( tmp_name, tmp_len, "%c/%s::%s", (breakpoint_type & XDEBUG_BREAKPOINT_TYPE_CALL) ? 'C' : 'R', ZSTR_VAL(fse->function.object_class), ZSTR_VAL(fse->function.function) ); } /* Unknown */ else { return true; } if (xdebug_hash_find(XG_DBG(context).function_breakpoints, tmp_name, tmp_len - 1, (void *) &extra_brk_info)) { /* Yup, breakpoint found, call handler if the breakpoint is not * disabled AND handle_hit_value is happy */ if (!extra_brk_info->disabled && (extra_brk_info->function_break_type == (breakpoint_type & XDEBUG_BREAKPOINT_TYPES_MASK))) { if (xdebug_handle_hit_value(extra_brk_info)) { if (fse->user_defined == XDEBUG_BUILT_IN || (breakpoint_type & XDEBUG_BREAKPOINT_TYPE_RETURN)) { if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), fse->filename, fse->lineno, XDEBUG_BREAK, NULL, 0, NULL, extra_brk_info, return_value)) { xdfree(tmp_name); return false; } } else { XG_DBG(context).do_break = 1; XG_DBG(context).pending_breakpoint = extra_brk_info; } } } } xdfree(tmp_name); return true; } void xdebug_debugger_set_has_line_breakpoints(function_stack_entry *fse) { if (fse->has_line_breakpoints) { return; } fse->has_line_breakpoints = true; xdebug_log_ex( XLOG_CHAN_DEBUG, XLOG_DEBUG, "HLB", "Setting 'has_line_breakpoints on %s (%s:%d)", fse->function.function ? ZSTR_VAL(fse->function.function) : "{no func}", ZSTR_VAL(fse->filename), fse->lineno ); } static void mark_fse_as_having_line_breakpoints(function_stack_entry *fse) { xdebug_llist_element *le; if (!XG_DBG(context).line_breakpoints || XG_DBG(context).line_breakpoints->size == 0) { return; } /* loop over all line breakpoints until one hits, and if so, turn on 'has_line_breakpoints' */ for (le = XDEBUG_LLIST_HEAD(XG_DBG(context).line_breakpoints); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { xdebug_brk_info *extra_brk_info = XDEBUG_LLIST_VALP(le); zend_string *executed_filename = zend_get_executed_filename_ex(); if (!executed_filename) { continue; } if (fse->function.type == XFUNC_EVAL) { zend_string *resolved_filename; if (!xdebug_debugger_check_evaled_code(executed_filename, &resolved_filename)) { continue; } if (!zend_string_equals(extra_brk_info->filename, resolved_filename)) { zend_string_release(resolved_filename); continue; } zend_string_release(resolved_filename); } else if (!zend_string_equals(extra_brk_info->filename, executed_filename)) { continue; } if (extra_brk_info->resolved_lineno >= fse->op_array->line_start && extra_brk_info->resolved_lineno <= fse->op_array->line_end) { xdebug_debugger_set_has_line_breakpoints(fse); return; } } } /* Returns false if something is wrong with the breakpoint */ static bool handle_breakpoints(function_stack_entry *fse, int breakpoint_type, zval *return_value) { /* If 'has_line_breakpoints' hasn't been marked, either use the resolve * list if it exists, or otherwise mark it as 'true' */ if (!fse->has_line_breakpoints) { mark_fse_as_having_line_breakpoints(fse); } if (!handle_function_breakpoints(fse, breakpoint_type, return_value)) { return false; } if ( (XG_DBG(context).breakpoint_include_return_value) && (breakpoint_type & XDEBUG_BREAKPOINT_TYPE_RETURN) && !(XG_DBG(suppress_return_value_step)) && return_value ) { if (XG_DBG(context).do_step) { XG_DBG(context).do_step = 0; } else if (XG_DBG(context).do_finish && finish_condition_met(fse, 1)) { XG_DBG(context).do_finish = 0; } else { return true; } if (!XG_DBG(context).handler->remote_breakpoint(&(XG_DBG(context)), XG_BASE(stack), fse->filename, fse->lineno, XDEBUG_BREAK, NULL, 0, NULL, NULL, return_value)) { return false; } } return true; } void xdebug_debugger_handle_breakpoints(function_stack_entry *fse, int breakpoint_type, zval *return_value) { if (xdebug_is_debug_connection_active() && XG_DBG(breakpoints_allowed)) { if (!handle_breakpoints(fse, breakpoint_type, return_value)) { xdebug_mark_debug_connection_not_active(); } } } static size_t xdebug_ub_write(const char *string, size_t length) { if (xdebug_is_debug_connection_active()) { if (-1 == XG_DBG(context).handler->remote_stream_output(string, length)) { return 0; } } return xdebug_orig_ub_write(string, length); } static void xdebug_hook_output_handlers() { /* Override output handler for capturing output */ if (xdebug_orig_ub_write == NULL) { xdebug_orig_ub_write = sapi_module.ub_write; sapi_module.ub_write = xdebug_ub_write; } } static void xdebug_unhook_output_handlers() { /* Restore original output handler */ sapi_module.ub_write = xdebug_orig_ub_write; xdebug_orig_ub_write = NULL; } void xdebug_debugger_zend_startup(void) { /* Hook output handlers (header and output writer) */ xdebug_hook_output_handlers(); } void xdebug_debugger_zend_shutdown(void) { /* Remove our hooks to output handlers (header and output writer) */ xdebug_unhook_output_handlers(); } void xdebug_debugger_minit(void) { XG_DBG(breakpoint_count) = 0; } void xdebug_debugger_minfo(void) { php_info_print_table_start(); php_info_print_table_header(2, "Debugger", "enabled"); php_info_print_table_row(2, "IDE Key", XG_DBG(ide_key)); php_info_print_table_end(); } void xdebug_debugger_rinit(void) { char *idekey; xdebug_disable_opcache_optimizer(); /* Get the ide key for this session */ XG_DBG(ide_key) = NULL; idekey = xdebug_debugger_get_ide_key(); if (idekey && *idekey) { if (XG_DBG(ide_key)) { xdfree(XG_DBG(ide_key)); } XG_DBG(ide_key) = xdstrdup(idekey); } XG_DBG(no_exec) = 0; xdebug_lib_set_active_symbol_table(NULL); /* Check if we have this special get variable that stops a debugging * request without executing any code */ { zend_string *stop_no_exec = zend_string_init(ZEND_STRL("XDEBUG_SESSION_STOP_NO_EXEC"), 0); if ( ( ( zend_hash_find(Z_ARR(PG(http_globals)[TRACK_VARS_GET]), stop_no_exec) != NULL ) || ( zend_hash_find(Z_ARR(PG(http_globals)[TRACK_VARS_POST]), stop_no_exec) != NULL ) ) && !SG(headers_sent) ) { xdebug_setcookie("XDEBUG_SESSION", sizeof("XDEBUG_SESSION") - 1, (char*) "", 0, 0, "/", 1, NULL, 0, 0, 1, 0); XG_DBG(no_exec) = 1; } zend_string_release(stop_no_exec); } xdebug_mark_debug_connection_not_active(); XG_DBG(breakpoints_allowed) = 1; XG_DBG(suppress_return_value_step) = 0; XG_DBG(detached) = 0; XG_DBG(breakable_lines_map) = xdebug_hash_alloc(2048, (xdebug_hash_dtor_t) xdebug_line_list_dtor); XG_DBG(function_count) = 0; XG_DBG(class_count) = 0; /* Initialize some debugger context properties */ XG_DBG(context).program_name = NULL; XG_DBG(context).list.last_filename = NULL; XG_DBG(context).list.last_line = 0; XG_DBG(context).do_break = 0; XG_DBG(context).pending_breakpoint = NULL; XG_DBG(context).do_step = 0; XG_DBG(context).do_next = 0; XG_DBG(context).do_finish = 0; XG_DBG(context).do_connect_to_client = 0; /* Statistics and diagnostics */ XG_DBG(context).connected_hostname = NULL; XG_DBG(context).connected_port = 0; XG_DBG(context).detached_message = NULL; } void xdebug_debugger_post_deactivate(void) { if (XG_DBG(remote_connection_enabled)) { XG_DBG(context).handler->remote_deinit(&(XG_DBG(context))); xdebug_close_socket(XG_DBG(context).socket); } if (XG_DBG(context).program_name) { zend_string_release(XG_DBG(context).program_name); } if (XG_DBG(ide_key)) { xdfree(XG_DBG(ide_key)); XG_DBG(ide_key) = NULL; } if (XG_DBG(context.list.last_filename)) { zend_string_release(XG_DBG(context).list.last_filename); XG_DBG(context).list.last_filename = NULL; } xdebug_hash_destroy(XG_DBG(breakable_lines_map)); XG_DBG(breakable_lines_map) = NULL; if (XG_DBG(context).connected_hostname) { xdfree(XG_DBG(context).connected_hostname); XG_DBG(context).connected_hostname = NULL; } if (XG_DBG(context).detached_message) { xdfree(XG_DBG(context).detached_message); XG_DBG(context).detached_message = NULL; } } xdebug_set *xdebug_debugger_get_breakable_lines_from_oparray(zend_op_array *opa) { int i; xdebug_set *tmp; tmp = xdebug_set_create(opa->line_end); for (i = 0; i < opa->last; i++ ) { if (opa->opcodes[i].opcode == ZEND_EXT_STMT ) { xdebug_set_add(tmp, opa->opcodes[i].lineno); } } return tmp; } /* {{{ function/lines map collection helpers */ static void xdebug_function_lines_map_dtor(xdebug_function_lines_map_item *lines_map) { xdebug_set_free(lines_map->lines_breakable); xdfree(lines_map); } static void xdebug_line_list_dtor(xdebug_lines_list *line_list) { size_t i; for (i = 0; i < line_list->count; i++) { xdebug_function_lines_map_dtor(line_list->functions[i]); } xdfree(line_list->functions); xdfree(line_list); } static xdebug_lines_list *get_file_function_line_list(zend_string *filename) { xdebug_lines_list *lines_list; if (xdebug_hash_find(XG_DBG(breakable_lines_map), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &lines_list)) { return lines_list; } lines_list = xdmalloc(sizeof(xdebug_lines_list)); lines_list->count = 0; lines_list->size = 0; lines_list->functions = NULL; xdebug_hash_add(XG_DBG(breakable_lines_map), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) lines_list); return lines_list; } static void add_function_to_lines_list(xdebug_lines_list *lines_list, zend_op_array *opa) { xdebug_function_lines_map_item *map_item = xdmalloc(sizeof(xdebug_function_lines_map_item)); map_item->line_start = opa->line_start; map_item->line_end = opa->line_end; map_item->line_span = opa->line_end - opa->line_start; map_item->lines_breakable = xdebug_debugger_get_breakable_lines_from_oparray(opa); if (lines_list->count >= lines_list->size) { lines_list->size = lines_list->size == 0 ? 16 : lines_list->size * 2; lines_list->functions = xdrealloc(lines_list->functions, sizeof(xdebug_function_lines_map_item *) * lines_list->size); } lines_list->functions[lines_list->count] = map_item; lines_list->count++; #if PHP_VERSION_ID >= 80100 if (opa->num_dynamic_func_defs) { uint32_t i; for (i = 0; i < opa->num_dynamic_func_defs; i++) { add_function_to_lines_list(lines_list, opa->dynamic_func_defs[i]); } } #endif } /* }}} */ static void resolve_breakpoints_for_function(xdebug_lines_list *lines_list, zend_op_array *opa) { add_function_to_lines_list(lines_list, opa); } #if PHP_VERSION_ID >= 80400 static void resolve_breakpoints_for_property_hooks(xdebug_lines_list *file_function_lines_list, zend_class_entry *ce, zend_string *filename) { int i; zend_property_info *prop_info; ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop_info) { if (!prop_info->hooks) { continue; } for (i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { if (!prop_info->hooks[i]) { continue; } if (!ZEND_USER_CODE((&prop_info->hooks[i]->op_array)->type)) { continue; } /* Only resolve if the file names are the same. This is needed in case * of inheritance or traits where op arrays from other files might get introduced */ if (ZSTR_LEN(filename) != ZSTR_LEN((&prop_info->hooks[i]->op_array)->filename)) { continue; } if (strcmp(ZSTR_VAL(filename), ZSTR_VAL((&prop_info->hooks[i]->op_array)->filename)) != 0) { continue; } resolve_breakpoints_for_function(file_function_lines_list, &prop_info->hooks[i]->op_array); } } ZEND_HASH_FOREACH_END(); } #endif static void resolve_breakpoints_for_class(xdebug_lines_list *file_function_lines_list, zend_class_entry *ce, zend_string *filename) { zend_op_array *function_op_array; ZEND_HASH_FOREACH_PTR(&ce->function_table, function_op_array) { if (!ZEND_USER_CODE(function_op_array->type)) { continue; } /* Only resolve if the file names are the same. This is needed in case * of inheritance or traits where op arrays from other files might get introduced */ if (ZSTR_LEN(filename) != ZSTR_LEN(function_op_array->filename)) { continue; } if (strcmp(ZSTR_VAL(filename), ZSTR_VAL(function_op_array->filename)) != 0) { continue; } resolve_breakpoints_for_function(file_function_lines_list, function_op_array); } ZEND_HASH_FOREACH_END(); #if PHP_VERSION_ID >= 80400 resolve_breakpoints_for_property_hooks(file_function_lines_list, ce, filename); #endif } void xdebug_debugger_compile_file(zend_op_array *op_array) { zend_op_array *function_op_array; zend_class_entry *class_entry; xdebug_lines_list *file_function_lines_list; RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_STEP_DEBUG); /* The breakable_lines_map can not be set if another extension compiles * scripts during RINIT */ if (!XG_DBG(breakable_lines_map)) { return; } file_function_lines_list = get_file_function_line_list(op_array->filename); ZEND_HASH_REVERSE_FOREACH_PTR(CG(function_table), function_op_array) { if (_idx == XG_DBG(function_count)) { break; } if (!ZEND_USER_CODE(function_op_array->type)) { continue; } resolve_breakpoints_for_function(file_function_lines_list, function_op_array); } ZEND_HASH_FOREACH_END(); XG_DBG(function_count) = CG(function_table)->nNumUsed; ZEND_HASH_REVERSE_FOREACH_PTR(CG(class_table), class_entry) { if (_idx == XG_DBG(class_count)) { break; } if (class_entry->type == ZEND_INTERNAL_CLASS) { continue; } resolve_breakpoints_for_class(file_function_lines_list, class_entry, op_array->filename); } ZEND_HASH_FOREACH_END(); XG_DBG(class_count) = CG(class_table)->nNumUsed; add_function_to_lines_list(file_function_lines_list, op_array); if (!xdebug_is_debug_connection_active()) { return; } XG_DBG(context).handler->resolve_breakpoints( &(XG_DBG(context)), op_array->filename ); } static void resolve_breakpoints_for_eval(int eval_id, zend_op_array *opa) { xdebug_lines_list *lines_list; char *eval_filename = xdebug_sprintf("dbgp://%d", eval_id); zend_string *eval_string = zend_string_init(eval_filename, strlen(eval_filename), 0); lines_list = get_file_function_line_list(eval_string); add_function_to_lines_list(lines_list, opa); resolve_breakpoints_for_function(lines_list, opa); if (!xdebug_is_debug_connection_active()) { zend_string_release(eval_string); xdfree(eval_filename); return; } XG_DBG(context).handler->resolve_breakpoints( &(XG_DBG(context)), eval_string ); zend_string_release(eval_string); xdfree(eval_filename); } void xdebug_debugger_register_eval(function_stack_entry *fse) { if (xdebug_is_debug_connection_active() && XG_DBG(context).handler->register_eval_id) { int eval_id = XG_DBG(context).handler->register_eval_id(&(XG_DBG(context)), fse); resolve_breakpoints_for_eval(eval_id, fse->op_array); } } void xdebug_debugger_restart_if_pid_changed() { zend_ulong pid; if (!xdebug_is_debug_connection_active()) { return; } pid = xdebug_get_pid(); /* Start debugger if previously a connection was established and this * process no longer has the same PID */ if (XG_DBG(remote_connection_pid) != pid) { xdebug_restart_debugger(); } } PHP_FUNCTION(xdebug_break) { RETURN_FALSE_IF_MODE_IS_NOT(XDEBUG_MODE_STEP_DEBUG); xdebug_debug_init_if_requested_on_xdebug_break(); if (!xdebug_is_debug_connection_active()) { RETURN_FALSE; } XG_DBG(context).do_break = 1; XG_DBG(context).pending_breakpoint = NULL; RETURN_TRUE; } PHP_FUNCTION(xdebug_connect_to_client) { RETURN_FALSE_IF_MODE_IS_NOT(XDEBUG_MODE_STEP_DEBUG); XG_DBG(context).do_connect_to_client = 1; RETURN_TRUE; } PHP_FUNCTION(xdebug_is_debugger_active) { RETURN_BOOL(xdebug_is_debug_connection_active()); } PHP_FUNCTION(xdebug_notify) { function_stack_entry *fse; zval *data; RETURN_FALSE_IF_MODE_IS_NOT(XDEBUG_MODE_STEP_DEBUG); if (!xdebug_is_debug_connection_active()) { RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &data) == FAILURE) { return; } fse = xdebug_get_stack_frame(0); XG_DBG(context).handler->user_notification( &(XG_DBG(context)), fse->filename, fse->lineno, data ); RETURN_TRUE; } xdebug-3.4.3/src/debugger/debugger.h0000664000175000017500000000756615011062311016601 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_DEBUGGER_H__ #define __XDEBUG_DEBUGGER_H__ #include "com.h" typedef struct _xdebug_debugger_globals_t { int status; int reason; const char *lastcmd; char *lasttransid; zval *current_return_value; zend_bool remote_connection_enabled; zend_ulong remote_connection_pid; zend_bool breakpoints_allowed; zend_bool suppress_return_value_step; zend_bool detached; xdebug_con context; unsigned int breakpoint_count; unsigned int no_exec; char *ide_key; /* As Xdebug uses it, from environment, USER, USERNAME or empty */ /* breakpoint resolving */ size_t function_count; size_t class_count; xdebug_hash *breakable_lines_map; /* output redirection */ int stdout_mode; } xdebug_debugger_globals_t; typedef struct _xdebug_debugger_settings_t { /* Cloud */ char *cloud_id; char *cloud_shared_key; /* Step Debugger */ zend_long client_port; /* 9003 */ char *client_host; /* localhost */ zend_bool discover_client_host; /* (try to) connect back to the HTTP requestor */ char *client_discovery_header; /* User configured header to check for forwarded IP address */ zend_long connect_timeout_ms; /* Timeout in MS for remote connections */ char *ide_key_setting; /* Set through php.ini and friends */ } xdebug_debugger_settings_t; PHP_INI_MH(OnUpdateDebugMode); void xdebug_init_debugger_globals(xdebug_debugger_globals_t *xg); #define XDEBUG_RETURN_VALUE_VAR_NAME "__RETURN_VALUE" void xdebug_debugger_reset_ide_key(char *envval); int xdebug_debugger_bailout_if_no_exec_requested(void); void xdebug_debugger_set_program_name(zend_string *filename); void xdebug_debugger_register_eval(function_stack_entry *fse); void xdebug_debugger_restart_if_pid_changed(void); xdebug_set *xdebug_debugger_get_breakable_lines_from_oparray(zend_op_array *opa); int xdebug_do_eval(char *eval_string, zval *ret_zval, zend_string **return_message); bool xdebug_debugger_check_evaled_code(zend_string *filename_in, zend_string **filename_out); void xdebug_debugger_set_has_line_breakpoints(function_stack_entry *fse); void xdebug_debugger_statement_call(zend_string *filename, int lineno); void xdebug_debugger_throw_exception_hook(zend_object *exception, zval *file, zval *line, zval *code, char *code_str, zval *message); void xdebug_debugger_error_cb(zend_string *error_filename, int error_lineno, int type, char *error_type_str, char *buffer); void xdebug_debugger_handle_breakpoints(function_stack_entry *fse, int breakpoint_type, zval *return_value); void xdebug_debugger_zend_startup(void); void xdebug_debugger_zend_shutdown(void); void xdebug_debugger_minit(void); void xdebug_debugger_minfo(void); void xdebug_debugger_rinit(void); void xdebug_debugger_post_deactivate(void); void xdebug_debugger_compile_file(zend_op_array *op_array); PHP_FUNCTION(xdebug_break); #endif xdebug-3.4.3/src/debugger/debugger_private.h0000664000175000017500000000345715011062311020326 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_DEBUGGER_PRIVATE_H__ #define __XDEBUG_DEBUGGER_PRIVATE_H__ #include "debugger.h" #include "lib/lib.h" typedef struct _fd_buf fd_buf; struct _fd_buf { char *buffer; int buffer_size; }; typedef struct _xdebug_function_lines_map_item xdebug_function_lines_map_item; struct _xdebug_function_lines_map_item { size_t line_start; size_t line_end; size_t line_span; xdebug_set *lines_breakable; }; typedef struct _xdebug_lines_list xdebug_lines_list; struct _xdebug_lines_list { size_t count; /* How many function/line mappings are in the list */ size_t size; /* How many function/line mappings are allocated */ xdebug_function_lines_map_item **functions; }; #define XG_DBG(v) (XG(globals.debugger.v)) #define XINI_DBG(v) (XG(settings.debugger.v)) bool xdebug_should_ignore(void); #endif xdebug-3.4.3/src/debugger/handlers.c0000664000175000017500000000454615011062311016603 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "php_xdebug.h" #include "com.h" #include "handlers.h" #include "handler_dbgp.h" #include "lib/mm.h" xdebug_brk_info *xdebug_brk_info_ctor(void) { xdebug_brk_info *tmp = xdmalloc(sizeof(xdebug_brk_info)); tmp->id = -1; tmp->brk_type = -1; tmp->resolved = XDEBUG_BRK_UNRESOLVED; tmp->filename = NULL; tmp->original_lineno = 0; tmp->resolved_lineno = 0; tmp->classname = NULL; tmp->functionname = NULL; tmp->function_break_type = 0; tmp->exceptionname = NULL; tmp->condition = NULL; tmp->disabled = 0; tmp->temporary = 0; tmp->hit_count = 0; tmp->hit_value = 0; tmp->hit_condition = XDEBUG_HIT_DISABLED; return tmp; } void xdebug_brk_info_dtor(xdebug_brk_info *brk_info) { if (brk_info->classname) { xdfree(brk_info->classname); } if (brk_info->functionname) { xdfree(brk_info->functionname); } if (brk_info->filename) { zend_string_release(brk_info->filename); } if (brk_info->exceptionname) { xdfree(brk_info->exceptionname); } if (brk_info->condition) { xdfree(brk_info->condition); } xdfree(brk_info); } void xdebug_hash_brk_dtor(xdebug_brk_info *brk_info) { xdebug_brk_info_dtor(brk_info); } void xdebug_llist_brk_dtor(void *dummy, xdebug_brk_info *brk_info) { xdebug_brk_info_dtor(brk_info); } void xdebug_hash_eval_info_dtor(xdebug_eval_info *ei) { ei->refcount--; if (ei->refcount == 0) { zend_string_release(ei->contents); xdfree(ei); } } xdebug-3.4.3/src/debugger/handlers.h0000664000175000017500000001437215011062311016606 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_HANDLERS_H__ #define __HAVE_XDEBUG_HANDLERS_H__ #include "php_xdebug.h" #include "lib/compat.h" #include "lib/llist.h" #include "lib/hash.h" #include "lib/lib.h" #include "lib/usefulstuff.h" #include "lib/vector.h" #include "debugger_private.h" typedef struct _xdebug_brk_admin xdebug_brk_admin; typedef struct _xdebug_brk_info xdebug_brk_info; typedef struct _xdebug_brk_span xdebug_brk_span; typedef struct _xdebug_eval_info xdebug_eval_info; typedef struct _xdebug_con xdebug_con; typedef struct _xdebug_debug_list xdebug_debug_list; typedef struct _xdebug_remote_handler xdebug_remote_handler; typedef struct _xdebug_remote_handler_info xdebug_remote_handler_info; struct _xdebug_debug_list { zend_string *last_filename; int last_line; }; #define XDEBUG_BREAKPOINT_TYPE_LINE 0x01 #define XDEBUG_BREAKPOINT_TYPE_CONDITIONAL 0x02 #define XDEBUG_BREAKPOINT_TYPE_CALL 0x04 #define XDEBUG_BREAKPOINT_TYPE_RETURN 0x08 #define XDEBUG_BREAKPOINT_TYPE_EXCEPTION 0x10 #define XDEBUG_BREAKPOINT_TYPE_WATCH 0x20 #define XDEBUG_BREAKPOINT_TYPES_MASK 0x3F #define XDEBUG_BREAKPOINT_TYPE_EXTERNAL 0x40 // user-defined PHP function #define XDEBUG_BREAKPOINT_TYPE_NAME(v) (xdebug_breakpoint_types[(int)(log2(v))]).name struct _xdebug_brk_admin { int id; int type; char *key; }; #define XDEBUG_NORMAL 0x01 #define XDEBUG_CLOUD 0x02 #define XDEBUG_CLOUD_FROM_TRIGGER_VALUE 0x03 struct _xdebug_con { int socket; int host_type; /* XDEBUG_NORMAL, XDEBUG_CLOUD, XDEBUG_CLOUD_FROM_TRIGGER_VALUE */ void *options; xdebug_remote_handler *handler; fd_buf *buffer; zend_string *program_name; xdebug_hash *breakpoint_list; xdebug_hash *function_breakpoints; xdebug_hash *eval_id_lookup; int eval_id_sequence; xdebug_llist *line_breakpoints; xdebug_hash *exception_breakpoints; xdebug_debug_list list; int do_break; xdebug_brk_info *pending_breakpoint; xdebug_vector *next_stack; int do_step; int do_next; int next_level; int do_finish; int do_connect_to_client; int finish_level; int finish_func_nr; int send_notifications; int inhibit_notifications; int resolved_breakpoints; int breakpoint_details; int breakpoint_include_return_value; /* Statistics and diagnostics */ char *connected_hostname; int connected_port; char *detached_message; }; #define XDEBUG_BRK_UNRESOLVED 0 #define XDEBUG_BRK_RESOLVED 1 #define XDEBUG_HIT_DISABLED 0 #define XDEBUG_HIT_GREATER_EQUAL 1 #define XDEBUG_HIT_EQUAL 2 #define XDEBUG_HIT_MOD 3 #define XDEBUG_RESOLVED_SPAN_MAX 2147483647 struct _xdebug_brk_info { int id; int brk_type; int resolved; char *classname; char *functionname; char *exceptionname; int function_break_type; /* XDEBUG_BRK_FUNC_* */ zend_string *filename; int original_lineno; /* line number that was set through breakpoint_set */ int resolved_lineno; /* line number after resolving, initialised with 'original_lineno' */ char *condition; int disabled; int temporary; int hit_count; int hit_value; int hit_condition; }; struct _xdebug_eval_info { int id; int refcount; zend_string *contents; }; struct _xdebug_remote_handler { /* Init / deinit */ int (*remote_init)(xdebug_con *h, int mode); int (*remote_deinit)(xdebug_con *h); /* Stack messages */ int (*remote_error)(xdebug_con *h, int type, char *exception_type, char *message, const char *location, const unsigned int line, xdebug_vector *stack); /* Breakpoints */ int (*break_on_line)(xdebug_con *h, xdebug_brk_info *brk, zend_string *filename, int lineno); int (*remote_breakpoint)(xdebug_con *h, xdebug_vector *stack, zend_string *filename, long lineno, int type, char *exception, char *code, const char *message, xdebug_brk_info *brk_info, zval *return_value); int (*resolve_breakpoints)(xdebug_con *h, zend_string *opa); /* Output redirection */ int (*remote_stream_output)(const char *string, unsigned int length); /* Notifications & Logging */ int (*remote_notification)(xdebug_con *h, zend_string *file, long lineno, int type, char *type_string, char *message); int (*user_notification)(xdebug_con *h, zend_string *filename, long lineno, zval *data); /* Eval ID registration and removal */ int (*register_eval_id)(xdebug_con *h, function_stack_entry *fse); }; xdebug_brk_info *xdebug_brk_info_ctor(void); void xdebug_brk_info_dtor(xdebug_brk_info *brk); void xdebug_llist_brk_dtor(void *dummy, xdebug_brk_info *brk); void xdebug_hash_brk_dtor(xdebug_brk_info *brk); void xdebug_hash_eval_info_dtor(xdebug_eval_info *ei); #endif xdebug-3.4.3/src/debugger/handler_dbgp.c0000664000175000017500000027615415011062311017422 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2025 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #ifndef PHP_WIN32 #include #endif #include "lib/php-header.h" #include "SAPI.h" #include "ext/standard/php_string.h" #include "ext/standard/url.h" #include "main/php_version.h" #include "main/php_network.h" #include "ext/standard/base64.h" #include "TSRM.h" #include "php_globals.h" #include "php_xdebug.h" #include "com.h" #include "handler_dbgp.h" #include "debugger_private.h" #include "coverage/code_coverage.h" #include "develop/stack.h" #include "lib/compat.h" #include "lib/hash.h" #include "lib/llist.h" #include "lib/log.h" #include "lib/mm.h" #include "lib/var_export_xml.h" #include "lib/vector.h" #include "lib/xml.h" #ifdef PHP_WIN32 #include "win32/time.h" #include #endif #include ZEND_EXTERN_MODULE_GLOBALS(xdebug) xdebug_remote_handler xdebug_handler_dbgp = { xdebug_dbgp_init, xdebug_dbgp_deinit, xdebug_dbgp_error, xdebug_dbgp_break_on_line, xdebug_dbgp_breakpoint, xdebug_dbgp_resolve_breakpoints, xdebug_dbgp_stream_output, xdebug_dbgp_notification, xdebug_dbgp_user_notify, xdebug_dbgp_register_eval_id, }; static char *create_eval_key_id(int id); static void line_breakpoint_resolve_helper(xdebug_con *context, xdebug_lines_list *lines_list, xdebug_brk_info *brk_info); /***************************************************************************** ** Constants and strings for statii and reasons */ /* Status structure */ #define DBGP_STATUS_STARTING 1 #define DBGP_STATUS_STOPPING 2 #define DBGP_STATUS_STOPPED 3 #define DBGP_STATUS_RUNNING 4 #define DBGP_STATUS_BREAK 5 #define DBGP_STATUS_DETACHED 6 const char *xdebug_dbgp_status_strings[7] = {"", "starting", "stopping", "stopped", "running", "break", "detached"}; #define DBGP_REASON_OK 0 #define DBGP_REASON_ERROR 1 #define DBGP_REASON_ABORTED 2 #define DBGP_REASON_EXCEPTION 3 const char *xdebug_dbgp_reason_strings[4] = {"ok", "error", "aborted", "exception"}; #define XDEBUG_ERROR_OK 0 #define XDEBUG_ERROR_PARSE 1 #define XDEBUG_ERROR_DUP_ARG 2 #define XDEBUG_ERROR_INVALID_ARGS 3 #define XDEBUG_ERROR_UNIMPLEMENTED 4 #define XDEBUG_ERROR_COMMAND_UNAVAILABLE 5 #define XDEBUG_ERROR_CANT_OPEN_FILE 100 #define XDEBUG_ERROR_STREAM_REDIRECT_FAILED 101 /* unused */ #define XDEBUG_ERROR_BREAKPOINT_NOT_SET 200 #define XDEBUG_ERROR_BREAKPOINT_TYPE_NOT_SUPPORTED 201 #define XDEBUG_ERROR_BREAKPOINT_INVALID 202 #define XDEBUG_ERROR_BREAKPOINT_NO_CODE 203 #define XDEBUG_ERROR_BREAKPOINT_INVALID_STATE 204 #define XDEBUG_ERROR_NO_SUCH_BREAKPOINT 205 #define XDEBUG_ERROR_EVALUATING_CODE 206 #define XDEBUG_ERROR_INVALID_EXPRESSION 207 /* unused */ #define XDEBUG_ERROR_PROPERTY_NON_EXISTENT 300 #define XDEBUG_ERROR_PROPERTY_NON_EXISTANT 300 /* compatibility typo */ #define XDEBUG_ERROR_STACK_DEPTH_INVALID 301 #define XDEBUG_ERROR_CONTEXT_INVALID 302 /* unused */ #define XDEBUG_ERROR_PROFILING_NOT_STARTED 800 #define XDEBUG_ERROR_ENCODING_NOT_SUPPORTED 900 typedef struct { int code; const char *message; } xdebug_error_entry; static xdebug_error_entry xdebug_error_codes[24] = { { 0, "no error" }, { 1, "parse error in command" }, { 2, "duplicate arguments in command" }, { 3, "invalid or missing options" }, { 4, "unimplemented command" }, { 5, "command is not available" }, { 100, "can not open file" }, { 101, "stream redirect failed" }, { 200, "breakpoint could not be set" }, { 201, "breakpoint type is not supported" }, { 202, "invalid breakpoint line" }, { 203, "no code on breakpoint line" }, { 204, "invalid breakpoint state" }, { 205, "no such breakpoint" }, { 206, "error evaluating code" }, { 207, "invalid expression" }, { 300, "can not get property" }, { 301, "stack depth invalid" }, { 302, "context invalid" }, { 800, "profiler not started" }, { 900, "encoding not supported" }, { 998, "an internal exception in the debugger" }, { 999, "unknown error" }, { -1, NULL } }; static const char *error_message_from_code(int code) { xdebug_error_entry *error_entry = &xdebug_error_codes[0]; while (error_entry->message) { if (code == error_entry->code) { return error_entry->message; } error_entry++; } return NULL; } #define XDEBUG_STR_SWITCH_DECL char *__switch_variable #define XDEBUG_STR_SWITCH(s) __switch_variable = (s); #define XDEBUG_STR_CASE(s) if (strcmp(__switch_variable, s) == 0) { #define XDEBUG_STR_CASE_END } else #define XDEBUG_STR_CASE_DEFAULT { #define XDEBUG_STR_CASE_DEFAULT_END } #define XDEBUG_TYPES_COUNT 8 const char *xdebug_dbgp_typemap[XDEBUG_TYPES_COUNT][3] = { /* common, lang, schema */ {"bool", "bool", "xsd:boolean"}, {"int", "int", "xsd:decimal"}, {"float", "float", "xsd:double"}, {"string", "string", "xsd:string"}, {"null", "null", NULL}, {"hash", "array", NULL}, {"object", "object", NULL}, {"resource", "resource", NULL} }; typedef struct { int value; const char *name; } xdebug_breakpoint_entry; #define XDEBUG_BREAKPOINT_TYPES_COUNT 6 xdebug_breakpoint_entry xdebug_breakpoint_types[XDEBUG_BREAKPOINT_TYPES_COUNT] = { { XDEBUG_BREAKPOINT_TYPE_LINE, "line" }, { XDEBUG_BREAKPOINT_TYPE_CONDITIONAL, "conditional" }, { XDEBUG_BREAKPOINT_TYPE_CALL, "call" }, { XDEBUG_BREAKPOINT_TYPE_RETURN, "return" }, { XDEBUG_BREAKPOINT_TYPE_EXCEPTION, "exception" }, { XDEBUG_BREAKPOINT_TYPE_WATCH, "watch" } }; #define XDEBUG_DBGP_SCAN_RANGE 5 /***************************************************************************** ** Prototypes for debug command handlers */ /* DBGP_FUNC(break); */ DBGP_FUNC(breakpoint_get); DBGP_FUNC(breakpoint_list); DBGP_FUNC(breakpoint_remove); DBGP_FUNC(breakpoint_set); DBGP_FUNC(breakpoint_update); DBGP_FUNC(context_get); DBGP_FUNC(context_names); DBGP_FUNC(eval); DBGP_FUNC(feature_get); DBGP_FUNC(feature_set); DBGP_FUNC(typemap_get); DBGP_FUNC(property_get); DBGP_FUNC(property_set); DBGP_FUNC(property_value); DBGP_FUNC(source); DBGP_FUNC(stack_depth); DBGP_FUNC(stack_get); DBGP_FUNC(status); DBGP_FUNC(stderr); DBGP_FUNC(stdout); DBGP_FUNC(stop); DBGP_FUNC(run); DBGP_FUNC(step_into); DBGP_FUNC(step_out); DBGP_FUNC(step_over); DBGP_FUNC(detach); /* Non standard comments */ DBGP_FUNC(xcmd_profiler_name_get); DBGP_FUNC(xcmd_get_executable_lines); /***************************************************************************** ** Dispatcher tables for supported debug commands */ static xdebug_dbgp_cmd dbgp_commands[] = { /* DBGP_FUNC_ENTRY(break) */ DBGP_FUNC_ENTRY(breakpoint_get, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(breakpoint_list, XDEBUG_DBGP_POST_MORTEM) DBGP_FUNC_ENTRY(breakpoint_remove, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(breakpoint_set, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(breakpoint_update, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(context_get, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(context_names, XDEBUG_DBGP_POST_MORTEM) DBGP_FUNC_ENTRY(eval, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(feature_get, XDEBUG_DBGP_POST_MORTEM) DBGP_FUNC_ENTRY(feature_set, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(typemap_get, XDEBUG_DBGP_POST_MORTEM) DBGP_FUNC_ENTRY(property_get, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(property_set, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(property_value, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(source, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(stack_depth, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(stack_get, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(status, XDEBUG_DBGP_POST_MORTEM) DBGP_FUNC_ENTRY(stderr, XDEBUG_DBGP_NONE) DBGP_FUNC_ENTRY(stdout, XDEBUG_DBGP_NONE) DBGP_CONT_FUNC_ENTRY(run, XDEBUG_DBGP_NONE) DBGP_CONT_FUNC_ENTRY(step_into, XDEBUG_DBGP_NONE) DBGP_CONT_FUNC_ENTRY(step_out, XDEBUG_DBGP_NONE) DBGP_CONT_FUNC_ENTRY(step_over, XDEBUG_DBGP_NONE) DBGP_STOP_FUNC_ENTRY(stop, XDEBUG_DBGP_POST_MORTEM) DBGP_STOP_FUNC_ENTRY(detach, XDEBUG_DBGP_POST_MORTEM) /* Non standard functions */ DBGP_FUNC_ENTRY(xcmd_profiler_name_get, XDEBUG_DBGP_POST_MORTEM) DBGP_FUNC_ENTRY(xcmd_get_executable_lines, XDEBUG_DBGP_NONE) { NULL, NULL, 0 } }; /***************************************************************************** ** Utility functions */ static xdebug_dbgp_cmd* lookup_cmd(char *cmd) { xdebug_dbgp_cmd *ptr = dbgp_commands; while (ptr->name) { if (strcmp(ptr->name, cmd) == 0) { return ptr; } ptr++; } return NULL; } static xdebug_str *make_message(xdebug_con *context, xdebug_xml_node *message) { xdebug_str xml_message = XDEBUG_STR_INITIALIZER; xdebug_str *ret = xdebug_str_new(); xdebug_xml_return_node(message, &xml_message); xdebug_log(XLOG_CHAN_DEBUG, XLOG_COM, "-> %s\n", xml_message.d); xdebug_str_add_fmt(ret, "%d", xml_message.l + sizeof("\n") - 1); xdebug_str_addc(ret, '\0'); xdebug_str_add_literal(ret, "\n"); xdebug_str_add(ret, xml_message.d, 0); xdebug_str_addc(ret, '\0'); xdebug_str_destroy(&xml_message); return ret; } static void send_message_ex(xdebug_con *context, xdebug_xml_node *message, int stage) { xdebug_str *tmp; size_t bytes_written; /* Sometimes we end up in 'send_message' although the debugging connection * is already closed. In that case, we early return. */ if (XG_DBG(status) != DBGP_STATUS_STARTING && !xdebug_is_debug_connection_active()) { return; } tmp = make_message(context, message); bytes_written = SSENDL(context->socket, tmp->d, tmp->l); /* Error */ if (bytes_written == -1) { int current_errno = php_socket_errno(); char *sock_error = php_socket_strerror(current_errno, NULL, 0); if (current_errno == EPIPE) { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "REMCLOSE", "The debugging client closed the connection on socket %d: %s (error: %d).", context->socket, sock_error, current_errno); xdebug_abort_debugger(); } else { xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SENDERR", "There was a problem sending %zd bytes on socket %d: %s (error: %d).", tmp->l, context->socket, sock_error, current_errno); } efree(sock_error); xdebug_str_free(tmp); return; } /* Not enough written, we'll allow that for now */ if (bytes_written != tmp->l) { char *sock_error = php_socket_strerror(php_socket_errno(), NULL, 0); xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "SENDERR", "There was a problem sending %zd bytes on socket %d: only %zd bytes were written: %s.", tmp->l, context->socket, bytes_written, sock_error); efree(sock_error); } xdebug_str_free(tmp); } static void send_message(xdebug_con *context, xdebug_xml_node *message) { send_message_ex(context, message, 0); } static xdebug_xml_node* get_symbol(xdebug_str *name, xdebug_var_export_options *options) { zval retval; xdebug_xml_node *tmp_node; xdebug_get_php_symbol(&retval, name); if (Z_TYPE(retval) == IS_UNDEF) { return NULL; } if (strcmp(name->d, "this") == 0 && Z_TYPE(retval) == IS_NULL) { return NULL; } tmp_node = xdebug_get_zval_value_xml_node(name, &retval, options); zval_ptr_dtor_nogc(&retval); return tmp_node; } static int get_symbol_contents(xdebug_str *name, xdebug_xml_node *node, xdebug_var_export_options *options) { zval retval; zval *retval_ptr; xdebug_get_php_symbol(&retval, name); if (Z_TYPE(retval) == IS_UNDEF) { return 0; } // TODO WTF??? retval_ptr = &retval; xdebug_var_export_xml_node(&retval_ptr, name, node, options, 1); zval_ptr_dtor_nogc(&retval); return 1; } static xdebug_str* return_file_source(zend_string *filename, int begin, int end) { php_stream *stream; int i = begin; char *line = NULL; xdebug_str *source = xdebug_str_new(); char *tmp_filename = NULL; if (i < 0) { begin = 0; i = 0; } xdebug_str_add_literal(source, ""); tmp_filename = xdebug_path_from_url(filename); stream = php_stream_open_wrapper(tmp_filename, "rb", USE_PATH | REPORT_ERRORS, NULL); xdfree(tmp_filename); /* Read until the "begin" line has been read */ if (!stream) { return NULL; } /* skip to the first requested line */ while (i > 0 && !php_stream_eof(stream)) { if (line) { efree(line); line = NULL; } line = php_stream_gets(stream, NULL, 1024); i--; } /* Read until the "end" line has been read */ do { if (line) { xdebug_str_add(source, line, 0); efree(line); line = NULL; if (php_stream_eof(stream)) break; } line = php_stream_gets(stream, NULL, 1024); i++; } while (i < end + 1 - begin); /* Print last line */ if (line) { efree(line); line = NULL; } php_stream_close(stream); return source; } static xdebug_str* return_eval_source(char *id, int begin, int end) { char *key; xdebug_str *joined; xdebug_eval_info *ei; xdebug_arg *parts; if (begin < 0) { begin = 0; } key = create_eval_key_id(atoi(id)); if (!xdebug_hash_find(XG_DBG(context).eval_id_lookup, key, strlen(key), (void *) &ei)) { return NULL; } parts = xdebug_arg_ctor(); xdebug_explode("\n", ZSTR_VAL(ei->contents), parts, end + 2); joined = xdebug_join("\n", parts, begin, end); xdebug_arg_dtor(parts); return joined; } static int is_dbgp_url(zend_string *filename) { return (strncmp(ZSTR_VAL(filename), "dbgp://", 7) == 0); } static xdebug_str* return_source(zend_string *filename, int begin, int end) { if (is_dbgp_url(filename)) { return return_eval_source(ZSTR_VAL(filename) + 7, begin, end); } else { return return_file_source(filename, begin, end); } } static xdebug_xml_node* return_stackframe(int nr) { function_stack_entry *fse, *fse_prev; char *tmp_fname; zend_string *tmp_filename; xdebug_xml_node *tmp; fse = xdebug_get_stack_frame(nr); fse_prev = xdebug_get_stack_frame(nr - 1); tmp_fname = xdebug_show_fname(fse->function, 0); tmp = xdebug_xml_node_init("stack"); xdebug_xml_add_attribute_ex(tmp, "where", xdstrdup(tmp_fname), 0, 1); xdebug_xml_add_attribute_ex(tmp, "level", xdebug_sprintf("%ld", nr), 0, 1); if (fse_prev) { if (xdebug_debugger_check_evaled_code(fse_prev->filename, &tmp_filename)) { xdebug_xml_add_attribute_ex(tmp, "type", xdstrdup("eval"), 0, 1); xdebug_xml_add_attribute_ex(tmp, "filename", ZSTR_VAL(tmp_filename), 0, 0); zend_string_release(tmp_filename); } else { xdebug_xml_add_attribute_ex(tmp, "type", xdstrdup("file"), 0, 1); xdebug_xml_add_attribute_ex(tmp, "filename", xdebug_path_to_url(fse_prev->filename), 0, 1); } xdebug_xml_add_attribute_ex(tmp, "lineno", xdebug_sprintf("%lu", fse_prev->lineno), 0, 1); } else { zend_string *executed_filename = zend_get_executed_filename_ex(); int executed_lineno = zend_get_executed_lineno(); zend_string *tmp_filename; if (xdebug_debugger_check_evaled_code(executed_filename, &tmp_filename)) { xdebug_xml_add_attribute_ex(tmp, "type", xdstrdup("eval"), 0, 1); xdebug_xml_add_attribute_ex(tmp, "filename", ZSTR_VAL(tmp_filename), 0, 0); zend_string_release(tmp_filename); } else if (executed_filename) { xdebug_xml_add_attribute_ex(tmp, "type", xdstrdup("file"), 0, 1); xdebug_xml_add_attribute_ex(tmp, "filename", xdebug_path_to_url(executed_filename), 0, 1); } xdebug_xml_add_attribute_ex(tmp, "lineno", xdebug_sprintf("%lu", executed_lineno), 0, 1); } xdfree(tmp_fname); return tmp; } /***************************************************************************** ** Client command handlers - Breakpoints */ /* Helper functions */ static void xdebug_hash_admin_dtor(xdebug_brk_admin *admin) { xdfree(admin->key); xdfree(admin); } static int breakpoint_admin_add(xdebug_con *context, int type, char *key) { xdebug_brk_admin *admin = xdmalloc(sizeof(xdebug_brk_admin)); char *hkey; XG_DBG(breakpoint_count)++; admin->id = ((xdebug_get_pid() & 0x1ffff) * 10000) + XG_DBG(breakpoint_count); admin->type = type; admin->key = xdstrdup(key); hkey = xdebug_sprintf("%lu", admin->id); xdebug_hash_add(context->breakpoint_list, hkey, strlen(hkey), (void*) admin); xdfree(hkey); return admin->id; } static int breakpoint_admin_fetch(xdebug_con *context, char *hkey, int *type, char **key) { xdebug_brk_admin *admin; if (!xdebug_hash_find(context->breakpoint_list, hkey, strlen(hkey), (void *) &admin)) { return FAILURE; } *type = admin->type; *key = admin->key; return SUCCESS; } static int breakpoint_admin_remove(xdebug_con *context, char *hkey) { if (!xdebug_hash_delete(context->breakpoint_list, hkey, strlen(hkey))) { return FAILURE; } return SUCCESS; } static void breakpoint_brk_info_add_resolved(xdebug_xml_node *xml, xdebug_brk_info *brk_info) { if (!XG_DBG(context).resolved_breakpoints) { return; } if (brk_info->resolved == XDEBUG_BRK_RESOLVED) { xdebug_xml_add_attribute(xml, "resolved", "resolved"); } else { xdebug_xml_add_attribute(xml, "resolved", "unresolved"); } } static void breakpoint_brk_info_add(xdebug_xml_node *xml, xdebug_brk_info *brk_info) { xdebug_xml_add_attribute_ex(xml, "type", xdstrdup(XDEBUG_BREAKPOINT_TYPE_NAME(brk_info->brk_type)), 0, 1); breakpoint_brk_info_add_resolved(xml, brk_info); if (brk_info->filename) { if (is_dbgp_url(brk_info->filename)) { xdebug_xml_add_attribute_ex(xml, "filename", ZSTR_VAL(brk_info->filename), 0, 0); } else { xdebug_xml_add_attribute_ex(xml, "filename", xdebug_path_to_url(brk_info->filename), 0, 1); } } if (brk_info->resolved_lineno) { xdebug_xml_add_attribute_ex(xml, "lineno", xdebug_sprintf("%lu", brk_info->resolved_lineno), 0, 1); } if (brk_info->functionname) { xdebug_xml_add_attribute_ex(xml, "function", xdstrdup(brk_info->functionname), 0, 1); } if (brk_info->classname) { xdebug_xml_add_attribute_ex(xml, "class", xdstrdup(brk_info->classname), 0, 1); } if (brk_info->exceptionname) { xdebug_xml_add_attribute_ex(xml, "exception", xdstrdup(brk_info->exceptionname), 0, 1); } if (brk_info->disabled) { xdebug_xml_add_attribute(xml, "state", "disabled"); } else if (brk_info->temporary) { xdebug_xml_add_attribute(xml, "state", "temporary"); } else { xdebug_xml_add_attribute(xml, "state", "enabled"); } xdebug_xml_add_attribute_ex(xml, "hit_count", xdebug_sprintf("%lu", brk_info->hit_count), 0, 1); switch (brk_info->hit_condition) { case XDEBUG_HIT_GREATER_EQUAL: xdebug_xml_add_attribute(xml, "hit_condition", ">="); break; case XDEBUG_HIT_EQUAL: xdebug_xml_add_attribute(xml, "hit_condition", "=="); break; case XDEBUG_HIT_MOD: xdebug_xml_add_attribute(xml, "hit_condition", "%"); break; } if (brk_info->condition) { xdebug_xml_node *condition = xdebug_xml_node_init("expression"); xdebug_xml_add_text_ex(condition, brk_info->condition, strlen(brk_info->condition), 0, 1); xdebug_xml_add_child(xml, condition); } xdebug_xml_add_attribute_ex(xml, "hit_value", xdebug_sprintf("%lu", brk_info->hit_value), 0, 1); xdebug_xml_add_attribute_ex(xml, "id", xdebug_sprintf("%lu", brk_info->id), 0, 1); } static xdebug_brk_info* breakpoint_brk_info_fetch(int type, char *hkey) { xdebug_llist_element *le; xdebug_brk_info *brk_info = NULL; switch (type) { case XDEBUG_BREAKPOINT_TYPE_LINE: case XDEBUG_BREAKPOINT_TYPE_CONDITIONAL: { xdebug_arg *parts; /* First we split the key into filename and linenumber */ parts = xdebug_arg_ctor(); xdebug_explode("$", hkey, parts, -1); /* Second we loop through the list of file/line breakpoints to * look for our thingy */ for (le = XDEBUG_LLIST_HEAD(XG_DBG(context).line_breakpoints); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { brk_info = XDEBUG_LLIST_VALP(le); if (atoi(parts->args[1]) == brk_info->original_lineno && memcmp(ZSTR_VAL(brk_info->filename), parts->args[0], ZSTR_LEN(brk_info->filename)) == 0) { xdebug_arg_dtor(parts); return brk_info; } } /* Cleaning up */ xdebug_arg_dtor(parts); break; } case XDEBUG_BREAKPOINT_TYPE_CALL: case XDEBUG_BREAKPOINT_TYPE_RETURN: if (xdebug_hash_find(XG_DBG(context).function_breakpoints, hkey, strlen(hkey), (void *) &brk_info)) { return brk_info; } break; case XDEBUG_BREAKPOINT_TYPE_EXCEPTION: if (xdebug_hash_find(XG_DBG(context).exception_breakpoints, hkey, strlen(hkey), (void *) &brk_info)) { return brk_info; } break; } return brk_info; } static int breakpoint_remove(int type, char *hkey) { xdebug_llist_element *le; xdebug_brk_info *brk_info = NULL; int retval = FAILURE; switch (type) { case XDEBUG_BREAKPOINT_TYPE_LINE: case XDEBUG_BREAKPOINT_TYPE_CONDITIONAL: { xdebug_arg *parts; /* First we split the key into filename and linenumber */ parts = xdebug_arg_ctor(); xdebug_explode("$", hkey, parts, -1); /* Second we loop through the list of file/line breakpoints to * look for our thingy */ for (le = XDEBUG_LLIST_HEAD(XG_DBG(context).line_breakpoints); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { brk_info = XDEBUG_LLIST_VALP(le); if (atoi(parts->args[1]) == brk_info->original_lineno && memcmp(ZSTR_VAL(brk_info->filename), parts->args[0], ZSTR_LEN(brk_info->filename)) == 0) { xdebug_llist_remove(XG_DBG(context).line_breakpoints, le, NULL); retval = SUCCESS; break; } } /* Cleaning up */ xdebug_arg_dtor(parts); break; } case XDEBUG_BREAKPOINT_TYPE_CALL: case XDEBUG_BREAKPOINT_TYPE_RETURN: if (xdebug_hash_delete(XG_DBG(context).function_breakpoints, hkey, strlen(hkey))) { retval = SUCCESS; } break; case XDEBUG_BREAKPOINT_TYPE_EXCEPTION: if (xdebug_hash_delete(XG_DBG(context).exception_breakpoints, hkey, strlen(hkey))) { retval = SUCCESS; } break; } return retval; } #define BREAKPOINT_ACTION_GET 1 #define BREAKPOINT_ACTION_REMOVE 2 #define BREAKPOINT_ACTION_UPDATE 3 #define BREAKPOINT_CHANGE_STATE() \ XDEBUG_STR_SWITCH(CMD_OPTION_CHAR('s')) { \ XDEBUG_STR_CASE("enabled") \ brk_info->disabled = 0; \ XDEBUG_STR_CASE_END \ \ XDEBUG_STR_CASE("disabled") \ brk_info->disabled = 1; \ XDEBUG_STR_CASE_END \ \ XDEBUG_STR_CASE_DEFAULT \ RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); \ XDEBUG_STR_CASE_DEFAULT_END \ } #define BREAKPOINT_CHANGE_OPERATOR() \ XDEBUG_STR_SWITCH(CMD_OPTION_CHAR('o')) { \ XDEBUG_STR_CASE(">=") \ brk_info->hit_condition = XDEBUG_HIT_GREATER_EQUAL; \ XDEBUG_STR_CASE_END \ \ XDEBUG_STR_CASE("==") \ brk_info->hit_condition = XDEBUG_HIT_EQUAL; \ XDEBUG_STR_CASE_END \ \ XDEBUG_STR_CASE("%") \ brk_info->hit_condition = XDEBUG_HIT_MOD; \ XDEBUG_STR_CASE_END \ \ XDEBUG_STR_CASE_DEFAULT \ RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); \ XDEBUG_STR_CASE_DEFAULT_END \ } static void breakpoint_do_action(DBGP_FUNC_PARAMETERS, int action) { int type; char *hkey; xdebug_brk_info *brk_info; xdebug_xml_node *breakpoint_node; XDEBUG_STR_SWITCH_DECL; if (!CMD_OPTION_SET('d')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } /* Lets check if it exists */ if (breakpoint_admin_fetch(context, CMD_OPTION_CHAR('d'), &type, (char**) &hkey) == FAILURE) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_NO_SUCH_BREAKPOINT) } /* so it exists, now we're going to find it in the correct hash/list * and return the info we have on it */ brk_info = breakpoint_brk_info_fetch(type, hkey); if (action == BREAKPOINT_ACTION_UPDATE) { if (CMD_OPTION_SET('s')) { BREAKPOINT_CHANGE_STATE(); } if (CMD_OPTION_SET('n')) { brk_info->original_lineno = strtol(CMD_OPTION_CHAR('n'), NULL, 10); brk_info->resolved_lineno = brk_info->original_lineno; } if (CMD_OPTION_SET('h')) { brk_info->hit_value = strtol(CMD_OPTION_CHAR('h'), NULL, 10); } if (CMD_OPTION_SET('o')) { BREAKPOINT_CHANGE_OPERATOR(); } } breakpoint_node = xdebug_xml_node_init("breakpoint"); breakpoint_brk_info_add(breakpoint_node, brk_info); xdebug_xml_add_child(*retval, breakpoint_node); if (action == BREAKPOINT_ACTION_REMOVE) { /* Now we remove the crap */ breakpoint_remove(type, hkey); breakpoint_admin_remove(context, CMD_OPTION_CHAR('d')); } } DBGP_FUNC(breakpoint_get) { breakpoint_do_action(DBGP_FUNC_PASS_PARAMETERS, BREAKPOINT_ACTION_GET); } DBGP_FUNC(breakpoint_remove) { breakpoint_do_action(DBGP_FUNC_PASS_PARAMETERS, BREAKPOINT_ACTION_REMOVE); } DBGP_FUNC(breakpoint_update) { breakpoint_do_action(DBGP_FUNC_PASS_PARAMETERS, BREAKPOINT_ACTION_UPDATE); } static void breakpoint_exists_helper(void *retval, xdebug_hash_element *he, void *key_to_match) { int *line_found = (int *) retval; xdebug_brk_admin *admin = (xdebug_brk_admin*) he->ptr; if (strcmp(admin->key, (const char*) key_to_match) == 0) { *line_found = 1; } } static int line_breakpoint_exists(xdebug_con *context, const char *key) { int line_found = 0; xdebug_hash_apply_with_argument(context->breakpoint_list, (void *) &line_found, breakpoint_exists_helper, (void*) key); return line_found; } static void breakpoint_list_helper(void *xml, xdebug_hash_element *he, void *dummy) { xdebug_xml_node *xml_node = (xdebug_xml_node*) xml; xdebug_xml_node *child; xdebug_brk_admin *admin = (xdebug_brk_admin*) he->ptr; xdebug_brk_info *brk_info; child = xdebug_xml_node_init("breakpoint"); brk_info = breakpoint_brk_info_fetch(admin->type, admin->key); breakpoint_brk_info_add(child, brk_info); xdebug_xml_add_child(xml_node, child); } DBGP_FUNC(breakpoint_list) { xdebug_hash_apply_with_argument(context->breakpoint_list, (void *) *retval, breakpoint_list_helper, NULL); } static void warn_if_breakpoint_file_does_not_exist(xdebug_brk_info *brk_info) { #ifndef WIN32 if (brk_info && brk_info->filename && strstr(ZSTR_VAL(brk_info->filename), "://") == NULL) { struct stat buf; if (stat(ZSTR_VAL(brk_info->filename), &buf) != 0) { xdebug_log_ex( XLOG_CHAN_DEBUG, XLOG_WARN, "BRKFILE", "Breakpoint file name does not exist: %s (%s).", ZSTR_VAL(brk_info->filename), strerror(errno) ); } } #endif } DBGP_FUNC(breakpoint_set) { xdebug_brk_info *brk_info; XDEBUG_STR_SWITCH_DECL; brk_info = xdebug_brk_info_ctor(); if (!CMD_OPTION_SET('t')) { xdebug_brk_info_dtor(brk_info); RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } else { int i; int found = 0; for (i = 0; i < XDEBUG_BREAKPOINT_TYPES_COUNT; i++) { if (strcmp(xdebug_breakpoint_types[i].name, CMD_OPTION_CHAR('t')) == 0) { brk_info->brk_type = xdebug_breakpoint_types[i].value; found = 1; break; } } if (!found) { xdebug_brk_info_dtor(brk_info); RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } } if (CMD_OPTION_SET('s')) { BREAKPOINT_CHANGE_STATE(); xdebug_xml_add_attribute_ex(*retval, "state", xdstrdup(CMD_OPTION_CHAR('s')), 0, 1); } if (CMD_OPTION_SET('o') && CMD_OPTION_SET('h')) { BREAKPOINT_CHANGE_OPERATOR(); brk_info->hit_value = strtol(CMD_OPTION_CHAR('h'), NULL, 10); } if (CMD_OPTION_SET('r')) { brk_info->temporary = strtol(CMD_OPTION_CHAR('r'), NULL, 10); } if ((strcmp(CMD_OPTION_CHAR('t'), "line") == 0) || (strcmp(CMD_OPTION_CHAR('t'), "conditional") == 0)) { size_t new_length = 0; char *tmp_name; function_stack_entry *fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); if (!CMD_OPTION_SET('n')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } brk_info->original_lineno = strtol(CMD_OPTION_CHAR('n'), NULL, 10); brk_info->resolved_lineno = brk_info->original_lineno; /* If no filename is given, we use the current one */ if (!CMD_OPTION_SET('f')) { if (!fse) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); } else { char *tmp_path = xdebug_path_from_url(fse->filename); brk_info->filename = zend_string_init(tmp_path, strlen(tmp_path), 0); } } else { char realpath_file[MAXPATHLEN]; zend_string *tmp_f = zend_string_init(CMD_OPTION_CHAR('f'), CMD_OPTION_LEN('f'), 0); char *tmp_path = xdebug_path_from_url(tmp_f); brk_info->filename = zend_string_init(tmp_path, strlen(tmp_path), 0); /* Now we do some real path checks to resolve symlinks. */ if (VCWD_REALPATH(ZSTR_VAL(brk_info->filename), realpath_file)) { zend_string_release(brk_info->filename); brk_info->filename = zend_string_init(realpath_file, strlen(realpath_file), 0); } zend_string_release(tmp_f); xdfree(tmp_path); } warn_if_breakpoint_file_does_not_exist(brk_info); /* Perhaps we have a break condition */ if (CMD_OPTION_SET('-')) { brk_info->condition = (char*) xdebug_base64_decode((unsigned char*) CMD_OPTION_CHAR('-'), CMD_OPTION_LEN('-'), &new_length); } tmp_name = xdebug_sprintf("%s$%lu", ZSTR_VAL(brk_info->filename), brk_info->original_lineno); if (line_breakpoint_exists(context, tmp_name)) { xdfree(tmp_name); xdebug_brk_info_dtor(brk_info); RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_BREAKPOINT_NOT_SET); } if (strcmp(CMD_OPTION_CHAR('t'), "line") == 0) { brk_info->id = breakpoint_admin_add(context, XDEBUG_BREAKPOINT_TYPE_LINE, tmp_name); } else { brk_info->id = breakpoint_admin_add(context, XDEBUG_BREAKPOINT_TYPE_CONDITIONAL, tmp_name); } xdfree(tmp_name); xdebug_llist_insert_next(context->line_breakpoints, XDEBUG_LLIST_TAIL(context->line_breakpoints), (void*) brk_info); if (XG_DBG(context).resolved_breakpoints) { xdebug_lines_list *lines_list; if (xdebug_hash_find(XG_DBG(breakable_lines_map), ZSTR_VAL(brk_info->filename), ZSTR_LEN(brk_info->filename), (void *) &lines_list)) { line_breakpoint_resolve_helper(context, lines_list, brk_info); } } if (fse) { function_stack_entry *loop_fse = fse; int i; size_t stack_size = XDEBUG_VECTOR_COUNT(XG_BASE(stack)); for (i = 0; i < stack_size; i++, loop_fse--) { xdebug_debugger_set_has_line_breakpoints(loop_fse); } } } else if ((strcmp(CMD_OPTION_CHAR('t'), "call") == 0) || (strcmp(CMD_OPTION_CHAR('t'), "return") == 0)) { void *dummy = NULL; char *tmp_name; if (strcmp(CMD_OPTION_CHAR('t'), "call") == 0) { brk_info->function_break_type = XDEBUG_BREAKPOINT_TYPE_CALL; } else { brk_info->function_break_type = XDEBUG_BREAKPOINT_TYPE_RETURN; } if (!CMD_OPTION_SET('m')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } brk_info->functionname = xdstrdup(CMD_OPTION_CHAR('m')); if (CMD_OPTION_SET('a')) { brk_info->classname = xdstrdup(CMD_OPTION_CHAR('a')); tmp_name = xdebug_sprintf( "%c/%s::%s", (brk_info->function_break_type & XDEBUG_BREAKPOINT_TYPE_CALL) ? 'C' : 'R', CMD_OPTION_CHAR('a'), CMD_OPTION_CHAR('m') ); } else { tmp_name = xdebug_sprintf( "%c/%s", (brk_info->function_break_type & XDEBUG_BREAKPOINT_TYPE_CALL) ? 'C' : 'R', CMD_OPTION_CHAR('m') ); } if (xdebug_hash_find(context->function_breakpoints, tmp_name, strlen(tmp_name), (void*) &dummy)) { xdfree(tmp_name); RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_BREAKPOINT_NOT_SET); } if (!xdebug_hash_add(context->function_breakpoints, tmp_name, strlen(tmp_name), brk_info)) { xdfree(tmp_name); RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_BREAKPOINT_NOT_SET); } else { if (brk_info->function_break_type & XDEBUG_BREAKPOINT_TYPE_CALL) { brk_info->id = breakpoint_admin_add(context, XDEBUG_BREAKPOINT_TYPE_CALL, tmp_name); } else { brk_info->id = breakpoint_admin_add(context, XDEBUG_BREAKPOINT_TYPE_RETURN, tmp_name); } } brk_info->resolved = XDEBUG_BRK_RESOLVED; xdfree(tmp_name); } else if (strcmp(CMD_OPTION_CHAR('t'), "exception") == 0) { if (!CMD_OPTION_SET('x')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } brk_info->exceptionname = xdstrdup(CMD_OPTION_CHAR('x')); if (!xdebug_hash_add(context->exception_breakpoints, CMD_OPTION_CHAR('x'), CMD_OPTION_LEN('x'), (void*) brk_info)) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_BREAKPOINT_NOT_SET); } else { brk_info->id = breakpoint_admin_add(context, XDEBUG_BREAKPOINT_TYPE_EXCEPTION, CMD_OPTION_CHAR('x')); } brk_info->resolved = XDEBUG_BRK_RESOLVED; } else if (strcmp(CMD_OPTION_CHAR('t'), "watch") == 0) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_BREAKPOINT_TYPE_NOT_SUPPORTED); } xdebug_xml_add_attribute_ex(*retval, "id", xdebug_sprintf("%lu", brk_info->id), 0, 1); breakpoint_brk_info_add_resolved(*retval, brk_info); } DBGP_FUNC(eval) { char *eval_string; xdebug_xml_node *ret_xml; zval ret_zval; size_t new_length = 0; int res; xdebug_var_export_options *options; zend_string *return_message; if (!CMD_OPTION_SET('-')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } options = (xdebug_var_export_options*) context->options; if (CMD_OPTION_SET('p')) { options->runtime[0].page = strtol(CMD_OPTION_CHAR('p'), NULL, 10); } else { options->runtime[0].page = 0; } /* base64 decode eval string */ eval_string = (char*) xdebug_base64_decode((unsigned char*) CMD_OPTION_CHAR('-'), CMD_OPTION_LEN('-'), &new_length); res = xdebug_do_eval(eval_string, &ret_zval, &return_message); xdfree(eval_string); /* Handle result */ if (!res) { if (return_message) { RETURN_RESULT_WITH_MESSAGE(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_EVALUATING_CODE, xdebug_sprintf("%s: %s", error_message_from_code(XDEBUG_ERROR_EVALUATING_CODE), ZSTR_VAL(return_message))); zend_string_release(return_message); } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_EVALUATING_CODE); } } else { ret_xml = xdebug_get_zval_value_xml_node(NULL, &ret_zval, options); xdebug_xml_add_child(*retval, ret_xml); zval_ptr_dtor(&ret_zval); } } /* these functions interupt PHP's output functions, so we can redirect to our remote debugger! */ static void xdebug_send_stream(const char *name, const char *str, unsigned int str_length) { /* create an xml document to send as the stream */ xdebug_xml_node *message; if (!xdebug_is_debug_connection_active()) { return; } message = xdebug_xml_node_init("stream"); xdebug_xml_add_attribute(message, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(message, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); xdebug_xml_add_attribute_ex(message, "type", (char *)name, 0, 0); xdebug_xml_add_text_encodel(message, xdstrndup(str, str_length), str_length); send_message(&XG_DBG(context), message); xdebug_xml_node_dtor(message); return; } DBGP_FUNC(stderr) { xdebug_xml_add_attribute(*retval, "success", "0"); } DBGP_FUNC(stdout) { int mode = 0; const char *success = "0"; if (!CMD_OPTION_SET('c')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } mode = strtol(CMD_OPTION_CHAR('c'), NULL, 10); XG_DBG(stdout_mode) = mode; success = "1"; xdebug_xml_add_attribute_ex(*retval, "success", xdstrdup(success), 0, 1); } DBGP_FUNC(stop) { XG_DBG(status) = DBGP_STATUS_STOPPED; xdebug_xml_add_attribute(*retval, "status", xdebug_dbgp_status_strings[XG_DBG(status)]); xdebug_xml_add_attribute(*retval, "reason", xdebug_dbgp_reason_strings[XG_DBG(reason)]); } DBGP_FUNC(run) { xdebug_xml_add_attribute_ex(*retval, "filename", ZSTR_VAL(context->program_name), 0, 0); } DBGP_FUNC(step_into) { XG_DBG(context).do_next = 0; XG_DBG(context).do_step = 1; XG_DBG(context).do_finish = 0; } DBGP_FUNC(step_out) { function_stack_entry *fse; XG_DBG(context).do_next = 0; XG_DBG(context).do_step = 0; XG_DBG(context).do_finish = 1; if ((fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)))) { XG_DBG(context).finish_level = fse->level; XG_DBG(context).finish_func_nr = fse->function_nr; } else { XG_DBG(context).finish_level = -1; XG_DBG(context).finish_func_nr = -1; } } DBGP_FUNC(step_over) { function_stack_entry *fse; XG_DBG(context).do_next = 1; XG_DBG(context).do_step = 0; XG_DBG(context).do_finish = 0; if ((fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)))) { XG_DBG(context).next_level = fse->level; XG_DBG(context).next_stack = XG_BASE(stack); } else { XG_DBG(context).next_level = 0; XG_DBG(context).next_stack = NULL; } } DBGP_FUNC(detach) { XG_DBG(status) = DBGP_STATUS_DETACHED; xdebug_xml_add_attribute(*retval, "status", xdebug_dbgp_status_strings[DBGP_STATUS_STOPPED]); xdebug_xml_add_attribute(*retval, "reason", xdebug_dbgp_reason_strings[XG_DBG(reason)]); XG_DBG(context).handler->remote_deinit(&(XG_DBG(context))); xdebug_mark_debug_connection_not_active(); XG_DBG(stdout_mode) = 0; XG_DBG(detached) = 1; if (CMD_OPTION_SET('-')) { XG_DBG(context).detached_message = xdstrdup(CMD_OPTION_CHAR('-')); xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "DETACH", "Debug client detached: %s.", XG_DBG(context).detached_message); } } DBGP_FUNC(source) { xdebug_str *source; int begin = 0, end = 999999; zend_string *filename; function_stack_entry *fse; if (!CMD_OPTION_SET('f')) { if ((fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)))) { filename = zend_string_copy(fse->filename); } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); } } else { filename = zend_string_init(CMD_OPTION_CHAR('f'), CMD_OPTION_LEN('f'), 0); } if (CMD_OPTION_SET('b')) { begin = strtol(CMD_OPTION_CHAR('b'), NULL, 10); } if (CMD_OPTION_SET('e')) { end = strtol(CMD_OPTION_CHAR('e'), NULL, 10); } /* return_source allocates memory for source */ XG_DBG(breakpoints_allowed) = 0; source = return_source(filename, begin, end); XG_DBG(breakpoints_allowed) = 1; zend_string_release(filename); if (!source) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_CANT_OPEN_FILE); } else { xdebug_xml_add_text_ex(*retval, xdstrdup(source->d), source->l, 1, 1); xdebug_str_free(source); } } DBGP_FUNC(feature_get) { xdebug_var_export_options *options; XDEBUG_STR_SWITCH_DECL; options = (xdebug_var_export_options*) context->options; if (!CMD_OPTION_SET('n')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } xdebug_xml_add_attribute_ex(*retval, "feature_name", xdstrdup(CMD_OPTION_CHAR('n')), 0, 1); XDEBUG_STR_SWITCH(CMD_OPTION_CHAR('n')) { XDEBUG_STR_CASE("breakpoint_languages") xdebug_xml_add_attribute(*retval, "supported", "0"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("breakpoint_types") xdebug_xml_add_text(*retval, xdstrdup("line conditional call return exception")); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("data_encoding") xdebug_xml_add_attribute(*retval, "supported", "0"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("encoding") xdebug_xml_add_text(*retval, xdstrdup("iso-8859-1")); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("language_name") xdebug_xml_add_text(*retval, xdstrdup("PHP")); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("language_supports_threads") xdebug_xml_add_text(*retval, xdstrdup("0")); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("language_version") xdebug_xml_add_text(*retval, xdstrdup(XG_BASE(php_version_run_time))); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("max_children") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", options->max_children)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("max_data") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", options->max_data)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("max_depth") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", options->max_depth)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("protocol_version") xdebug_xml_add_text(*retval, xdstrdup(DBGP_VERSION)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("supported_encodings") xdebug_xml_add_text(*retval, xdstrdup("iso-8859-1")); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("supports_async") xdebug_xml_add_text(*retval, xdstrdup("0")); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("supports_postmortem") xdebug_xml_add_text(*retval, xdstrdup("1")); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("show_hidden") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", options->show_hidden)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("extended_properties") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", options->extended_properties)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("notify_ok") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", XG_DBG(context).send_notifications)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("resolved_breakpoints") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", XG_DBG(context).resolved_breakpoints)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("breakpoint_details") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", XG_DBG(context).breakpoint_details)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("breakpoint_include_return_value") xdebug_xml_add_text(*retval, xdebug_sprintf("%ld", XG_DBG(context).breakpoint_include_return_value)); xdebug_xml_add_attribute(*retval, "supported", "1"); XDEBUG_STR_CASE_END XDEBUG_STR_CASE_DEFAULT xdebug_xml_add_text(*retval, xdstrdup(lookup_cmd(CMD_OPTION_CHAR('n')) ? "1" : "0")); xdebug_xml_add_attribute(*retval, "supported", lookup_cmd(CMD_OPTION_CHAR('n')) ? "1" : "0"); XDEBUG_STR_CASE_DEFAULT_END } } DBGP_FUNC(feature_set) { xdebug_var_export_options *options; XDEBUG_STR_SWITCH_DECL; options = (xdebug_var_export_options*) context->options; if (!CMD_OPTION_SET('n') || !CMD_OPTION_SET('v')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } XDEBUG_STR_SWITCH(CMD_OPTION_CHAR('n')) { XDEBUG_STR_CASE("encoding") if (strcmp(CMD_OPTION_CHAR('v'), "iso-8859-1") != 0) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_ENCODING_NOT_SUPPORTED); } XDEBUG_STR_CASE_END XDEBUG_STR_CASE("max_children") options->max_children = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("max_data") options->max_data = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("max_depth") int i; options->max_depth = strtol(CMD_OPTION_CHAR('v'), NULL, 10); /* Reallocating page structure */ xdfree(options->runtime); options->runtime = (xdebug_var_runtime_page*) xdmalloc(options->max_depth * sizeof(xdebug_var_runtime_page)); for (i = 0; i < options->max_depth; i++) { options->runtime[i].page = 0; options->runtime[i].current_element_nr = 0; } XDEBUG_STR_CASE_END XDEBUG_STR_CASE("show_hidden") options->show_hidden = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("multiple_sessions") /* FIXME: Add new boolean option check / struct field for this */ XDEBUG_STR_CASE_END XDEBUG_STR_CASE("extended_properties") options->extended_properties = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("notify_ok") XG_DBG(context).send_notifications = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("resolved_breakpoints") XG_DBG(context).resolved_breakpoints = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("breakpoint_details") XG_DBG(context).breakpoint_details = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE("breakpoint_include_return_value") XG_DBG(context).breakpoint_include_return_value = strtol(CMD_OPTION_CHAR('v'), NULL, 10); XDEBUG_STR_CASE_END XDEBUG_STR_CASE_DEFAULT RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); XDEBUG_STR_CASE_DEFAULT_END } xdebug_xml_add_attribute_ex(*retval, "feature", xdstrdup(CMD_OPTION_CHAR('n')), 0, 1); xdebug_xml_add_attribute_ex(*retval, "success", "1", 0, 0); } DBGP_FUNC(typemap_get) { int i; xdebug_xml_node *type; xdebug_xml_add_attribute(*retval, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); xdebug_xml_add_attribute(*retval, "xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); /* Add our basic types */ for (i = 0; i < XDEBUG_TYPES_COUNT; i++) { type = xdebug_xml_node_init("map"); xdebug_xml_add_attribute(type, "name", xdebug_dbgp_typemap[i][1]); xdebug_xml_add_attribute(type, "type", xdebug_dbgp_typemap[i][0]); if (xdebug_dbgp_typemap[i][2]) { xdebug_xml_add_attribute(type, "xsi:type", xdebug_dbgp_typemap[i][2]); } xdebug_xml_add_child(*retval, type); } } static int add_constant_node(xdebug_xml_node *node, xdebug_str *name, zval *const_val, xdebug_var_export_options *options) { xdebug_xml_node *contents; contents = xdebug_get_zval_value_xml_node_ex(name, const_val, XDEBUG_VAR_TYPE_CONSTANT, options); if (!contents) { return FAILURE; } xdebug_xml_expand_attribute_value(contents, "facet", "constant"); xdebug_xml_add_child(node, contents); return SUCCESS; } static int add_variable_node(xdebug_xml_node *node, xdebug_str *name, int var_only, int non_null, int no_eval, xdebug_var_export_options *options) { xdebug_xml_node *contents; contents = get_symbol(name, options); if (!contents) { return FAILURE; } xdebug_xml_add_child(node, contents); return SUCCESS; } static int xdebug_get_constant(xdebug_str *val, zval *const_val) { zval *tmp_const = NULL; tmp_const = zend_get_constant_str(val->d, val->l); if (!tmp_const) { return 0; } *const_val = *tmp_const; return 1; } DBGP_FUNC(property_get) { int depth = 0; int context_nr = 0; function_stack_entry *fse; int old_max_data; xdebug_var_export_options *options = (xdebug_var_export_options*) context->options; if (!CMD_OPTION_SET('n')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } if (CMD_OPTION_SET('d')) { depth = strtol(CMD_OPTION_CHAR('d'), NULL, 10); } if (CMD_OPTION_SET('c')) { context_nr = strtol(CMD_OPTION_CHAR('c'), NULL, 10); } /* Set the symbol table corresponding with the requested stack depth */ if (context_nr == 0) { /* locals */ if ((fse = xdebug_get_stack_frame(depth))) { function_stack_entry *old_fse = xdebug_get_stack_frame(depth - 1); if (depth > 0) { xdebug_lib_set_active_data(old_fse->execute_data); } else { xdebug_lib_set_active_data(EG(current_execute_data)); } xdebug_lib_set_active_stack_entry(fse); xdebug_lib_set_active_symbol_table(fse->symbol_table); } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); } } else if (context_nr == 1) { /* superglobals */ xdebug_lib_set_active_symbol_table(&EG(symbol_table)); } else if (context_nr == 2) { /* constants */ /* Do nothing */ } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } if (CMD_OPTION_SET('p')) { options->runtime[0].page = strtol(CMD_OPTION_CHAR('p'), NULL, 10); } else { options->runtime[0].page = 0; } /* Override max data size if necessary */ old_max_data = options->max_data; if (CMD_OPTION_SET('m')) { options->max_data= strtol(CMD_OPTION_CHAR('m'), NULL, 10); } if (context_nr == 2) { /* constants */ zval const_val; if (!xdebug_get_constant(CMD_OPTION_XDEBUG_STR('n'), &const_val)) { options->max_data = old_max_data; RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_PROPERTY_NON_EXISTENT); } if (add_constant_node(*retval, CMD_OPTION_XDEBUG_STR('n'), &const_val, options) == FAILURE) { options->max_data = old_max_data; RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_PROPERTY_NON_EXISTENT); } } else { int add_var_retval; XG_DBG(context).inhibit_notifications = 1; add_var_retval = add_variable_node(*retval, CMD_OPTION_XDEBUG_STR('n'), 1, 0, 0, options); XG_DBG(context).inhibit_notifications = 0; if (add_var_retval == FAILURE) { options->max_data = old_max_data; RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_PROPERTY_NON_EXISTENT); } } options->max_data = old_max_data; } DBGP_FUNC(property_set) { unsigned char *new_value; size_t new_length = 0; int depth = 0; int context_nr = 0; int res; char *eval_string; const char *cast_as; zval ret_zval; function_stack_entry *fse; xdebug_var_export_options *options = (xdebug_var_export_options*) context->options; zend_execute_data *original_execute_data; XDEBUG_STR_SWITCH_DECL; if (!CMD_OPTION_SET('n')) { /* name */ RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } if (!CMD_OPTION_SET('-')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } if (CMD_OPTION_SET('d')) { /* depth */ depth = strtol(CMD_OPTION_CHAR('d'), NULL, 10); } if (CMD_OPTION_SET('c')) { /* context_id */ context_nr = strtol(CMD_OPTION_CHAR('c'), NULL, 10); } /* Set the symbol table corresponding with the requested stack depth */ if (context_nr == 0) { /* locals */ if ((fse = xdebug_get_stack_frame(depth))) { function_stack_entry *old_fse = xdebug_get_stack_frame(depth - 1); if (depth > 0) { xdebug_lib_set_active_data(old_fse->execute_data); } else { xdebug_lib_set_active_data(EG(current_execute_data)); } xdebug_lib_set_active_stack_entry(fse); xdebug_lib_set_active_symbol_table(fse->symbol_table); } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); } } else { /* superglobals */ xdebug_lib_set_active_symbol_table(&EG(symbol_table)); } if (CMD_OPTION_SET('p')) { options->runtime[0].page = strtol(CMD_OPTION_CHAR('p'), NULL, 10); } else { options->runtime[0].page = 0; } new_value = xdebug_base64_decode((unsigned char*) CMD_OPTION_CHAR('-'), CMD_OPTION_LEN('-'), &new_length); /* Set a cast, if requested through the 't' option */ cast_as = ""; if (CMD_OPTION_SET('t')) { XDEBUG_STR_SWITCH(CMD_OPTION_CHAR('t')) { XDEBUG_STR_CASE("bool") cast_as = "(bool) "; XDEBUG_STR_CASE_END XDEBUG_STR_CASE("int") cast_as = "(int) "; XDEBUG_STR_CASE_END XDEBUG_STR_CASE("float") cast_as = "(float) "; XDEBUG_STR_CASE_END XDEBUG_STR_CASE("string") cast_as = "(string) "; XDEBUG_STR_CASE_END XDEBUG_STR_CASE_DEFAULT xdebug_xml_add_attribute(*retval, "success", "0"); XDEBUG_STR_CASE_DEFAULT_END } } /* backup executor state */ if (depth > 0) { original_execute_data = EG(current_execute_data); EG(current_execute_data) = xdebug_lib_get_active_data(); } /* Do the eval */ eval_string = xdebug_sprintf("%s = %s %s", CMD_OPTION_CHAR('n'), cast_as, new_value); res = xdebug_do_eval(eval_string, &ret_zval, NULL); /* restore executor state */ if (depth > 0) { EG(current_execute_data) = original_execute_data; } /* Free data */ xdfree(eval_string); xdfree(new_value); /* Handle result */ if (!res) { /* don't send an error, send success = zero */ xdebug_xml_add_attribute(*retval, "success", "0"); } else { zval_dtor(&ret_zval); xdebug_xml_add_attribute(*retval, "success", "1"); } } static int add_variable_contents_node(xdebug_xml_node *node, xdebug_str *name, int var_only, int non_null, int no_eval, xdebug_var_export_options *options) { int contents_found; contents_found = get_symbol_contents(name, node, options); if (!contents_found) { return FAILURE; } return SUCCESS; } DBGP_FUNC(property_value) { int depth = 0; int context_nr = 0; function_stack_entry *fse; int old_max_data; xdebug_var_export_options *options = (xdebug_var_export_options*) context->options; if (!CMD_OPTION_SET('n')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } if (CMD_OPTION_SET('d')) { depth = strtol(CMD_OPTION_CHAR('d'), NULL, 10); } if (CMD_OPTION_SET('c')) { context_nr = strtol(CMD_OPTION_CHAR('c'), NULL, 10); } /* Set the symbol table corresponding with the requested stack depth */ if (context_nr == 0) { /* locals */ if ((fse = xdebug_get_stack_frame(depth))) { function_stack_entry *old_fse = xdebug_get_stack_frame(depth - 1); if (depth > 0) { xdebug_lib_set_active_data(old_fse->execute_data); } else { xdebug_lib_set_active_data(EG(current_execute_data)); } xdebug_lib_set_active_stack_entry(fse); xdebug_lib_set_active_symbol_table(fse->symbol_table); } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); } } else { /* superglobals */ xdebug_lib_set_active_symbol_table(&EG(symbol_table)); } if (CMD_OPTION_SET('p')) { options->runtime[0].page = strtol(CMD_OPTION_CHAR('p'), NULL, 10); } else { options->runtime[0].page = 0; } /* Override max data size if necessary */ old_max_data = options->max_data; if (CMD_OPTION_SET('m')) { options->max_data = strtol(CMD_OPTION_CHAR('m'), NULL, 10); } if (options->max_data < 0) { options->max_data = old_max_data; RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } if (add_variable_contents_node(*retval, CMD_OPTION_XDEBUG_STR('n'), 1, 0, 0, options) == FAILURE) { options->max_data = old_max_data; RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_PROPERTY_NON_EXISTENT); } options->max_data = old_max_data; } static void attach_declared_var_with_contents(void *xml, xdebug_hash_element* he, void *options) { xdebug_str *name = (xdebug_str*) he->ptr; xdebug_xml_node *node = (xdebug_xml_node *) xml; xdebug_xml_node *contents; contents = get_symbol(name, options); if (!contents) { xdebug_var_xml_attach_uninitialized_var(options, node, name); return; } xdebug_xml_add_child(node, contents); } static int xdebug_add_filtered_symboltable_var(zval *symbol, int num_args, va_list args, zend_hash_key *hash_key) { xdebug_hash *tmp_hash; tmp_hash = va_arg(args, xdebug_hash *); /* We really ought to deal properly with non-associate keys for symbol * tables, but for now, we'll just ignore them. */ if (!hash_key->key) { return 0; } if (hash_key->key->val[0] == '\0') { return 0; } if (strcmp("argc", hash_key->key->val) == 0) { return 0; } if (strcmp("argv", hash_key->key->val) == 0) { return 0; } if (hash_key->key->val[0] == '_') { if (strcmp("_COOKIE", hash_key->key->val) == 0) { return 0; } if (strcmp("_ENV", hash_key->key->val) == 0) { return 0; } if (strcmp("_FILES", hash_key->key->val) == 0) { return 0; } if (strcmp("_GET", hash_key->key->val) == 0) { return 0; } if (strcmp("_POST", hash_key->key->val) == 0) { return 0; } if (strcmp("_REQUEST", hash_key->key->val) == 0) { return 0; } if (strcmp("_SERVER", hash_key->key->val) == 0) { return 0; } if (strcmp("_SESSION", hash_key->key->val) == 0) { return 0; } } if (hash_key->key->val[0] == 'H') { if (strcmp("HTTP_COOKIE_VARS", hash_key->key->val) == 0) { return 0; } if (strcmp("HTTP_ENV_VARS", hash_key->key->val) == 0) { return 0; } if (strcmp("HTTP_GET_VARS", hash_key->key->val) == 0) { return 0; } if (strcmp("HTTP_POST_VARS", hash_key->key->val) == 0) { return 0; } if (strcmp("HTTP_POST_FILES", hash_key->key->val) == 0) { return 0; } if (strcmp("HTTP_RAW_POST_DATA", hash_key->key->val) == 0) { return 0; } if (strcmp("HTTP_SERVER_VARS", hash_key->key->val) == 0) { return 0; } if (strcmp("HTTP_SESSION_VARS", hash_key->key->val) == 0) { return 0; } } if (strcmp("GLOBALS", hash_key->key->val) == 0) { return 0; } xdebug_hash_add(tmp_hash, (char*) hash_key->key->val, hash_key->key->len, xdebug_str_create(hash_key->key->val, hash_key->key->len)); return 0; } static int attach_context_vars(xdebug_xml_node *node, xdebug_var_export_options *options, long context_id, long depth, void (*func)(void *, xdebug_hash_element*, void*)) { function_stack_entry *fse; char *var_name; /* right now, we only have zero, one, or two with one being globals, which * is always the head of the stack */ if (context_id == 1) { zend_string *key; /* add super globals */ xdebug_lib_set_active_symbol_table(&EG(symbol_table)); xdebug_lib_set_active_data(NULL); ZEND_HASH_FOREACH_STR_KEY(&EG(symbol_table), key) { if (!HASH_KEY_IS_NUMERIC(key)) { add_variable_node(node, XDEBUG_STR_WRAP_CHAR(HASH_APPLY_KEY_VAL(key)), 1, 1, 0, options); } } ZEND_HASH_FOREACH_END(); xdebug_lib_set_active_symbol_table(NULL); return 0; } /* add user defined constants */ if (context_id == 2) { zend_constant *val; zend_string *const_name; ZEND_HASH_FOREACH_STR_KEY_PTR(EG(zend_constants), const_name, val) { xdebug_str *tmp_name; if (ZEND_CONSTANT_MODULE_NUMBER(val) != PHP_USER_CONSTANT) { /* we're only interested in user defined constants */ continue; } tmp_name = xdebug_str_create(ZSTR_VAL(const_name), ZSTR_LEN(const_name)); add_constant_node(node, tmp_name, &(val->value), options); xdebug_str_free(tmp_name); } ZEND_HASH_FOREACH_END(); return 0; } /* Add return value special one if set and depth = 0 */ if (XG_DBG(context).breakpoint_include_return_value && XG_DBG(current_return_value) && depth == 0) { xdebug_xml_node *tmp_node; xdebug_str *name = xdebug_str_create_from_const_char("$"XDEBUG_RETURN_VALUE_VAR_NAME); tmp_node = xdebug_get_zval_value_xml_node(name, XG_DBG(current_return_value), options); xdebug_xml_expand_attribute_value(tmp_node, "facet", "readonly return_value virtual"); xdebug_xml_add_child(node, tmp_node); xdebug_str_free(name); return 0; } /* Here the context_id is 0 */ if ((fse = xdebug_get_stack_frame(depth))) { function_stack_entry *old_fse = xdebug_get_stack_frame(depth - 1); bool must_add_this = true; if (depth > 0) { xdebug_lib_set_active_data(old_fse->execute_data); } else { xdebug_lib_set_active_data(EG(current_execute_data)); } xdebug_lib_set_active_symbol_table(fse->symbol_table); /* Only show vars when they are scanned */ xdebug_lib_register_compiled_variables(fse); if (fse->declared_vars) { xdebug_hash *tmp_hash; /* Get a hash from all the used vars (which can have duplicates) */ tmp_hash = xdebug_declared_var_hash_from_llist(fse->declared_vars); /* Check for dynamically defined variables, but make sure we don't already * have them. Also exclude superglobals and argv/argc */ if (xdebug_lib_has_active_symbol_table()) { zend_hash_apply_with_arguments(xdebug_lib_get_active_symbol_table(), (apply_func_args_t) xdebug_add_filtered_symboltable_var, 1, tmp_hash); } /* Add all the found variables to the node */ xdebug_hash_apply_with_argument(tmp_hash, (void *) node, func, (void *) options); /* Zend engine 2 does not give us $this, eval so we can get it */ if (xdebug_hash_find(tmp_hash, "this", 4, (void *) &var_name)) { must_add_this = false; } xdebug_hash_destroy(tmp_hash); } if (must_add_this) { add_variable_node(node, XDEBUG_STR_WRAP_CHAR("this"), 1, 1, 0, options); } /* Check for static variables and constants, but only if it's a static * method call as we attach constants and static properties to "this" * too normally. */ if (fse->function.type == XFUNC_STATIC_MEMBER) { zend_class_entry *ce = zend_fetch_class(fse->function.object_class, ZEND_FETCH_CLASS_DEFAULT); if (ce->type == ZEND_INTERNAL_CLASS || (ce->ce_flags & ZEND_ACC_IMMUTABLE)) { zend_class_init_statics(ce); } xdebug_var_xml_attach_static_vars(node, options, ce); } xdebug_lib_set_active_data(NULL); xdebug_lib_set_active_symbol_table(NULL); return 0; } return 1; } DBGP_FUNC(stack_depth) { xdebug_xml_add_attribute_ex(*retval, "depth", xdebug_sprintf("%lu", XDEBUG_VECTOR_COUNT(XG_BASE(stack))), 0, 1); } DBGP_FUNC(stack_get) { xdebug_xml_node *stackframe; long depth; if (CMD_OPTION_SET('d')) { depth = strtol(CMD_OPTION_CHAR('d'), NULL, 10); if (depth >= 0 && depth < (long) XDEBUG_VECTOR_COUNT(XG_BASE(stack))) { stackframe = return_stackframe(depth); xdebug_xml_add_child(*retval, stackframe); } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); } } else { function_stack_entry *fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); int i = 0; for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)); i++, fse--) { stackframe = return_stackframe(i); xdebug_xml_add_child(*retval, stackframe); } } } DBGP_FUNC(status) { xdebug_xml_add_attribute(*retval, "status", xdebug_dbgp_status_strings[XG_DBG(status)]); xdebug_xml_add_attribute(*retval, "reason", xdebug_dbgp_reason_strings[XG_DBG(reason)]); } DBGP_FUNC(context_names) { xdebug_xml_node *child; child = xdebug_xml_node_init("context"); xdebug_xml_add_attribute(child, "name", "Locals"); xdebug_xml_add_attribute(child, "id", "0"); xdebug_xml_add_child(*retval, child); child = xdebug_xml_node_init("context"); xdebug_xml_add_attribute(child, "name", "Superglobals"); xdebug_xml_add_attribute(child, "id", "1"); xdebug_xml_add_child(*retval, child); child = xdebug_xml_node_init("context"); xdebug_xml_add_attribute(child, "name", "User defined constants"); xdebug_xml_add_attribute(child, "id", "2"); xdebug_xml_add_child(*retval, child); } DBGP_FUNC(context_get) { int res; int context_id = 0; int depth = 0; xdebug_var_export_options *options = (xdebug_var_export_options*) context->options; if (CMD_OPTION_SET('c')) { context_id = atol(CMD_OPTION_CHAR('c')); } if (CMD_OPTION_SET('d')) { depth = atol(CMD_OPTION_CHAR('d')); } /* Always reset to page = 0, as it might have been modified by property_get or property_value */ options->runtime[0].page = 0; res = attach_context_vars(*retval, options, context_id, depth, attach_declared_var_with_contents); switch (res) { case 1: RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); break; } xdebug_xml_add_attribute_ex(*retval, "context", xdebug_sprintf("%d", context_id), 0, 1); } DBGP_FUNC(xcmd_profiler_name_get) { char *filename = xdebug_get_profiler_filename(); if (!filename) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_PROFILING_NOT_STARTED); } xdebug_xml_add_text(*retval, xdstrdup(filename)); } DBGP_FUNC(xcmd_get_executable_lines) { function_stack_entry *fse; unsigned int i; long depth; xdebug_xml_node *lines, *line; if (!CMD_OPTION_SET('d')) { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_INVALID_ARGS); } depth = strtol(CMD_OPTION_CHAR('d'), NULL, 10); if (depth >= 0 && depth < (long) XDEBUG_VECTOR_COUNT(XG_BASE(stack))) { fse = xdebug_get_stack_frame(depth); } else { RETURN_RESULT(XG_DBG(status), XG_DBG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); } lines = xdebug_xml_node_init("xdebug:lines"); for (i = 0; i < fse->op_array->last; i++ ) { if (fse->op_array->opcodes[i].opcode == ZEND_EXT_STMT ) { line = xdebug_xml_node_init("xdebug:line"); xdebug_xml_add_attribute_ex(line, "lineno", xdebug_sprintf("%lu", fse->op_array->opcodes[i].lineno), 0, 1); xdebug_xml_add_child(lines, line); } } xdebug_xml_add_child(*retval, lines); } /***************************************************************************** ** Parsing functions */ static int xdebug_dbgp_parse_option(xdebug_con *context, char* line, int flags, xdebug_xml_node *retval) { char *cmd = NULL; int res, ret = 0; xdebug_dbgp_arg *args; xdebug_dbgp_cmd *command; xdebug_xml_node *error; xdebug_log(XLOG_CHAN_DEBUG, XLOG_COM, "<- %s", line); res = xdebug_cmd_parse(line, (char**) &cmd, (xdebug_dbgp_arg**) &args); /* Add command name to return packet */ if (cmd) { /* if no cmd res will be XDEBUG_ERROR_PARSE */ xdebug_xml_add_attribute_ex(retval, "command", xdstrdup(cmd), 0, 1); } /* Handle missing transaction ID, and if it exist add it to the result */ if (!CMD_OPTION_SET('i')) { /* we need the transaction_id even for errors in parse_cmd, but if we error out here, just force the error to happen below */ res = XDEBUG_ERROR_INVALID_ARGS; } else { xdebug_xml_add_attribute_ex(retval, "transaction_id", xdstrdup(CMD_OPTION_CHAR('i')), 0, 1); } /* Handle parse errors */ /* FIXME: use RETURN_RESULT here too */ if (res != XDEBUG_ERROR_OK) { error = xdebug_xml_node_init("error"); xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%lu", res), 0, 1); xdebug_xml_add_child(retval, error); ADD_REASON_MESSAGE(res); } else { /* Execute commands and stuff */ command = lookup_cmd(cmd); if (command) { if (command->cont) { XG_DBG(status) = DBGP_STATUS_RUNNING; XG_DBG(reason) = DBGP_REASON_OK; } XG_DBG(lastcmd) = command->name; if (XG_DBG(lasttransid)) { xdfree(XG_DBG(lasttransid)); } XG_DBG(lasttransid) = xdstrdup(CMD_OPTION_CHAR('i')); if ( XG_DBG(status) != DBGP_STATUS_STOPPING || (XG_DBG(status) == DBGP_STATUS_STOPPING && command->flags & XDEBUG_DBGP_POST_MORTEM)) { command->handler((xdebug_xml_node**) &retval, context, args); ret = command->cont; } else { error = xdebug_xml_node_init("error"); xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%lu", XDEBUG_ERROR_COMMAND_UNAVAILABLE), 0, 1); ADD_REASON_MESSAGE(XDEBUG_ERROR_COMMAND_UNAVAILABLE); xdebug_xml_add_child(retval, error); ret = -1; } } else { error = xdebug_xml_node_init("error"); xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%lu", XDEBUG_ERROR_UNIMPLEMENTED), 0, 1); ADD_REASON_MESSAGE(XDEBUG_ERROR_UNIMPLEMENTED); xdebug_xml_add_child(retval, error); ret = -1; } } xdfree(cmd); xdebug_cmd_arg_dtor(args); return ret; } /***************************************************************************** ** Handlers for debug functions */ #define READ_BUFFER_SIZE 128 #define FD_RL_FILE 0 #define FD_RL_SOCKET 1 static char* xdebug_fd_read_line_delim(int socketfd, fd_buf *context, int type, unsigned char delim, int *length) { int size = 0, newl = 0, nbufsize = 0; char *tmp; char *tmp_buf = NULL; char *ptr; char buffer[READ_BUFFER_SIZE + 1]; if (!context->buffer) { context->buffer = calloc(1,1); context->buffer_size = 0; } while (context->buffer_size < 1 || context->buffer[context->buffer_size - 1] != delim) { ptr = context->buffer + context->buffer_size; if (type == FD_RL_FILE) { newl = read(socketfd, buffer, READ_BUFFER_SIZE); } else { newl = recv(socketfd, buffer, READ_BUFFER_SIZE, 0); } if (newl > 0) { context->buffer = realloc(context->buffer, context->buffer_size + newl + 1); memcpy(context->buffer + context->buffer_size, buffer, newl); context->buffer_size += newl; context->buffer[context->buffer_size] = '\0'; } else if (newl == -1 && errno == EINTR) { continue; } else { free(context->buffer); context->buffer = NULL; context->buffer_size = 0; return NULL; } } ptr = memchr(context->buffer, delim, context->buffer_size); size = ptr - context->buffer; /* Copy that line into tmp */ tmp = malloc(size + 1); tmp[size] = '\0'; memcpy(tmp, context->buffer, size); /* Rewrite existing buffer */ if ((nbufsize = context->buffer_size - size - 1) > 0) { tmp_buf = malloc(nbufsize + 1); memcpy(tmp_buf, ptr + 1, nbufsize); tmp_buf[nbufsize] = 0; } free(context->buffer); context->buffer = tmp_buf; context->buffer_size = context->buffer_size - (size + 1); /* Return normal line */ if (length) { *length = size; } return tmp; } static int xdebug_dbgp_cmdloop(xdebug_con *context, int bail) { char *option; int length; int ret; xdebug_xml_node *response; do { length = 0; option = xdebug_fd_read_line_delim(context->socket, context->buffer, FD_RL_SOCKET, '\0', &length); if (!option) { return 0; } response = xdebug_xml_node_init("response"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); ret = xdebug_dbgp_parse_option(context, option, 0, response); if (ret != 1) { send_message(context, response); } xdebug_xml_node_dtor(response); free(option); } while (0 == ret); if (bail && XG_DBG(status) == DBGP_STATUS_STOPPED) { _zend_bailout((char*)__FILE__, __LINE__); } return ret; } static int xdebug_compare_brk_info(const void *le1, const void *le2) { xdebug_brk_info *a = (xdebug_brk_info *) XDEBUG_LLIST_VALP(*(xdebug_llist_element **) le1); xdebug_brk_info *b = (xdebug_brk_info *) XDEBUG_LLIST_VALP(*(xdebug_llist_element **) le2); return (a->id > b->id); } int xdebug_dbgp_init(xdebug_con *context, int mode) { xdebug_var_export_options *options; xdebug_xml_node *response, *child; int i; /* initialize our status information */ if (mode == XDEBUG_REQ) { XG_DBG(status) = DBGP_STATUS_STARTING; XG_DBG(reason) = DBGP_REASON_OK; } else if (mode == XDEBUG_JIT) { XG_DBG(status) = DBGP_STATUS_BREAK; XG_DBG(reason) = DBGP_REASON_ERROR; } XG_DBG(lastcmd) = NULL; XG_DBG(lasttransid) = NULL; response = xdebug_xml_node_init("init"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); /* {{{ XML Init Stuff*/ child = xdebug_xml_node_init("engine"); xdebug_xml_add_attribute(child, "version", XDEBUG_VERSION); xdebug_xml_add_text(child, xdstrdup(XDEBUG_NAME)); xdebug_xml_add_child(response, child); child = xdebug_xml_node_init("author"); xdebug_xml_add_text(child, xdstrdup(XDEBUG_AUTHOR)); xdebug_xml_add_child(response, child); child = xdebug_xml_node_init("url"); xdebug_xml_add_text(child, xdstrdup(XDEBUG_URL)); xdebug_xml_add_child(response, child); child = xdebug_xml_node_init("copyright"); xdebug_xml_add_text(child, xdstrdup(XDEBUG_COPYRIGHT)); xdebug_xml_add_child(response, child); if (zend_string_equals_literal(context->program_name, "-") || zend_string_equals_literal(context->program_name, "Command line code")) { xdebug_xml_add_attribute_ex(response, "fileuri", xdstrdup("dbgp://stdin"), 0, 1); } else { xdebug_xml_add_attribute_ex(response, "fileuri", xdebug_path_to_url(context->program_name), 0, 1); } xdebug_xml_add_attribute_ex(response, "language", "PHP", 0, 0); xdebug_xml_add_attribute_ex(response, "xdebug:language_version", XG_BASE(php_version_run_time), 0, 0); xdebug_xml_add_attribute_ex(response, "protocol_version", DBGP_VERSION, 0, 0); xdebug_xml_add_attribute_ex(response, "appid", xdebug_sprintf(ZEND_ULONG_FMT, xdebug_get_pid()), 0, 1); if (getenv("DBGP_COOKIE")) { xdebug_xml_add_attribute_ex(response, "session", xdstrdup(getenv("DBGP_COOKIE")), 0, 1); } if (XG_DBG(context).host_type == XDEBUG_CLOUD && XINI_DBG(cloud_id) && *XINI_DBG(cloud_id)) { xdebug_xml_add_attribute_ex(response, "xdebug:userid", xdstrdup(XINI_DBG(cloud_id)), 0, 1); } if (XG_DBG(context).host_type == XDEBUG_CLOUD_FROM_TRIGGER_VALUE && XG_DBG(ide_key) && *XG_DBG(ide_key)) { xdebug_xml_add_attribute_ex(response, "xdebug:userid", xdstrdup(XG_DBG(ide_key)), 0, 1); } if (XG_DBG(context).host_type == XDEBUG_NORMAL && XG_DBG(ide_key) && *XG_DBG(ide_key)) { xdebug_xml_add_attribute_ex(response, "idekey", xdstrdup(XG_DBG(ide_key)), 0, 1); } #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT if (XG_BASE(control_socket_path)) { xdebug_xml_add_attribute_ex(response, "xdebug:ctrl_socket", xdstrdup(XG_BASE(control_socket_path)), 0, 1); } #endif context->buffer = xdmalloc(sizeof(fd_buf)); context->buffer->buffer = NULL; context->buffer->buffer_size = 0; send_message_ex(context, response, DBGP_STATUS_STARTING); xdebug_xml_node_dtor(response); /* }}} */ context->options = xdmalloc(sizeof(xdebug_var_export_options)); options = (xdebug_var_export_options*) context->options; options->max_children = 32; options->max_data = 1024; options->max_depth = 1; options->show_hidden = 0; options->extended_properties = 0; options->encode_as_extended_property = 0; options->runtime = (xdebug_var_runtime_page*) xdmalloc((options->max_depth + 1) * sizeof(xdebug_var_runtime_page)); for (i = 0; i < options->max_depth; i++) { options->runtime[i].page = 0; options->runtime[i].current_element_nr = 0; } context->breakpoint_list = xdebug_hash_alloc_with_sort(64, (xdebug_hash_dtor_t) xdebug_hash_admin_dtor, xdebug_compare_brk_info); context->function_breakpoints = xdebug_hash_alloc(64, (xdebug_hash_dtor_t) xdebug_hash_brk_dtor); context->exception_breakpoints = xdebug_hash_alloc(64, (xdebug_hash_dtor_t) xdebug_hash_brk_dtor); context->line_breakpoints = xdebug_llist_alloc((xdebug_llist_dtor) xdebug_llist_brk_dtor); context->eval_id_lookup = xdebug_hash_alloc(64, (xdebug_hash_dtor_t) xdebug_hash_eval_info_dtor); context->eval_id_sequence = 0; context->send_notifications = 0; context->inhibit_notifications = 0; context->resolved_breakpoints = 0; context->breakpoint_details = 0; context->breakpoint_include_return_value = 0; xdebug_mark_debug_connection_active(); xdebug_dbgp_cmdloop(context, XDEBUG_CMDLOOP_BAIL); return 1; } int xdebug_dbgp_deinit(xdebug_con *context) { xdebug_xml_node *response; xdebug_var_export_options *options; int detaching = (XG_DBG(status) == DBGP_STATUS_DETACHED); if (xdebug_is_debug_connection_active()) { XG_DBG(status) = DBGP_STATUS_STOPPING; XG_DBG(reason) = DBGP_REASON_OK; response = xdebug_xml_node_init("response"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); /* lastcmd and lasttransid are not always set (for example when the * connection is severed before the first command is send) */ if (XG_DBG(lastcmd) && XG_DBG(lasttransid)) { xdebug_xml_add_attribute_ex(response, "command", XG_DBG(lastcmd), 0, 0); xdebug_xml_add_attribute_ex(response, "transaction_id", XG_DBG(lasttransid), 0, 0); } xdebug_xml_add_attribute_ex(response, "status", xdebug_dbgp_status_strings[XG_DBG(status)], 0, 0); xdebug_xml_add_attribute_ex(response, "reason", xdebug_dbgp_reason_strings[XG_DBG(reason)], 0, 0); send_message(context, response); xdebug_xml_node_dtor(response); if (!detaching) { xdebug_dbgp_cmdloop(context, XDEBUG_CMDLOOP_NONBAIL); } } if (xdebug_is_debug_connection_active()) { options = (xdebug_var_export_options*) context->options; xdfree(options->runtime); xdfree(context->options); xdebug_hash_destroy(context->function_breakpoints); xdebug_hash_destroy(context->exception_breakpoints); xdebug_hash_destroy(context->eval_id_lookup); xdebug_llist_destroy(context->line_breakpoints, NULL); xdebug_hash_destroy(context->breakpoint_list); xdfree(context->buffer); context->buffer = NULL; } if (XG_DBG(lasttransid)) { xdfree(XG_DBG(lasttransid)); XG_DBG(lasttransid) = NULL; } xdebug_mark_debug_connection_not_active(); return 1; } int xdebug_dbgp_error(xdebug_con *context, int type, char *exception_type, char *message, const char *location, const unsigned int line, xdebug_vector *stack) { char *errortype; xdebug_xml_node *response, *error; if (exception_type) { errortype = exception_type; } else { errortype = xdebug_error_type(type); } if (exception_type) { XG_DBG(status) = DBGP_STATUS_BREAK; XG_DBG(reason) = DBGP_REASON_EXCEPTION; } else { switch (type) { case E_CORE_ERROR: /* no break - intentionally */ case E_ERROR: /*case E_PARSE: the parser would return 1 (failure), we can bail out nicely */ case E_COMPILE_ERROR: case E_USER_ERROR: XG_DBG(status) = DBGP_STATUS_STOPPING; XG_DBG(reason) = DBGP_REASON_ABORTED; break; default: XG_DBG(status) = DBGP_STATUS_BREAK; XG_DBG(reason) = DBGP_REASON_ERROR; } } /* runtime_allowed = ( (type != E_ERROR) && (type != E_CORE_ERROR) && (type != E_COMPILE_ERROR) && (type != E_USER_ERROR) ) ? XDEBUG_BREAKPOINT | XDEBUG_RUNTIME : 0; */ response = xdebug_xml_node_init("response"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); /* lastcmd and lasttransid are not always set (for example when the * connection is severed before the first command is send) */ if (XG_DBG(lastcmd) && XG_DBG(lasttransid)) { xdebug_xml_add_attribute_ex(response, "command", XG_DBG(lastcmd), 0, 0); xdebug_xml_add_attribute_ex(response, "transaction_id", XG_DBG(lasttransid), 0, 0); } xdebug_xml_add_attribute(response, "status", xdebug_dbgp_status_strings[XG_DBG(status)]); xdebug_xml_add_attribute(response, "reason", xdebug_dbgp_reason_strings[XG_DBG(reason)]); error = xdebug_xml_node_init("error"); xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%lu", type), 0, 1); xdebug_xml_add_attribute_ex(error, "exception", xdstrdup(errortype), 0, 1); xdebug_xml_add_text(error, xdstrdup(message)); xdebug_xml_add_child(response, error); send_message(context, response); xdebug_xml_node_dtor(response); if (!exception_type) { xdfree(errortype); } xdebug_dbgp_cmdloop(context, XDEBUG_CMDLOOP_BAIL); return 1; } int xdebug_dbgp_break_on_line(xdebug_con *context, xdebug_brk_info *brk, zend_string *orig_filename, int lineno) { zend_string *resolved_filename = orig_filename; bool free_eval_filename = false; xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "Checking whether to break on %s:%d.", ZSTR_VAL(brk->filename), brk->resolved_lineno); if (brk->disabled) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: Breakpoint is disabled."); return 0; } xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "I: Current location: %s:%d.", ZSTR_VAL(orig_filename), lineno); if (is_dbgp_url(brk->filename) && xdebug_debugger_check_evaled_code(orig_filename, &resolved_filename)) { free_eval_filename = true; xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "I: Found eval code for '%s': %s.", ZSTR_VAL(orig_filename), ZSTR_VAL(resolved_filename)); } xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "I: Matching breakpoint '%s:%d' against location '%s:%d'.", ZSTR_VAL(brk->filename), brk->resolved_lineno, ZSTR_VAL(resolved_filename), lineno); if (brk->resolved_lineno != lineno) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: Line number (%d) doesn't match with breakpoint (%d).", lineno, brk->resolved_lineno); if (free_eval_filename) { zend_string_release(resolved_filename); } return 0; } if (zend_string_equals_ci(brk->filename, resolved_filename)) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "F: File names match (%s).", ZSTR_VAL(brk->filename)); if (free_eval_filename) { zend_string_release(resolved_filename); } return 1; } xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: File names (%s) doesn't match with breakpoint (%s).", ZSTR_VAL(resolved_filename), ZSTR_VAL(brk->filename)); if (free_eval_filename) { zend_string_release(resolved_filename); } return 0; } int xdebug_dbgp_breakpoint(xdebug_con *context, xdebug_vector *stack, zend_string *filename, long lineno, int type, char *exception, char *code, const char *message, xdebug_brk_info *brk_info, zval *return_value) { xdebug_xml_node *response, *error_container; XG_DBG(status) = DBGP_STATUS_BREAK; XG_DBG(reason) = DBGP_REASON_OK; response = xdebug_xml_node_init("response"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); /* lastcmd and lasttransid are not always set (for example when the * connection is severed before the first command is send) */ if (XG_DBG(lastcmd) && XG_DBG(lasttransid)) { xdebug_xml_add_attribute_ex(response, "command", XG_DBG(lastcmd), 0, 0); xdebug_xml_add_attribute_ex(response, "transaction_id", XG_DBG(lasttransid), 0, 0); } xdebug_xml_add_attribute(response, "status", xdebug_dbgp_status_strings[XG_DBG(status)]); xdebug_xml_add_attribute(response, "reason", xdebug_dbgp_reason_strings[XG_DBG(reason)]); error_container = xdebug_xml_node_init("xdebug:message"); if (filename) { zend_string *tmp_filename = NULL; if (xdebug_debugger_check_evaled_code(filename, &tmp_filename)) { xdebug_xml_add_attribute_ex(error_container, "filename", ZSTR_VAL(tmp_filename), 0, 0); zend_string_release(tmp_filename); } else { xdebug_xml_add_attribute_ex(error_container, "filename", xdebug_path_to_url(filename), 0, 1); } } if (lineno) { xdebug_xml_add_attribute_ex(error_container, "lineno", xdebug_sprintf("%lu", lineno), 0, 1); } if (exception) { xdebug_xml_add_attribute_ex(error_container, "exception", xdstrdup(exception), 0, 1); } if (code) { xdebug_xml_add_attribute_ex(error_container, "code", xdstrdup(code), 0, 1); } if (message) { xdebug_xml_add_text(error_container, xdstrdup(message)); } xdebug_xml_add_child(response, error_container); if (XG_DBG(context).breakpoint_include_return_value && return_value) { xdebug_xml_node *return_value_container, *tmp_node; xdebug_var_export_options *options = (xdebug_var_export_options*) context->options; return_value_container = xdebug_xml_node_init("xdebug:return_value"); tmp_node = xdebug_get_zval_value_xml_node(NULL, return_value, options); xdebug_xml_add_child(return_value_container, tmp_node); xdebug_xml_add_child(response, return_value_container); } if (XG_DBG(context).breakpoint_details && brk_info) { xdebug_xml_node *breakpoint_node = xdebug_xml_node_init("breakpoint"); breakpoint_brk_info_add(breakpoint_node, brk_info); xdebug_xml_add_child(response, breakpoint_node); } send_message(context, response); xdebug_xml_node_dtor(response); XG_DBG(lastcmd) = NULL; if (XG_DBG(lasttransid)) { xdfree(XG_DBG(lasttransid)); XG_DBG(lasttransid) = NULL; } XG_DBG(current_return_value) = return_value; if (XG_DBG(current_return_value)) { Z_TRY_ADDREF_P(XG_DBG(current_return_value)); } xdebug_dbgp_cmdloop(context, XDEBUG_CMDLOOP_BAIL); if (XG_DBG(current_return_value)) { Z_TRY_DELREF_P(XG_DBG(current_return_value)); } XG_DBG(current_return_value) = NULL; return xdebug_is_debug_connection_active(); } static int xdebug_dbgp_resolved_breakpoint_notification(xdebug_con *context, xdebug_brk_info *brk_info) { xdebug_xml_node *response, *child; if (!context->send_notifications) { return 0; } response = xdebug_xml_node_init("notify"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); xdebug_xml_add_attribute(response, "name", "breakpoint_resolved"); child = xdebug_xml_node_init("breakpoint"); breakpoint_brk_info_add(child, brk_info); xdebug_xml_add_child(response, child); send_message(context, response); xdebug_xml_node_dtor(response); return 1; } /* static void function_breakpoint_resolve_helper(void *rctxt, xdebug_brk_info *brk_info, xdebug_hash_element *he) { xdebug_dbgp_resolve_context *ctxt = (xdebug_dbgp_resolve_context*) rctxt; if (brk_info->resolved == XDEBUG_BRK_RESOLVED) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: %s breakpoint for '%s' has already been resolved.", XDEBUG_BREAKPOINT_TYPE_NAME(brk_info->brk_type), ctxt->fse->function.function); return; } if (ctxt->fse->function.type == XFUNC_NORMAL) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "I: '%s' is a normal function (%02x).", ctxt->fse->function.function, ctxt->fse->function.type); if (strcmp(ctxt->fse->function.function, brk_info->functionname) == 0) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "F: Breakpoint function (%s) matches current function (%s).", brk_info->functionname, ctxt->fse->function.function); brk_info->resolved = XDEBUG_BRK_RESOLVED; xdebug_dbgp_resolved_breakpoint_notification(ctxt->context, brk_info); return; } } else if (ctxt->fse->function.type == XFUNC_MEMBER || ctxt->fse->function.type == XFUNC_STATIC_MEMBER) { char *tmp_name = NULL; size_t tmp_len = 0; tmp_len = strlen(ctxt->fse->function.class) + strlen(ctxt->fse->function.function) + 3; tmp_name = xdmalloc(tmp_len); snprintf(tmp_name, tmp_len, "%s::%s", ctxt->fse->function.class, ctxt->fse->function.function); xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "I: '%s::%s' is a normal method (%02x).", ctxt->fse->function.class, ctxt->fse->function.function, ctxt->fse->function.type); if (strcmp(tmp_name, brk_info->functionname) == 0) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "F: Breakpoint method (%s) matches current method (%s).", brk_info->functionname, tmp_name); brk_info->resolved = XDEBUG_BRK_RESOLVED; xdebug_dbgp_resolved_breakpoint_notification(ctxt->context, brk_info); xdfree(tmp_name); return; } xdfree(tmp_name); } else { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: We don't handle this function type (%02x) yet.", ctxt->fse->function.type); return; } } */ static xdebug_function_lines_map_item* find_smallest_range(xdebug_lines_list *lines_list, xdebug_brk_info *brk_info) { int i; xdebug_function_lines_map_item *found_item = NULL; int found_item_span = XDEBUG_RESOLVED_SPAN_MAX; /* Loop over all definitions in lines_list for file */ for (i = 0; i < lines_list->count; i++) { xdebug_function_lines_map_item *item = lines_list->functions[i]; /* Loop over all the file/line list entries to find the best fitting one for 'brk_info->original_lineno' */ if (brk_info->original_lineno < item->line_start || brk_info->original_lineno > item->line_end) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: Line number (%d) out of range (%zd-%zd).", brk_info->original_lineno, item->line_start, item->line_end); continue; } if (item->line_span < found_item_span) { found_item = item; found_item_span = item->line_span; } } return found_item; } static void line_breakpoint_resolve_helper(xdebug_con *context, xdebug_lines_list *lines_list, xdebug_brk_info *brk_info) { xdebug_function_lines_map_item *found_item = NULL; /* Find smallest function span that fits the line nr in brk_info */ found_item = find_smallest_range(lines_list, brk_info); if (!found_item) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: Could not find any file/line entry in lines list."); return; } xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: Line number (%d) in smallest range of range (%zd-%zd).", brk_info->original_lineno, found_item->line_start, found_item->line_end); /* If the breakpoint's line number is in the set, mark as resolved */ if (xdebug_set_in(found_item->lines_breakable, brk_info->original_lineno)) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "F: Breakpoint line (%d) found in set of executable lines.", brk_info->original_lineno); brk_info->resolved_lineno = brk_info->original_lineno; brk_info->resolved = XDEBUG_BRK_RESOLVED; xdebug_dbgp_resolved_breakpoint_notification(context, brk_info); return; } else { int tmp_lineno; xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "I: Breakpoint line (%d) NOT found in set of executable lines.", brk_info->original_lineno); /* Check for a following line in the function */ tmp_lineno = brk_info->original_lineno; do { tmp_lineno++; if (xdebug_set_in(found_item->lines_breakable, tmp_lineno)) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, " F: Line (%d) in set.", tmp_lineno); brk_info->resolved_lineno = tmp_lineno; brk_info->resolved = XDEBUG_BRK_RESOLVED; xdebug_dbgp_resolved_breakpoint_notification(context, brk_info); return; } else { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, " I: Line (%d) not in set.", tmp_lineno); } } while (tmp_lineno < found_item->line_end && (tmp_lineno < brk_info->original_lineno + XDEBUG_DBGP_SCAN_RANGE)); /* Check for a previous line in the function */ tmp_lineno = brk_info->original_lineno; do { tmp_lineno--; if (xdebug_set_in(found_item->lines_breakable, tmp_lineno)) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, " F: Line (%d) in set.", tmp_lineno); brk_info->resolved_lineno = tmp_lineno; brk_info->resolved = XDEBUG_BRK_RESOLVED; xdebug_dbgp_resolved_breakpoint_notification(context, brk_info); return; } else { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, " I: Line (%d) not in set.", tmp_lineno); } } while (tmp_lineno > found_item->line_start && (tmp_lineno > brk_info->original_lineno - XDEBUG_DBGP_SCAN_RANGE)); } } static void breakpoint_resolve_helper(void *rctxt, xdebug_hash_element *he, void *dummy) { xdebug_dbgp_resolve_context *ctxt = (xdebug_dbgp_resolve_context*) rctxt; xdebug_brk_admin *admin = (xdebug_brk_admin*) he->ptr; xdebug_brk_info *brk_info; brk_info = breakpoint_brk_info_fetch(admin->type, admin->key); xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "Breakpoint %d (type: %s).", admin->id, XDEBUG_BREAKPOINT_TYPE_NAME(brk_info->brk_type)); /* Bail early if it's already resolved */ if (brk_info->resolved == XDEBUG_BRK_RESOLVED) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "D: Breakpoint %d (type: %s) is already resolved.", admin->id, XDEBUG_BREAKPOINT_TYPE_NAME(brk_info->brk_type)); return; } switch (brk_info->brk_type) { case XDEBUG_BREAKPOINT_TYPE_LINE: case XDEBUG_BREAKPOINT_TYPE_CONDITIONAL: if (!zend_string_equals(brk_info->filename, ctxt->filename)) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: File name (%s) does not match breakpoint to resolve (%s).", ZSTR_VAL(ctxt->filename), ZSTR_VAL(brk_info->filename)); return; } line_breakpoint_resolve_helper(ctxt->context, ctxt->lines_list, brk_info); return; /* case XDEBUG_BREAKPOINT_TYPE_CALL: case XDEBUG_BREAKPOINT_TYPE_RETURN: function_breakpoint_resolve_helper(rctxt, brk_info, he); return; */ default: xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "R: The breakpoint type '%s' can not be resolved.", XDEBUG_BREAKPOINT_TYPE_NAME(brk_info->brk_type)); return; } } /* Fetches the lines list for 'filename', and loops over all breakpoints to try * to resolve them at run-time */ int xdebug_dbgp_resolve_breakpoints(xdebug_con *context, zend_string *filename) { xdebug_dbgp_resolve_context resolv_ctxt; xdebug_lines_list *lines_list; /* Get the lines list for the current file */ if (!XG_DBG(breakable_lines_map) || !xdebug_hash_find(XG_DBG(breakable_lines_map), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &lines_list)) { xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "E: Lines list for '%s' does not exist.", ZSTR_VAL(filename)); return 0; } resolv_ctxt.context = context; resolv_ctxt.filename = filename; resolv_ctxt.lines_list = lines_list; xdebug_hash_apply_with_argument(context->breakpoint_list, (void *) &resolv_ctxt, breakpoint_resolve_helper, NULL); return 1; } int xdebug_dbgp_stream_output(const char *string, unsigned int length) { if ((XG_DBG(stdout_mode) == 1 || XG_DBG(stdout_mode) == 2) && length) { xdebug_send_stream("stdout", string, length); } if (XG_DBG(stdout_mode) == 0 || XG_DBG(stdout_mode) == 1) { return 0; } return -1; } int xdebug_dbgp_notification(xdebug_con *context, zend_string *filename, long lineno, int type, char *type_string, char *message) { xdebug_xml_node *response, *error_container; response = xdebug_xml_node_init("notify"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); xdebug_xml_add_attribute(response, "name", "error"); error_container = xdebug_xml_node_init("xdebug:message"); if (filename) { zend_string *tmp_filename = NULL; if (xdebug_debugger_check_evaled_code(filename, &tmp_filename)) { xdebug_xml_add_attribute_ex(error_container, "filename", ZSTR_VAL(tmp_filename), 0, 0); zend_string_release(tmp_filename); } else { xdebug_xml_add_attribute_ex(error_container, "filename", xdebug_path_to_url(filename), 0, 1); } } if (lineno) { xdebug_xml_add_attribute_ex(error_container, "lineno", xdebug_sprintf("%lu", lineno), 0, 1); } if (type_string) { xdebug_xml_add_attribute_ex(error_container, "type", xdstrdup(type_string), 0, 1); } if (message) { char *tmp_buf; if (type == E_ERROR && ((tmp_buf = xdebug_strip_php_stack_trace(message)) != NULL)) { xdebug_xml_add_text(error_container, tmp_buf); } else { xdebug_xml_add_text(error_container, xdstrdup(message)); } } xdebug_xml_add_child(response, error_container); send_message(context, response); xdebug_xml_node_dtor(response); return 1; } int xdebug_dbgp_user_notify(xdebug_con *context, zend_string *filename, long lineno, zval *data) { xdebug_xml_node *response, *data_node, *location_node; xdebug_var_export_options *options; if (!context->send_notifications) { return 0; } response = xdebug_xml_node_init("notify"); xdebug_xml_add_attribute(response, "xmlns", "urn:debugger_protocol_v1"); xdebug_xml_add_attribute(response, "xmlns:xdebug", "https://xdebug.org/dbgp/xdebug"); xdebug_xml_add_attribute(response, "name", "user"); options = xdebug_var_export_options_from_ini(); options->extended_properties = 1; location_node = xdebug_xml_node_init("xdebug:location"); if (filename) { zend_string *tmp_filename = NULL; if (xdebug_debugger_check_evaled_code(filename, &tmp_filename)) { xdebug_xml_add_attribute_ex(location_node, "filename", ZSTR_VAL(tmp_filename), 0, 0); zend_string_release(tmp_filename); } else { xdebug_xml_add_attribute_ex(location_node, "filename", xdebug_path_to_url(filename), 0, 1); } } if (lineno) { xdebug_xml_add_attribute_ex(location_node, "lineno", xdebug_sprintf("%lu", lineno), 0, 1); } xdebug_xml_add_child(response, location_node); data_node = xdebug_xml_node_init("property"); xdebug_var_export_xml_node(&data, NULL, data_node, options, 0); xdebug_xml_add_child(response, data_node); send_message(context, response); xdebug_xml_node_dtor(response); xdfree(options->runtime); xdfree(options); return 1; } static char *create_eval_key_file(zend_string *filename, int lineno) { return xdebug_sprintf("%s(%d) : eval()'d code", ZSTR_VAL(filename), lineno); } static char *create_eval_key_id(int id) { return xdebug_sprintf("%04x", id); } int xdebug_dbgp_register_eval_id(xdebug_con *context, function_stack_entry *fse) { char *key; xdebug_eval_info *ei; context->eval_id_sequence++; ei = xdcalloc(1, sizeof(xdebug_eval_info)); ei->id = context->eval_id_sequence; ei->contents = zend_string_copy(fse->function.include_filename); ei->refcount = 2; key = create_eval_key_file(fse->filename, fse->lineno); xdebug_hash_add(context->eval_id_lookup, key, strlen(key), (void*) ei); xdfree(key); key = create_eval_key_id(ei->id); xdebug_hash_add(context->eval_id_lookup, key, strlen(key), (void*) ei); xdfree(key); return ei->id; } xdebug-3.4.3/src/debugger/handler_dbgp.h0000664000175000017500000001131415011062311017410 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __HAVE_XDEBUG_HANDLER_DBGP_H__ #define __HAVE_XDEBUG_HANDLER_DBGP_H__ #include #include "handlers.h" #include "lib/cmd_parser.h" #include "lib/xml.h" #define DBGP_VERSION "1.0" typedef struct xdebug_dbgp_result { int status; int reason; int code; } xdebug_dbgp_result; #define ADD_REASON_MESSAGE(c) { \ xdebug_xml_node *message = xdebug_xml_node_init("message"); \ xdebug_xml_add_text(message, xdstrdup(error_message_from_code(c))); \ xdebug_xml_add_child(error, message); \ } #define RETURN_RESULT(s, r, c) { \ xdebug_xml_node *error = xdebug_xml_node_init("error"); \ xdebug_xml_node *message = xdebug_xml_node_init("message"); \ \ xdebug_xml_add_attribute(*retval, "status", xdebug_dbgp_status_strings[(s)]); \ xdebug_xml_add_attribute(*retval, "reason", xdebug_dbgp_reason_strings[(r)]); \ xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%u", (c)), 0, 1); \ xdebug_xml_add_text(message, xdstrdup(error_message_from_code(c))); \ xdebug_xml_add_child(error, message); \ \ xdebug_xml_add_child(*retval, error); \ return; \ } #define RETURN_RESULT_WITH_MESSAGE(s, r, c, m) { \ xdebug_xml_node *error = xdebug_xml_node_init("error"); \ xdebug_xml_node *message = xdebug_xml_node_init("message"); \ \ xdebug_xml_add_attribute(*retval, "status", xdebug_dbgp_status_strings[(s)]); \ xdebug_xml_add_attribute(*retval, "reason", xdebug_dbgp_reason_strings[(r)]); \ xdebug_xml_add_attribute_ex(error, "code", xdebug_sprintf("%u", (c)), 0, 1); \ xdebug_xml_add_text(message, (m)); \ xdebug_xml_add_child(error, message); \ xdebug_xml_add_child(*retval, error); \ return; \ } #define DBGP_FUNC_PARAMETERS xdebug_xml_node **retval, xdebug_con *context, xdebug_dbgp_arg *args #define DBGP_FUNC_PASS_PARAMETERS retval, context, args #define DBGP_FUNC(name) static void xdebug_dbgp_handle_##name(DBGP_FUNC_PARAMETERS) #define DBGP_FUNC_ENTRY(name,flags) { #name, xdebug_dbgp_handle_##name, 0, flags }, #define DBGP_CONT_FUNC_ENTRY(name,flags) { #name, xdebug_dbgp_handle_##name, 1, flags }, #define DBGP_STOP_FUNC_ENTRY(name,flags) { #name, xdebug_dbgp_handle_##name, 2, flags }, #define XDEBUG_DBGP_NONE 1 << 0 #define XDEBUG_DBGP_POST_MORTEM 1 << 1 /* command is valid in 'stopping' (post mortem) state */ typedef struct xdebug_dbgp_cmd { const char *name; void (*handler)(DBGP_FUNC_PARAMETERS); int cont; int flags; } xdebug_dbgp_cmd; typedef struct xdebug_dbgp_resolve_context { xdebug_con *context; zend_string *filename; xdebug_lines_list *lines_list; } xdebug_dbgp_resolve_context; int xdebug_dbgp_init(xdebug_con *context, int mode); int xdebug_dbgp_deinit(xdebug_con *context); int xdebug_dbgp_error(xdebug_con *context, int type, char *exception_type, char *message, const char *location, const unsigned int line, xdebug_vector *stack); int xdebug_dbgp_break_on_line(xdebug_con *context, xdebug_brk_info *brk, zend_string *filename, int lineno); int xdebug_dbgp_breakpoint(xdebug_con *context, xdebug_vector *stack, zend_string *filename, long lineno, int type, char *exception, char *code, const char *message, xdebug_brk_info *brk_info, zval *return_value); int xdebug_dbgp_resolve_breakpoints(xdebug_con *context, zend_string *filename); int xdebug_dbgp_stream_output(const char *string, unsigned int length); int xdebug_dbgp_notification(xdebug_con *context, zend_string *filename, long lineno, int type, char *type_string, char *message); int xdebug_dbgp_user_notify(xdebug_con *context, zend_string *filename, long lineno, zval *data); void XDEBUG_ATTRIBUTE_FORMAT(printf, 2, 3) xdebug_dbgp_log(int log_level, const char *fmt, ...); int xdebug_dbgp_register_eval_id(xdebug_con *context, function_stack_entry *fse); extern xdebug_remote_handler xdebug_handler_dbgp; #endif xdebug-3.4.3/src/debugger/ip_info.c0000664000175000017500000001544215011062311016423 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifdef __linux__ #include "php_xdebug.h" #include #include #include #include #include #include #include #include #include #include #include "ip_info.h" #if XDEBUG_GATEWAY_SUPPORT #define BUFFER_SIZE 4096 static char *convert_to_quad(int domain, void *buf) { char *ip = xdcalloc(1, INET6_ADDRSTRLEN + 1); inet_ntop(domain, buf, ip, INET6_ADDRSTRLEN); return ip; } static int get_ip(int fd, struct sockaddr_nl *sa, int domain) { char buf[BUFFER_SIZE]; struct nlmsghdr *nl; struct ifaddrmsg *ifa; struct iovec iov = { 0 }; struct msghdr msg = { 0 }; int r; memset(buf, 0, BUFFER_SIZE); /* Assemble the message according to the netlink protocol */ nl = (struct nlmsghdr*)buf; nl->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); nl->nlmsg_type = RTM_GETADDR; nl->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; ifa = (struct ifaddrmsg*)NLMSG_DATA(nl); ifa->ifa_family = domain; // We only get IPv4 address here /* Prepare struct msghdr for sending */ iov.iov_base = nl; iov.iov_len = nl->nlmsg_len; msg.msg_name = sa; msg.msg_namelen = sizeof(*sa); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; /* Send netlink message to kernel */ r = sendmsg(fd, &msg, 0); return (r < 0) ? -1 : 0; } static int get_msg(int fd, struct sockaddr_nl *sa, void *buf, size_t len) { struct iovec iov; struct msghdr msg; iov.iov_base = buf; iov.iov_len = len; memset(&msg, 0, sizeof(msg)); msg.msg_name = sa; msg.msg_namelen = sizeof(*sa); msg.msg_iov = &iov; msg.msg_iovlen = 1; return recvmsg(fd, &msg, 0); } static char *parse_ifa_msg(struct ifaddrmsg *ifa, void *buf, size_t len, const char *wanted_iface) { char ifname[IF_NAMESIZE]; struct rtattr *rta = NULL; int fa = ifa->ifa_family; if_indextoname(ifa->ifa_index, ifname); if (strcmp(ifname, wanted_iface) != 0) { return NULL; } for (rta = (struct rtattr*)buf; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { if (rta->rta_type == IFA_ADDRESS) { return convert_to_quad(fa, RTA_DATA(rta)); } } return NULL; } static uint32_t parse_nl_msg(void *buf, size_t len, const char *iface, char **if_address) { struct nlmsghdr *nl = NULL; uint32_t nlmsg_type = NLMSG_ERROR; for ( nl = (struct nlmsghdr*)buf; NLMSG_OK(nl, (uint32_t)len) && nl->nlmsg_type != NLMSG_DONE; nl = NLMSG_NEXT(nl, len) ) { nlmsg_type = nl->nlmsg_type; if (nl->nlmsg_type == NLMSG_ERROR) { return -1; } if (nl->nlmsg_type == RTM_NEWADDR) { struct ifaddrmsg *ifa; ifa = (struct ifaddrmsg*)NLMSG_DATA(nl); if (*if_address == NULL) { *if_address = parse_ifa_msg(ifa, IFA_RTA(ifa), IFA_PAYLOAD(nl), iface); if (*if_address) { return NLMSG_DONE; } } continue; } } return nlmsg_type; } char *xdebug_get_ip_for_interface(const char *iface) { char buf[BUFFER_SIZE]; int fd = 0, len = 0; char *if_address = NULL; uint32_t nl_msg_type = NLMSG_DONE; struct sockaddr_nl sa; /* Create a socket with the AF_NETLINK domain */ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { return NULL; } memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; len = get_ip(fd, &sa, AF_INET); // For IPv6: use AF_INET6 instead if (len < 0) { return NULL; } do { len = get_msg(fd, &sa, buf, BUFFER_SIZE); if (len < 0) { return NULL; } nl_msg_type = parse_nl_msg(buf, len, iface, &if_address); } while (nl_msg_type != NLMSG_DONE && nl_msg_type != NLMSG_ERROR); return if_address; } /***/ static int get_gateway_and_iface(in_addr_t *addr, char *interface) { long destination, gateway; char iface[IF_NAMESIZE]; char buf[BUFFER_SIZE]; FILE *file; memset(iface, 0, sizeof(iface)); memset(buf, 0, sizeof(buf)); file = fopen("/proc/net/route", "r"); if (!file) { return 0; } while (fgets(buf, sizeof(buf), file)) { if (sscanf(buf, "%s %lx %lx", iface, &destination, &gateway) == 3) { if (destination == 0) { /* default */ *addr = gateway; strcpy(interface, iface); fclose(file); return 1; } } } /* default route not found */ if (file) { fclose(file); } return 0; } char *xdebug_get_gateway_ip(void) { in_addr_t addr = 0; char iface[IF_NAMESIZE]; char addrbuf[INET6_ADDRSTRLEN]; memset(iface, 0, sizeof(iface)); if (get_gateway_and_iface(&addr, iface)) { return xdstrdup(inet_ntop(AF_INET, &addr, addrbuf, sizeof(addrbuf))); } return NULL; } #endif /* XDEBUG_GATEWAY_SUPPORT */ /***/ #if XDEBUG_NAMESERVER_SUPPORT char *xdebug_get_private_nameserver(void) { res_state res = malloc(sizeof(struct __res_state)); in_addr_t ns_addr = 0; char nameserver_buf[20]; char *nameserver = NULL; res_ninit(res); if (res->nscount > 0 && res->nsaddr_list[0].sin_family == AF_INET) { ns_addr = res->nsaddr_list[0].sin_addr.s_addr; /* Only allow private networks */ if ( ((ns_addr & 0x000000ff) == 0x0000000a) || // 10.x.x.x/24 ((ns_addr & 0x0000f0ff) == 0x000010ac) || // 172.16.x.x/20 ((ns_addr & 0x0000ffff) == 0x0000a8c0) || // 192.168.x.x/16 ((ns_addr & 0x000000ff) == 0x0000007f) // 127.x.x.x/8 ) { snprintf( nameserver_buf, 16, "%d.%d.%d.%d", (ns_addr & 0xff), (ns_addr & 0xff00) >> 8, (ns_addr & 0xff0000) >> 16, (ns_addr & 0xff000000) >> 24 ); nameserver = xdstrdup(nameserver_buf); } } res_nclose(res); free(res); return nameserver; } #endif /* XDEBUG_NAMESERVER_SUPPORT */ # if 0 int main(int argc, char *argv[]) { char *gateway_address = xdebug_get_gateway_ip(); char *ip_address = xdebug_get_ip_for_interface(argv[1]); printf("Gateway: %s\n", gateway_address); printf("IP: %s\n", ip_address); free(ip_address); free(gateway_address); } # endif // int main #endif // __linux__ xdebug-3.4.3/src/debugger/ip_info.h0000664000175000017500000000262715011062311016431 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifdef __linux__ #include "php_xdebug.h" char *xdebug_get_ip_for_interface(const char *iface); #if HAVE_LINUX_RTNETLINK_H # define XDEBUG_GATEWAY_SUPPORT 1 char *xdebug_get_gateway_ip(void); #else # define XDEBUG_GATEWAY_SUPPORT 0 #endif #if HAVE_RES_NINIT && HAVE_RES_NCLOSE # define XDEBUG_NAMESERVER_SUPPORT 1 char *xdebug_get_private_nameserver(void); #else # define XDEBUG_NAMESERVER_SUPPORT 0 #endif #endif /* __linux__ */ xdebug-3.4.3/src/gcstats/gc_stats.c0000664000175000017500000002250015011062311016464 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "zend_builtin_functions.h" #include "SAPI.h" #include "Zend/zend_long.h" #include "php_xdebug.h" #include "gc_stats_private.h" #include "base/base.h" #include "lib/lib.h" #include "lib/log.h" /* Set correct int format to use */ #define XDEBUG_GCINT_FMT ZEND_LONG_FMT_SPEC ZEND_EXTERN_MODULE_GLOBALS(xdebug) static void xdebug_gc_stats_print_run(xdebug_gc_run *run); static void xdebug_gc_stats_run_free(xdebug_gc_run *run); int (*xdebug_old_gc_collect_cycles)(void); static int xdebug_gc_collect_cycles(void) { int ret; uint32_t collected; xdebug_gc_run *run; zend_execute_data *execute_data; long int memory; uint64_t start; xdebug_func tmp; zend_gc_status status; if (!XG_GCSTATS(active)) { return xdebug_old_gc_collect_cycles(); } execute_data = EG(current_execute_data); zend_gc_get_status(&status); collected = status.collected; start = xdebug_get_nanotime(); memory = zend_memory_usage(0); ret = xdebug_old_gc_collect_cycles(); run = xdmalloc(sizeof(xdebug_gc_run)); run->function_name = NULL; run->class_name = NULL; zend_gc_get_status(&status); run->collected = status.collected - collected; run->duration = xdebug_get_nanotime() - start; run->memory_before = memory; run->memory_after = zend_memory_usage(0); xdebug_build_fname(&tmp, execute_data); run->function_name = tmp.function ? zend_string_copy(tmp.function) : NULL; run->class_name = tmp.object_class ? zend_string_copy(tmp.object_class) : NULL; xdebug_gc_stats_print_run(run); xdebug_gc_stats_run_free(run); xdebug_func_dtor_by_ref(&tmp); return ret; } static void xdebug_gc_stats_run_free(xdebug_gc_run *run) { if (!run) { return; } if (run->function_name) { zend_string_release(run->function_name); } if (run->class_name) { zend_string_release(run->class_name); } xdfree(run); } static int xdebug_gc_stats_init(char *requested_filename, zend_string *script_name) { char *filename_to_use = NULL; char *generated_filename = NULL; char *output_dir = xdebug_lib_get_output_dir(); /* not duplicated */ if (!gc_enabled()) { xdebug_log_ex(XLOG_CHAN_GCSTATS, XLOG_ERR, "DISABLED", "PHP's Garbage Collection is disabled"); return FAILURE; } if (requested_filename && strlen(requested_filename)) { filename_to_use = xdstrdup(requested_filename); } else { if (!strlen(XINI_GCSTATS(output_name)) || xdebug_format_output_filename(&generated_filename, XINI_GCSTATS(output_name), ZSTR_VAL(script_name)) <= 0) { return FAILURE; } if (IS_SLASH(output_dir[strlen(output_dir) - 1])) { filename_to_use = xdebug_sprintf("%s%s", output_dir, generated_filename); } else { filename_to_use = xdebug_sprintf("%s%c%s", output_dir, DEFAULT_SLASH, generated_filename); } } XG_GCSTATS(file) = xdebug_fopen(filename_to_use, "w", NULL, &XG_GCSTATS(filename)); if (!XG_GCSTATS(file)) { xdebug_log_diagnose_permissions(XLOG_CHAN_GCSTATS, output_dir, filename_to_use); xdfree(filename_to_use); if (generated_filename) { xdfree(generated_filename); } return FAILURE; } xdfree(filename_to_use); fprintf(XG_GCSTATS(file), "Garbage Collection Report\n"); fprintf(XG_GCSTATS(file), "version: 1\ncreator: xdebug %s (PHP %s)\n\n", XDEBUG_VERSION, PHP_VERSION); fprintf(XG_GCSTATS(file), "Collected | Efficiency%% | Duration | Memory Before | Memory After | Reduction%% | Function\n"); fprintf(XG_GCSTATS(file), "----------+-------------+----------+---------------+--------------+------------+---------\n"); fflush(XG_GCSTATS(file)); if (generated_filename) { xdfree(generated_filename); } return SUCCESS; } static void xdebug_gc_stats_stop() { XG_GCSTATS(active) = 0; if (XG_GCSTATS(file)) { if (!gc_enabled()) { fprintf(XG_GCSTATS(file), "Garbage Collection Disabled End\n"); xdebug_log_ex(XLOG_CHAN_GCSTATS, XLOG_ERR, "DISABLED", "PHP's Garbage Collection is disabled at the end of the script"); } fclose(XG_GCSTATS(file)); XG_GCSTATS(file) = NULL; } } #define AS_PERCENT(n) ((n)*100.0) #define ROOTS_AS_PERCENT(n) AS_PERCENT((n) / 10000.0) static void xdebug_gc_stats_print_run(xdebug_gc_run *run) { double reduction; if (run->memory_before) { reduction = (1 - (float)run->memory_after / (float)run->memory_before) * 100.0; } else { reduction = 0; } if (!XG_GCSTATS(file)) { return; } if (!run->function_name) { fprintf(XG_GCSTATS(file), "%9" XDEBUG_GCINT_FMT " | %9.2f %% | %5.2f ms | %13" XDEBUG_GCINT_FMT " | %12" XDEBUG_GCINT_FMT " | %8.2f %% | -\n", run->collected, ROOTS_AS_PERCENT(run->collected), run->duration / (double)NANOS_IN_MILLISEC, run->memory_before, run->memory_after, reduction ); } else if (!run->class_name && run->function_name) { fprintf(XG_GCSTATS(file), "%9" XDEBUG_GCINT_FMT " | %9.2f %% | %5.2f ms | %13" XDEBUG_GCINT_FMT " | %12" XDEBUG_GCINT_FMT " | %8.2f %% | %s\n", run->collected, ROOTS_AS_PERCENT(run->collected), run->duration / (double)NANOS_IN_MILLISEC, run->memory_before, run->memory_after, reduction, ZSTR_VAL(run->function_name) ); } else if (run->class_name && run->function_name) { fprintf(XG_GCSTATS(file), "%9" XDEBUG_GCINT_FMT " | %9.2f %% | %5.2f ms | %13" XDEBUG_GCINT_FMT " | %12" XDEBUG_GCINT_FMT " | %8.2f %% | %s::%s\n", run->collected, ROOTS_AS_PERCENT(run->collected), run->duration / (double)NANOS_IN_MILLISEC, run->memory_before, run->memory_after, reduction, ZSTR_VAL(run->class_name), ZSTR_VAL(run->function_name) ); } fflush(XG_GCSTATS(file)); } /* {{{ proto void xdebug_get_gcstats_filename() Returns the name of the current garbage collection statistics report file */ PHP_FUNCTION(xdebug_get_gcstats_filename) { if (XG_GCSTATS(filename)) { RETURN_STRING(XG_GCSTATS(filename)); } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto void xdebug_start_gcstats([string $fname]) Start collecting garbage collection statistics */ PHP_FUNCTION(xdebug_start_gcstats) { char *fname = NULL; size_t fname_len = 0; function_stack_entry *fse; if (XG_GCSTATS(active)) { php_error(E_NOTICE, "Garbage Collection statistics are already being collected."); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &fname, &fname_len) == FAILURE) { return; } fse = xdebug_get_stack_frame(0); if (fse && xdebug_gc_stats_init(fname, fse->filename) == SUCCESS) { XG_GCSTATS(active) = 1; RETVAL_STRING(XG_GCSTATS(filename)); return; } php_error(E_NOTICE, "Garbage Collection statistics could not be started"); XG_GCSTATS(active) = 0; RETURN_FALSE; } /* }}} */ /* {{{ proto void xdebug_stop_gcstats() Stop collecting garbage collection statistics */ PHP_FUNCTION(xdebug_stop_gcstats) { if (!XG_GCSTATS(active)) { php_error(E_NOTICE, "Garbage Collection statistics was not started"); RETURN_FALSE; } xdebug_gc_stats_stop(); RETURN_STRING(XG_GCSTATS(filename)); } /* {{{ proto void xdebug_get_gc_run_count() Return number of times garbage collection was triggered. */ PHP_FUNCTION(xdebug_get_gc_run_count) { zend_gc_status status; zend_gc_get_status(&status); RETURN_LONG(status.runs); } /* {{{ proto void xdebug_get_gc_total_collected_roots() Return total number of collected root variables during garbage collection. */ PHP_FUNCTION(xdebug_get_gc_total_collected_roots) { zend_gc_status status; zend_gc_get_status(&status); RETURN_LONG(status.collected); } /* {{{ helpers */ void xdebug_gcstats_init_if_requested(zend_op_array* op_array) { RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_GCSTATS); if (!xdebug_lib_start_with_request(XDEBUG_MODE_GCSTATS)) { return; } if (XG_GCSTATS(active)) { return; } if (xdebug_gc_stats_init(NULL, op_array->filename) == SUCCESS) { XG_GCSTATS(active) = 1; } } /* }}} */ /* {{{ initialisation */ void xdebug_init_gc_stats_globals(xdebug_gc_stats_globals_t *xg) { xg->file = NULL; xg->filename = NULL; xg->active = 0; } void xdebug_gcstats_minit() { /* Replace garbage collection handler with our own */ xdebug_old_gc_collect_cycles = gc_collect_cycles; gc_collect_cycles = xdebug_gc_collect_cycles; } void xdebug_gcstats_mshutdown() { gc_collect_cycles = xdebug_old_gc_collect_cycles; } void xdebug_gcstats_rinit() { xdebug_init_gc_stats_globals(&XG(globals).gc_stats); } void xdebug_gcstats_rshutdown() { if (XG_GCSTATS(active)) { xdebug_gc_stats_stop(); } if (XG_GCSTATS(filename)) { xdfree(XG_GCSTATS(filename)); } } /* }}} */ xdebug-3.4.3/src/gcstats/gc_stats.h0000664000175000017500000000305015011062311016470 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_GC_STATS_H__ #define __XDEBUG_GC_STATS_H__ typedef struct _xdebug_gc_stats_settings_t { char *output_name; } xdebug_gc_stats_settings_t; typedef struct _xdebug_gc_stats_globals_t { /* garbage stats */ zend_bool active; FILE *file; char *filename; } xdebug_gc_stats_globals_t; void xdebug_gcstats_init_if_requested(zend_op_array* op_array); void xdebug_init_gc_stats_globals(xdebug_gc_stats_globals_t *xg); void xdebug_gcstats_minit(); void xdebug_gcstats_mshutdown(); void xdebug_gcstats_rinit(); void xdebug_gcstats_rshutdown(); #endif xdebug-3.4.3/src/gcstats/gc_stats_private.h0000664000175000017500000000260615011062311020230 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_GC_STATS_PRIVATE_H__ #define __XDEBUG_GC_STATS_PRIVATE_H__ #include "gc_stats.h" typedef struct _xdebug_gc_run { zend_long collected; zend_long duration; zend_long memory_before; zend_long memory_after; zend_string *function_name; zend_string *class_name; } xdebug_gc_run; #define XINI_GCSTATS(v) (XG(settings.gc_stats.v)) #define XG_GCSTATS(v) (XG(globals.gc_stats.v)) #endif xdebug-3.4.3/src/profiler/profiler.c0000664000175000017500000004004615011062311016656 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifdef PHP_WIN32 #include #endif #include "lib/php-header.h" #include "TSRM.h" #include "php_globals.h" #include "Zend/zend_alloc.h" #include "Zend/zend_exceptions.h" #include "php_xdebug.h" #include "profiler.h" #include "profiler_private.h" #include "lib/log.h" #include "lib/mm.h" #include "lib/str.h" #include "lib/var.h" #include "lib/usefulstuff.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) int xdebug_profiler_exit_handler(XDEBUG_OPCODE_HANDLER_ARGS); void xdebug_init_profiler_globals(xdebug_profiler_globals_t *xg) { xg->active = 0; } void xdebug_profiler_minit(void) { #if PHP_VERSION_ID < 80400 /* Overload the "exit" opcode */ xdebug_set_opcode_handler(ZEND_EXIT, xdebug_profiler_exit_handler); #endif } void xdebug_profiler_mshutdown(void) { } void xdebug_profiler_rinit(void) { xdebug_file_init(&XG_PROF(profile_file)); XG_PROF(profile_filename_refs) = NULL; XG_PROF(profile_functionname_refs) = NULL; XG_PROF(profile_last_filename_ref) = 0; XG_PROF(php_internal_seen_before) = 0; XG_PROF(profile_last_functionname_ref) = 0; XG_PROF(active) = 0; } static void deinit_if_active(void) { if (!XG_PROF(active)) { return; } xdebug_profiler_deinit(); } void xdebug_profiler_post_deactivate(void) { deinit_if_active(); } void xdebug_profiler_pcntl_exec_handler(void) { deinit_if_active(); } void xdebug_profiler_exit_function_handler(void) { function_stack_entry *fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); deinit_if_active(); xdebug_profiler_free_function_details(fse); } int xdebug_profiler_exit_handler(XDEBUG_OPCODE_HANDLER_ARGS) { const zend_op *cur_opcode = execute_data->opline; deinit_if_active(); return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } void xdebug_profiler_init_if_requested(zend_op_array *op_array) { if (XG_PROF(active)) { return; } if (EG(flags) & EG_FLAGS_IN_SHUTDOWN) { return; } if (xdebug_lib_start_with_request(XDEBUG_MODE_PROFILING) || xdebug_lib_start_with_trigger(XDEBUG_MODE_PROFILING, NULL)) { xdebug_profiler_init((char*) STR_NAME_VAL(op_array->filename)); } } void xdebug_profiler_execute_ex(function_stack_entry *fse, zend_op_array *op_array) { if (!XG_PROF(active)) { return; } /* Calculate all elements for profile entries */ xdebug_profiler_add_function_details_user(fse, op_array); xdebug_profiler_function_begin(fse); } void xdebug_profiler_execute_ex_end(function_stack_entry *fse) { xdebug_profiler_function_end(fse); xdebug_profiler_free_function_details(fse); } void xdebug_profiler_execute_internal(function_stack_entry *fse) { if (!XG_PROF(active)) { return; } xdebug_profiler_add_function_details_internal(fse); xdebug_profiler_function_begin(fse); } void xdebug_profiler_execute_internal_end(function_stack_entry *fse) { if (!XG_PROF(active)) { return; } xdebug_profiler_function_end(fse); xdebug_profiler_free_function_details(fse); } void xdebug_profile_call_entry_dtor(void *dummy, void *elem) { xdebug_call_entry *ce = elem; if (ce->function) { zend_string_release(ce->function); } if (ce->filename) { zend_string_release(ce->filename); } xdfree(ce); } static void profiler_write_header(xdebug_file *file, char *script_name) { if (XINI_PROF(profiler_append)) { xdebug_file_printf(file, "\n==== NEW PROFILING FILE ==============================================\n"); } xdebug_file_printf(file, "version: 1\ncreator: xdebug %s (PHP %s)\n", XDEBUG_VERSION, XG_BASE(php_version_run_time)); xdebug_file_printf(file, "cmd: %s\npart: 1\npositions: line\n\n", script_name); xdebug_file_printf(file, "events: Time_(10ns) Memory_(bytes)\n\n"); xdebug_file_flush(file); } #define NANOTIME_SCALE_10NS(nanotime) ((unsigned long)(((nanotime) + 5) / 10)) void xdebug_profiler_init(char *script_name) { char *filename = NULL, *fname = NULL; char *output_dir = NULL; if (XG_PROF(active)) { return; } if (!strlen(XINI_PROF(profiler_output_name)) || xdebug_format_output_filename(&fname, XINI_PROF(profiler_output_name), script_name) <= 0 ) { /* Invalid or empty xdebug.profiler_output_name */ return; } /* Add a slash if none is present in the output_dir setting */ output_dir = xdebug_lib_get_output_dir(); /* not duplicated */ if (IS_SLASH(output_dir[strlen(output_dir) - 1])) { filename = xdebug_sprintf("%s%s", output_dir, fname); } else { filename = xdebug_sprintf("%s%c%s", output_dir, DEFAULT_SLASH, fname); } if (!xdebug_file_open(&XG_PROF(profile_file), filename, NULL, XINI_PROF(profiler_append) ? "ab" : "wb")) { xdebug_log_diagnose_permissions(XLOG_CHAN_PROFILE, output_dir, fname); goto return_and_free_names; } profiler_write_header(&XG_PROF(profile_file), script_name); if (!SG(headers_sent)) { sapi_header_line ctr = {0}; ctr.line = xdebug_sprintf("X-Xdebug-Profile-Filename: %s", XG_PROF(profile_file).name); ctr.line_len = strlen(ctr.line); sapi_header_op(SAPI_HEADER_REPLACE, &ctr); xdfree((void*) ctr.line); } XG_PROF(profiler_start_nanotime) = xdebug_get_nanotime(); XG_PROF(active) = 1; XG_PROF(profile_filename_refs) = xdebug_hash_alloc(128, xdfree); XG_PROF(profile_functionname_refs) = xdebug_hash_alloc(128, xdfree); XG_PROF(profile_last_filename_ref) = 1; XG_PROF(profile_last_functionname_ref) = 0; return_and_free_names: xdfree(filename); xdfree(fname); } void xdebug_profiler_deinit() { function_stack_entry *fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); int i; for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)); i++, fse--) { xdebug_profiler_function_end(fse); } xdebug_file_printf( &XG_PROF(profile_file), "summary: %lu %zd\n\n", NANOTIME_SCALE_10NS(xdebug_get_nanotime() - XG_PROF(profiler_start_nanotime)), zend_memory_peak_usage(0) ); XG_PROF(active) = 0; xdebug_file_flush(&XG_PROF(profile_file)); if (XG_PROF(profile_file).type != XDEBUG_FILE_TYPE_NULL) { xdebug_file_close(&XG_PROF(profile_file)); xdebug_file_deinit(&XG_PROF(profile_file)); } xdebug_hash_destroy(XG_PROF(profile_filename_refs)); xdebug_hash_destroy(XG_PROF(profile_functionname_refs)); XG_PROF(profile_filename_refs) = NULL; XG_PROF(profile_functionname_refs) = NULL; } static inline void xdebug_profiler_function_push(function_stack_entry *fse) { fse->profile.nanotime += (xdebug_get_nanotime() - fse->profile.nanotime_mark); fse->profile.nanotime_mark = 0; fse->profile.memory += (zend_memory_usage(0) - fse->profile.mem_mark); fse->profile.mem_mark = 0; } void xdebug_profiler_function_continue(function_stack_entry *fse) { fse->profile.nanotime_mark = xdebug_get_nanotime(); } void xdebug_profiler_function_pause(function_stack_entry *fse) { xdebug_profiler_function_push(fse); } static inline void add_filename_ref(xdebug_str *buffer, char *name) { char *ref; if (xdebug_hash_find(XG_PROF(profile_filename_refs), name, strlen(name), (void*) &ref)) { xdebug_str_add(buffer, ref, 0); } else { XG_PROF(profile_last_filename_ref)++; ref = xdebug_sprintf("(%d)", XG_PROF(profile_last_filename_ref)); xdebug_hash_add(XG_PROF(profile_filename_refs), name, strlen(name), (void*) ref); xdebug_str_add(buffer, ref, 0); xdebug_str_addc(buffer, ' '); xdebug_str_add(buffer, name, 0); } } static inline void add_functionname_ref(xdebug_str *buffer, char *name) { char *ref; if (xdebug_hash_find(XG_PROF(profile_functionname_refs), name, strlen(name), (void*) &ref)) { xdebug_str_add(buffer, ref, 0); } else { XG_PROF(profile_last_functionname_ref)++; ref = xdebug_sprintf("(%d)", XG_PROF(profile_last_functionname_ref)); xdebug_hash_add(XG_PROF(profile_functionname_refs), name, strlen(name), (void*) ref); xdebug_str_add(buffer, ref, 0); xdebug_str_addc(buffer, ' '); xdebug_str_add(buffer, name, 0); } } void xdebug_profiler_add_function_details_user(function_stack_entry *fse, zend_op_array *op_array) { char *tmp_fname, *tmp_name; tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT); switch (fse->function.type) { case XFUNC_INCLUDE: case XFUNC_INCLUDE_ONCE: case XFUNC_REQUIRE: case XFUNC_REQUIRE_ONCE: tmp_fname = xdebug_sprintf("%s::%s", tmp_name, ZSTR_VAL(fse->function.include_filename)); xdfree(tmp_name); tmp_name = tmp_fname; fse->profiler.lineno = 1; break; default: if (op_array/* && op_array->function_name*/) { fse->profiler.lineno = fse->op_array->line_start; } else { fse->profiler.lineno = fse->lineno; } break; } if (fse->profiler.lineno == 0) { fse->profiler.lineno = 1; } if (op_array && op_array->filename) { fse->profiler.filename = zend_string_copy(op_array->filename); } else { fse->profiler.filename = zend_string_copy(fse->filename); } fse->profiler.function = ZSTR_INIT_LITERAL(tmp_name, false); xdfree(tmp_name); } void xdebug_profiler_add_function_details_internal(function_stack_entry *fse) { char *tmp_fname, *tmp_name; tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT); switch (fse->function.type) { case XFUNC_INCLUDE: case XFUNC_INCLUDE_ONCE: case XFUNC_REQUIRE: case XFUNC_REQUIRE_ONCE: tmp_fname = xdebug_sprintf("%s::%s", tmp_name, fse->function.include_filename); xdfree(tmp_name); tmp_name = tmp_fname; fse->profiler.lineno = 1; break; default: fse->profiler.lineno = fse->lineno; break; } if (fse->profiler.lineno == 0) { fse->profiler.lineno = 1; } fse->profiler.filename = zend_string_copy(fse->filename); fse->profiler.function = ZSTR_INIT_LITERAL(tmp_name, false); xdfree(tmp_name); } void xdebug_profiler_function_begin(function_stack_entry *fse) { fse->profile.nanotime = 0; fse->profile.nanotime_mark = xdebug_get_nanotime(); fse->profile.memory = 0; fse->profile.mem_mark = zend_memory_usage(0); } #define TMP_KEY_BUFFER_LEN 1024 #define TMP_KEY_PREFIX "php::" #define TMP_KEY_PREFIX_LEN (sizeof(TMP_KEY_PREFIX)-1) #define TMP_KEY_MAX_LEN (TMP_KEY_BUFFER_LEN-TMP_KEY_PREFIX_LEN-1) void xdebug_profiler_function_end(function_stack_entry *fse) { xdebug_llist_element *le; xdebug_str file_buffer = XDEBUG_STR_INITIALIZER; char tmp_key[TMP_KEY_BUFFER_LEN]; if (!XG_PROF(active)) { return; } /* The temporary key always starts with 'php::' */ memcpy(tmp_key, TMP_KEY_PREFIX, TMP_KEY_PREFIX_LEN); if (xdebug_vector_element_is_valid(XG_BASE(stack), fse - 1) && !(fse - 1)->profile.call_list) { (fse - 1)->profile.call_list = xdebug_llist_alloc(xdebug_profile_call_entry_dtor); } if (!fse->profile.call_list) { fse->profile.call_list = xdebug_llist_alloc(xdebug_profile_call_entry_dtor); } xdebug_profiler_function_push(fse); if (xdebug_vector_element_is_valid(XG_BASE(stack), fse - 1)) { xdebug_call_entry *ce = xdmalloc(sizeof(xdebug_call_entry)); ce->filename = zend_string_copy(fse->profiler.filename); ce->function = zend_string_copy(fse->profiler.function); ce->nanotime_taken = fse->profile.nanotime; ce->lineno = fse->lineno; ce->user_defined = fse->user_defined; ce->mem_used = fse->profile.memory; xdebug_llist_insert_next((fse - 1)->profile.call_list, NULL, ce); } /* use previously created filename and funcname (or a reference to them) to show * time spend */ if (fse->user_defined == XDEBUG_BUILT_IN) { size_t tmp_key_funcname_len = ZSTR_LEN(fse->profiler.function); memcpy(tmp_key + TMP_KEY_PREFIX_LEN, ZSTR_VAL(fse->profiler.function), tmp_key_funcname_len > TMP_KEY_MAX_LEN ? TMP_KEY_MAX_LEN : tmp_key_funcname_len + 1 ); tmp_key[TMP_KEY_BUFFER_LEN - 1] = '\0'; if (XG_PROF(php_internal_seen_before)) { xdebug_str_add_literal(&file_buffer, "fl=(1)\n"); } else { xdebug_str_add_literal(&file_buffer, "fl=(1) php:internal\n"); XG_PROF(php_internal_seen_before) = 1; } xdebug_str_add_literal(&file_buffer, "fn="); add_functionname_ref(&file_buffer, tmp_key); xdebug_str_addc(&file_buffer, '\n'); } else { xdebug_str_add_literal(&file_buffer, "fl="); add_filename_ref(&file_buffer, ZSTR_VAL(fse->profiler.filename)); xdebug_str_add_literal(&file_buffer, "\nfn="); add_functionname_ref(&file_buffer, ZSTR_VAL(fse->profiler.function)); xdebug_str_addc(&file_buffer, '\n'); } /* Subtract time in calledfunction from time here */ for (le = XDEBUG_LLIST_HEAD(fse->profile.call_list); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { xdebug_call_entry *call_entry = XDEBUG_LLIST_VALP(le); fse->profile.nanotime -= call_entry->nanotime_taken; fse->profile.memory -= call_entry->mem_used; } /* Adds %d %lu %lu, with lineno, time, and memory */ xdebug_str_add_uint64(&file_buffer, fse->profiler.lineno); xdebug_str_addc(&file_buffer, ' '); xdebug_str_add_uint64(&file_buffer, NANOTIME_SCALE_10NS(fse->profile.nanotime)); xdebug_str_addc(&file_buffer, ' '); xdebug_str_add_uint64(&file_buffer, fse->profile.memory >= 0 ? fse->profile.memory : 0); xdebug_str_addc(&file_buffer, '\n'); /* dump call list */ for (le = XDEBUG_LLIST_HEAD(fse->profile.call_list); le != NULL; le = XDEBUG_LLIST_NEXT(le)) { xdebug_call_entry *call_entry = XDEBUG_LLIST_VALP(le); if (call_entry->user_defined == XDEBUG_BUILT_IN) { size_t tmp_key_funcname_len = ZSTR_LEN(call_entry->function); memcpy(tmp_key + TMP_KEY_PREFIX_LEN, ZSTR_VAL(call_entry->function), tmp_key_funcname_len > TMP_KEY_MAX_LEN ? TMP_KEY_MAX_LEN : tmp_key_funcname_len + 1 ); tmp_key[TMP_KEY_BUFFER_LEN - 1] = '\0'; if (XG_PROF(php_internal_seen_before)) { xdebug_str_add_literal(&file_buffer, "cfl=(1)\n"); } else { xdebug_str_add_literal(&file_buffer, "cfl=(1) php:internal\n"); XG_PROF(php_internal_seen_before) = 1; } xdebug_str_add_literal(&file_buffer, "cfn="); add_functionname_ref(&file_buffer, tmp_key); xdebug_str_addc(&file_buffer, '\n'); } else { xdebug_str_add_literal(&file_buffer, "cfl="); add_filename_ref(&file_buffer, ZSTR_VAL(call_entry->filename)); xdebug_str_add_literal(&file_buffer, "\ncfn="); add_functionname_ref(&file_buffer, ZSTR_VAL(call_entry->function)); xdebug_str_addc(&file_buffer, '\n'); } xdebug_str_add_literal(&file_buffer, "calls=1 0 0\n"); /* Adds %d %lu %lu, with lineno, time, and memory */ xdebug_str_add_uint64(&file_buffer, call_entry->lineno); xdebug_str_addc(&file_buffer, ' '); xdebug_str_add_uint64(&file_buffer, NANOTIME_SCALE_10NS(call_entry->nanotime_taken)); xdebug_str_addc(&file_buffer, ' '); xdebug_str_add_uint64(&file_buffer, call_entry->mem_used >= 0 ? call_entry->mem_used : 0); xdebug_str_addc(&file_buffer, '\n'); } xdebug_str_addc(&file_buffer, '\n'); xdebug_file_write(file_buffer.d, sizeof(char), file_buffer.l, &XG_PROF(profile_file)); xdebug_str_dtor(file_buffer); } void xdebug_profiler_free_function_details(function_stack_entry *fse) { if (fse->profiler.function) { zend_string_release(fse->profiler.function); fse->profiler.function = NULL; } if (fse->profiler.filename) { zend_string_release(fse->profiler.filename); fse->profiler.filename = NULL; } } /* Returns a *pointer* to the current profile filename, if active. NULL * otherwise. Calling function is responsible for duplicating immediately */ char *xdebug_get_profiler_filename() { if (!XG_PROF(active)) { return NULL; } return XG_PROF(profile_file).name; } PHP_FUNCTION(xdebug_get_profiler_filename) { char *filename = xdebug_get_profiler_filename(); if (!filename) { RETURN_FALSE; } RETURN_STRING(filename); } xdebug-3.4.3/src/profiler/profiler.h0000664000175000017500000000570215011062311016663 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2022 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_PROFILER_H__ #define __XDEBUG_PROFILER_H__ #include "lib/php-header.h" #include "TSRM.h" #include "lib/file.h" #include "lib/lib.h" #include "php_xdebug.h" typedef struct _xdebug_profiler_globals_t { zend_bool active; uint64_t profiler_start_nanotime; xdebug_file profile_file; xdebug_hash *profile_filename_refs; int profile_last_filename_ref; int php_internal_seen_before; xdebug_hash *profile_functionname_refs; int profile_last_functionname_ref; } xdebug_profiler_globals_t; typedef struct _xdebug_profiler_settings_t { char *profiler_output_name; /* "pid" or "crc32" */ zend_bool profiler_append; } xdebug_profiler_settings_t; void xdebug_init_profiler_globals(xdebug_profiler_globals_t *xg); void xdebug_profiler_minit(void); void xdebug_profiler_mshutdown(void); void xdebug_profiler_rinit(void); void xdebug_profiler_post_deactivate(void); void xdebug_profiler_pcntl_exec_handler(void); void xdebug_profiler_exit_function_handler(void); void xdebug_profiler_init_if_requested(zend_op_array *op_array); void xdebug_profiler_execute_ex(function_stack_entry *fse, zend_op_array *op_array); void xdebug_profiler_execute_ex_end(function_stack_entry *fse); void xdebug_profiler_execute_internal(function_stack_entry *fse); void xdebug_profiler_execute_internal_end(function_stack_entry *fse); void xdebug_profiler_init(char *script_name); void xdebug_profiler_deinit(); void xdebug_profiler_add_function_details_user(function_stack_entry *fse, zend_op_array *op_array); void xdebug_profiler_add_function_details_internal(function_stack_entry *fse); void xdebug_profiler_free_function_details(function_stack_entry *fse); void xdebug_profiler_function_begin(function_stack_entry *fse); void xdebug_profiler_function_end(function_stack_entry *fse); void xdebug_profile_call_entry_dtor(void *dummy, void *elem); char *xdebug_get_profiler_filename(void); PHP_FUNCTION(xdebug_get_profiler_filename); #endif xdebug-3.4.3/src/profiler/profiler_private.h0000664000175000017500000000264115011062311020414 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_PROFILER_PRIVATE_H__ #define __XDEBUG_PROFILER_PRIVATE_H__ typedef struct _xdebug_call_entry { int type; /* 0 = function call, 1 = line */ int user_defined; zend_string *filename; zend_string *function; int lineno; uint64_t nanotime_taken; long mem_used; } xdebug_call_entry; #define XG_PROF(v) (XG(globals.profiler.v)) #define XINI_PROF(v) (XG(settings.profiler.v)) #endif xdebug-3.4.3/src/tracing/tracing.c0000664000175000017500000007047215011062311016276 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "php_xdebug.h" #include "tracing_private.h" #include "trace_textual.h" #include "trace_flamegraph.h" #include "trace_computerized.h" #include "trace_html.h" #include "lib/compat.h" #include "lib/log.h" #include "lib/str.h" #include "lib/var_export_line.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) static xdebug_trace_handler_t *xdebug_select_trace_handler(int options) { xdebug_trace_handler_t *tmp; switch (XINI_TRACE(trace_format)) { case 0: tmp = &xdebug_trace_handler_textual; break; case 1: tmp = &xdebug_trace_handler_computerized; break; case 2: tmp = &xdebug_trace_handler_html; break; case 3: tmp = &xdebug_trace_handler_flamegraph_cost; break; case 4: tmp = &xdebug_trace_handler_flamegraph_mem; break; default: php_error(E_NOTICE, "A wrong value for xdebug.trace_format was selected (%d), defaulting to the textual format", (int) XINI_TRACE(trace_format)); tmp = &xdebug_trace_handler_textual; break; } /* Override handler based on options */ if (options & XDEBUG_TRACE_OPTION_FLAMEGRAPH_COST) { tmp = &xdebug_trace_handler_flamegraph_cost; } if (options & XDEBUG_TRACE_OPTION_FLAMEGRAPH_MEM) { tmp = &xdebug_trace_handler_flamegraph_mem; } if (options & XDEBUG_TRACE_OPTION_COMPUTERIZED) { tmp = &xdebug_trace_handler_computerized; } if (options & XDEBUG_TRACE_OPTION_HTML) { tmp = &xdebug_trace_handler_html; } if (!tmp->init || !tmp->deinit || !tmp->get_filename) { xdebug_log_ex(XLOG_CHAN_TRACE, XLOG_CRIT, "HNDLR", "Broken trace handler for format '%d', missing 'init', 'deinit', or 'get_filename' handler", options); } return tmp; } xdebug_file *xdebug_trace_open_file(char *requested_filename, zend_string *script_filename, long options) { xdebug_file *file = xdebug_file_ctor(); char *filename_to_use; char *generated_filename = NULL; char *output_dir = xdebug_lib_get_output_dir(); /* not duplicated */ if (requested_filename && strlen(requested_filename)) { filename_to_use = xdstrdup(requested_filename); } else { if (!strlen(XINI_TRACE(trace_output_name)) || xdebug_format_output_filename(&generated_filename, XINI_TRACE(trace_output_name), ZSTR_VAL(script_filename)) <= 0 ) { /* Invalid or empty xdebug.trace_output_name */ xdebug_file_dtor(file); return NULL; } /* Add a slash if none is present in the output_dir setting */ output_dir = xdebug_lib_get_output_dir(); /* not duplicated */ if (IS_SLASH(output_dir[strlen(output_dir) - 1])) { filename_to_use = xdebug_sprintf("%s%s", output_dir, generated_filename); } else { filename_to_use = xdebug_sprintf("%s%c%s", output_dir, DEFAULT_SLASH, generated_filename); } } if (!xdebug_file_open( file, filename_to_use, (options & XDEBUG_TRACE_OPTION_NAKED_FILENAME) ? NULL : "xt", (options & XDEBUG_TRACE_OPTION_APPEND) ? "ab" : "wb" )) { xdebug_log_diagnose_permissions(XLOG_CHAN_TRACE, output_dir, generated_filename); } if (generated_filename) { xdfree(generated_filename); } xdfree(filename_to_use); return file; } static char* xdebug_start_trace(char* fname, zend_string *script_filename, long options) { if (XG_TRACE(trace_context)) { return NULL; } XG_TRACE(trace_handler) = xdebug_select_trace_handler(options); if (!XG_TRACE(trace_handler)) { return NULL; } XG_TRACE(trace_context) = (void*) XG_TRACE(trace_handler)->init(fname, script_filename, options); if (!XG_TRACE(trace_context)) { return NULL; } if (XG_TRACE(trace_handler)->write_header) { XG_TRACE(trace_handler)->write_header(XG_TRACE(trace_context)); } return xdstrdup(XG_TRACE(trace_handler)->get_filename(XG_TRACE(trace_context))); } static void xdebug_stop_trace(void) { if (!XG_TRACE(trace_context)) { return; } if (XG_TRACE(trace_handler)->write_footer) { XG_TRACE(trace_handler)->write_footer(XG_TRACE(trace_context)); } XG_TRACE(trace_handler)->deinit(XG_TRACE(trace_context)); XG_TRACE(trace_context) = NULL; } char *xdebug_get_trace_filename(void) { if (!(XG_TRACE(trace_context) && XG_TRACE(trace_handler) && XG_TRACE(trace_handler)->get_filename)) { return NULL; } return XG_TRACE(trace_handler)->get_filename(XG_TRACE(trace_context)); } PHP_FUNCTION(xdebug_start_trace) { char *fname = NULL; size_t fname_len = 0; char *trace_fname; zend_long options = XINI_TRACE(trace_options); function_stack_entry *fse; WARN_AND_RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_TRACING); if (XG_TRACE(trace_context)) { php_error(E_NOTICE, "Function trace already started"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sl", &fname, &fname_len, &options) == FAILURE) { return; } fse = xdebug_get_stack_frame(0); if ((trace_fname = xdebug_start_trace(fname, fse->filename, options)) != NULL) { RETVAL_STRING(trace_fname); xdfree(trace_fname); return; } else { php_error(E_NOTICE, "Trace could not be started"); } RETURN_FALSE; } PHP_FUNCTION(xdebug_stop_trace) { WARN_AND_RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_TRACING); if (!XG_TRACE(trace_context)) { php_error(E_NOTICE, "Function trace was not started"); RETURN_FALSE; } RETVAL_STRING(XG_TRACE(trace_handler)->get_filename(XG_TRACE(trace_context))); xdebug_stop_trace(); } PHP_FUNCTION(xdebug_get_tracefile_name) { char *filename; WARN_AND_RETURN_IF_MODE_IS_NOT(XDEBUG_MODE_TRACING); filename = xdebug_get_trace_filename(); if (!filename) { RETURN_FALSE; } RETVAL_STRING(filename); } static const char *get_assign_operation(uint32_t extended_value) { switch (extended_value) { case ZEND_ADD: return "+="; case ZEND_SUB: return "-="; case ZEND_MUL: return "*="; case ZEND_DIV: return "/="; case ZEND_MOD: return "%="; case ZEND_SL: return "<<="; case ZEND_SR: return ">>="; case ZEND_CONCAT: return ".="; case ZEND_BW_OR: return "|="; case ZEND_BW_AND: return "&="; case ZEND_BW_XOR: return "^="; case ZEND_POW: return "**="; default: return ""; } } static int xdebug_is_static_call(const zend_op *first_opcode, const zend_op *cur_opcode, const zend_op *prev_opcode, const zend_op **found_opcode) { const zend_op *opcode_ptr; opcode_ptr = cur_opcode; if ( (opcode_ptr->opcode == ZEND_ASSIGN_STATIC_PROP) || (opcode_ptr->opcode == ZEND_ASSIGN_STATIC_PROP_REF) || (opcode_ptr->opcode == ZEND_PRE_INC_STATIC_PROP) || (opcode_ptr->opcode == ZEND_PRE_DEC_STATIC_PROP) || (opcode_ptr->opcode == ZEND_POST_INC_STATIC_PROP) || (opcode_ptr->opcode == ZEND_POST_DEC_STATIC_PROP) ) { *found_opcode = opcode_ptr; return 1; } while (!(opcode_ptr->opcode == ZEND_EXT_STMT) && !((opcode_ptr->opcode == ZEND_FETCH_STATIC_PROP_W) || (opcode_ptr->opcode == ZEND_FETCH_STATIC_PROP_RW))) { opcode_ptr = opcode_ptr - 1; if (opcode_ptr < first_opcode) { return 0; } } if ((opcode_ptr->opcode == ZEND_FETCH_STATIC_PROP_W) || (opcode_ptr->opcode == ZEND_FETCH_STATIC_PROP_RW)) { *found_opcode = opcode_ptr; return 1; } return 0; } static const zend_op *xdebug_find_referenced_opline(zend_execute_data *execute_data, const zend_op *cur_opcode, int op1_or_op2) { size_t variable_number; const zend_op *scan_opcode; int found; int op_type = (op1_or_op2 == 1) ? cur_opcode->op1_type : cur_opcode->op2_type; if (op_type != IS_VAR) { return NULL; } variable_number = (op1_or_op2 == 1) ? cur_opcode->op1.var : cur_opcode->op2.var; scan_opcode = cur_opcode; found = 0; /* Scroll up until we find a RES of IS_VAR with the right value */ do { scan_opcode--; if (scan_opcode->result_type == IS_VAR && scan_opcode->result.var == variable_number) { found = 1; } } while (!found); return scan_opcode; } static int is_fetch_op(const zend_op *op) { return ( op->opcode == ZEND_FETCH_DIM_W || op->opcode == ZEND_FETCH_DIM_RW || op->opcode == ZEND_FETCH_OBJ_W || op->opcode == ZEND_FETCH_OBJ_RW || op->opcode == ZEND_FETCH_W || op->opcode == ZEND_FETCH_RW ); } static char *xdebug_find_var_name(zend_execute_data *execute_data, const zend_op *cur_opcode, const zend_op *lower_bound) { const zend_op *next_opcode, *prev_opcode = NULL, *opcode_ptr; zval *dimval; zend_op_array *op_array = &execute_data->func->op_array; xdebug_str name = XDEBUG_STR_INITIALIZER; int gohungfound = 0, is_static = 0; xdebug_str *zval_value = NULL; xdebug_var_export_options *options; const zend_op *static_opcode_ptr = NULL; next_opcode = cur_opcode + 1; prev_opcode = cur_opcode - 1; if (cur_opcode->opcode == ZEND_QM_ASSIGN) { xdebug_str_addc(&name, '$'); xdebug_str_add(&name, zend_get_compiled_variable_name(op_array, cur_opcode->result.var)->val, 0); return name.d; } is_static = xdebug_is_static_call(op_array->opcodes, cur_opcode, prev_opcode, &static_opcode_ptr); options = xdebug_var_export_options_from_ini(); options->no_decoration = 1; if (cur_opcode->op1_type == IS_CV) { if (!lower_bound) { xdebug_str_addc(&name, '$'); xdebug_str_add(&name, zend_get_compiled_variable_name(op_array, cur_opcode->op1.var)->val, 0); } } else if (cur_opcode->op1_type == IS_VAR && cur_opcode->opcode == ZEND_ASSIGN && (prev_opcode->opcode == ZEND_FETCH_W || prev_opcode->opcode == ZEND_FETCH_RW)) { if (is_static) { xdebug_str_add_literal(&name, "self::"); } else { zval_value = xdebug_get_zval_value_line(xdebug_get_zval_with_opline(execute_data, prev_opcode, prev_opcode->op1_type, &prev_opcode->op1), 0, options); xdebug_str_addc(&name, '$'); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } } else if (is_static) { /* todo : see if you can change this and the previous cases around */ xdebug_str_add_literal(&name, "self::"); } if (cur_opcode->opcode >= ZEND_PRE_INC_OBJ && cur_opcode->opcode <= ZEND_POST_DEC_OBJ) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval(execute_data, cur_opcode->op2_type, &cur_opcode->op2), 0, options); xdebug_str_add_literal(&name, "$this->"); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } if (cur_opcode->opcode >= ZEND_PRE_INC_STATIC_PROP && cur_opcode->opcode <= ZEND_POST_DEC_STATIC_PROP) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval(execute_data, cur_opcode->op1_type, &cur_opcode->op1), 0, options); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } /* Scroll back to start of FETCHES */ /* FIXME: See whether we can do this unroll looping only once - in is_static() */ gohungfound = 0; if (!is_static) { if (cur_opcode == lower_bound) { gohungfound = 1; } opcode_ptr = prev_opcode; while ((opcode_ptr >= lower_bound) && is_fetch_op(opcode_ptr)) { opcode_ptr = opcode_ptr - 1; gohungfound = 1; } opcode_ptr = opcode_ptr + 1; } else { /* if we have a static method, we should already have found the first fetch */ opcode_ptr = static_opcode_ptr; gohungfound = 1; } if (gohungfound) { int cv_found = 0; do { if ( opcode_ptr->op1_type == IS_UNUSED && (opcode_ptr->opcode == ZEND_FETCH_OBJ_W || opcode_ptr->opcode == ZEND_FETCH_OBJ_RW) ) { xdebug_str_add_literal(&name, "$this"); } if (opcode_ptr->op1_type == IS_CV) { xdebug_str_addc(&name, '$'); xdebug_str_add(&name, zend_get_compiled_variable_name(op_array, opcode_ptr->op1.var)->val, 0); } if (opcode_ptr->opcode == ZEND_FETCH_STATIC_PROP_W || opcode_ptr->opcode == ZEND_FETCH_STATIC_PROP_R || opcode_ptr->opcode == ZEND_FETCH_STATIC_PROP_RW) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval_with_opline(execute_data, opcode_ptr, opcode_ptr->op1_type, &opcode_ptr->op1), 0, options); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } if (opcode_ptr->opcode == ZEND_FETCH_W) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval_with_opline(execute_data, opcode_ptr, opcode_ptr->op1_type, &opcode_ptr->op1), 0, options); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } if (is_static && opcode_ptr->opcode == ZEND_FETCH_RW) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval_with_opline(execute_data, opcode_ptr, opcode_ptr->op1_type, &opcode_ptr->op1), 0, options); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } if (opcode_ptr->opcode == ZEND_FETCH_DIM_W || opcode_ptr->opcode == ZEND_FETCH_DIM_RW) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval_with_opline(execute_data, opcode_ptr, opcode_ptr->op2_type, &opcode_ptr->op2), 0, NULL); xdebug_str_addc(&name, '['); if (zval_value) { xdebug_str_add_str(&name, zval_value); } xdebug_str_addc(&name, ']'); xdebug_str_free(zval_value); } else if (opcode_ptr->opcode == ZEND_FETCH_OBJ_W || opcode_ptr->opcode == ZEND_FETCH_OBJ_RW) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval_with_opline(execute_data, opcode_ptr, opcode_ptr->op2_type, &opcode_ptr->op2), 0, options); xdebug_str_add_literal(&name, "->"); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } opcode_ptr = opcode_ptr + 1; if (opcode_ptr->op1_type == IS_CV) { cv_found = 1; } } while (!cv_found && is_fetch_op(opcode_ptr)); } if ( (cur_opcode->opcode == ZEND_ASSIGN_OBJ) || (cur_opcode->opcode == ZEND_ASSIGN_OBJ_REF) ) { if (cur_opcode->op1_type == IS_UNUSED) { xdebug_str_add_literal(&name, "$this"); } dimval = xdebug_get_zval(execute_data, cur_opcode->op2_type, &cur_opcode->op2); xdebug_str_add_literal(&name, "->"); xdebug_str_add(&name, Z_STRVAL_P(dimval), 0); } if (cur_opcode->opcode == ZEND_ASSIGN_STATIC_PROP_REF) { dimval = xdebug_get_zval(execute_data, cur_opcode->op1_type, &cur_opcode->op1); xdebug_str_add(&name, Z_STRVAL_P(dimval), 0); } if (cur_opcode->opcode == ZEND_ASSIGN_DIM_OP) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval(execute_data, cur_opcode->op2_type, &cur_opcode->op2), 0, NULL); xdebug_str_addc(&name, '['); xdebug_str_add_str(&name, zval_value); xdebug_str_addc(&name, ']'); xdebug_str_free(zval_value); } if (cur_opcode->opcode == ZEND_ASSIGN_OBJ_OP) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval(execute_data, cur_opcode->op2_type, &cur_opcode->op2), 0, options); if (cur_opcode->op1_type == IS_UNUSED) { xdebug_str_add_literal(&name, "$this->"); } else { xdebug_str_add_literal(&name, "->"); } xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } if (cur_opcode->opcode == ZEND_ASSIGN_STATIC_PROP_OP) { zval_value = xdebug_get_zval_value_line(xdebug_get_zval(execute_data, cur_opcode->op1_type, &cur_opcode->op1), 0, options); xdebug_str_add_literal(&name, "self::"); xdebug_str_add_str(&name, zval_value); xdebug_str_free(zval_value); } if (cur_opcode->opcode == ZEND_ASSIGN_DIM) { if (next_opcode->opcode == ZEND_OP_DATA && cur_opcode->op2_type == IS_UNUSED) { xdebug_str_add_literal(&name, "[]"); } else { zval_value = xdebug_get_zval_value_line(xdebug_get_zval_with_opline(execute_data, opcode_ptr, opcode_ptr->op2_type, &opcode_ptr->op2), 0, NULL); xdebug_str_addc(&name, '['); xdebug_str_add_str(&name, zval_value); xdebug_str_addc(&name, ']'); xdebug_str_free(zval_value); } } if (cur_opcode->opcode == ZEND_ASSIGN_STATIC_PROP) { dimval = xdebug_get_zval(execute_data, cur_opcode->op1_type, &cur_opcode->op1); xdebug_str_add(&name, Z_STRVAL_P(dimval), 0); } xdfree(options->runtime); xdfree(options); return name.d; } static int xdebug_common_assign_dim_handler(const char *op, XDEBUG_OPCODE_HANDLER_ARGS) { char *file; zend_op_array *op_array = &execute_data->func->op_array; int lineno; const zend_op *cur_opcode, *next_opcode; zval *val = NULL; char *right_full_varname = NULL; function_stack_entry *fse; cur_opcode = execute_data->opline; next_opcode = cur_opcode + 1; file = (char*) STR_NAME_VAL(op_array->filename); lineno = cur_opcode->lineno; /* TODO TEST FOR ASSIGNMENTS IN FILTERING */ // if (xdebug_is_top_stack_frame_filtered(XDEBUG_FILTER_CODE_COVERAGE)) { // return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); // } if (XG_TRACE(trace_context) && XINI_TRACE(collect_assignments)) { char *full_varname; if (cur_opcode->opcode == ZEND_QM_ASSIGN && cur_opcode->result_type != IS_CV) { return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } full_varname = xdebug_find_var_name(execute_data, execute_data->opline, NULL); if (cur_opcode->opcode >= ZEND_PRE_INC && cur_opcode->opcode <= ZEND_POST_DEC) { char *tmp_varname; switch (cur_opcode->opcode) { case ZEND_PRE_INC: tmp_varname = xdebug_sprintf("++%s", full_varname); break; case ZEND_POST_INC: tmp_varname = xdebug_sprintf("%s++", full_varname); break; case ZEND_PRE_DEC: tmp_varname = xdebug_sprintf("--%s", full_varname); break; case ZEND_POST_DEC: tmp_varname = xdebug_sprintf("%s--", full_varname); break; } xdfree(full_varname); full_varname = tmp_varname; val = xdebug_get_zval(execute_data, cur_opcode->op1_type, &cur_opcode->op1); } else if (cur_opcode->opcode >= ZEND_PRE_INC_OBJ && cur_opcode->opcode <= ZEND_POST_DEC_OBJ) { char *tmp_varname; switch (cur_opcode->opcode) { case ZEND_PRE_INC_OBJ: tmp_varname = xdebug_sprintf("++%s", full_varname); break; case ZEND_POST_INC_OBJ: tmp_varname = xdebug_sprintf("%s++", full_varname); break; case ZEND_PRE_DEC_OBJ: tmp_varname = xdebug_sprintf("--%s", full_varname); break; case ZEND_POST_DEC_OBJ: tmp_varname = xdebug_sprintf("%s--", full_varname); break; } xdfree(full_varname); full_varname = tmp_varname; val = xdebug_get_zval(execute_data, cur_opcode->op2_type, &cur_opcode->op2); } else if (cur_opcode->opcode >= ZEND_PRE_INC_STATIC_PROP && cur_opcode->opcode <= ZEND_POST_DEC_STATIC_PROP) { char *tmp_varname; switch (cur_opcode->opcode) { case ZEND_PRE_INC_STATIC_PROP: tmp_varname = xdebug_sprintf("++%s", full_varname); break; case ZEND_POST_INC_STATIC_PROP: tmp_varname = xdebug_sprintf("%s++", full_varname); break; case ZEND_PRE_DEC_STATIC_PROP: tmp_varname = xdebug_sprintf("--%s", full_varname); break; case ZEND_POST_DEC_STATIC_PROP: tmp_varname = xdebug_sprintf("%s--", full_varname); break; } xdfree(full_varname); full_varname = tmp_varname; val = xdebug_get_zval(execute_data, cur_opcode->op2_type, &cur_opcode->op2); } else if ( (next_opcode->opcode == ZEND_OP_DATA) && (cur_opcode->opcode != ZEND_ASSIGN_OBJ_REF) && (cur_opcode->opcode != ZEND_ASSIGN_STATIC_PROP_REF) ) { val = xdebug_get_zval_with_opline(execute_data, next_opcode, next_opcode->op1_type, &next_opcode->op1); } else if (cur_opcode->opcode == ZEND_QM_ASSIGN) { val = xdebug_get_zval(execute_data, cur_opcode->op1_type, &cur_opcode->op1); } else if (cur_opcode->opcode == ZEND_ASSIGN_REF) { if (cur_opcode->op2_type == IS_CV) { right_full_varname = xdebug_sprintf("$%s", zend_get_compiled_variable_name(op_array, cur_opcode->op2.var)->val); } else { const zend_op *referenced_opline = xdebug_find_referenced_opline(execute_data, cur_opcode, 2); right_full_varname = xdebug_find_var_name(execute_data, referenced_opline, NULL); } } else if (cur_opcode->opcode == ZEND_ASSIGN_OBJ_REF) { if (next_opcode->op1_type == IS_CV) { right_full_varname = xdebug_sprintf("$%s", zend_get_compiled_variable_name(op_array, next_opcode->op1.var)->val); } else { const zend_op *referenced_opline = xdebug_find_referenced_opline(execute_data, next_opcode, 1); right_full_varname = xdebug_find_var_name(execute_data, referenced_opline, NULL); } } else if (cur_opcode->opcode == ZEND_ASSIGN_STATIC_PROP_REF) { if (next_opcode->op1_type == IS_CV) { right_full_varname = xdebug_sprintf("$%s", zend_get_compiled_variable_name(op_array, next_opcode->op1.var)->val); } else { const zend_op *referenced_opline = xdebug_find_referenced_opline(execute_data, next_opcode, 1); right_full_varname = xdebug_find_var_name(execute_data, referenced_opline, NULL); } } else { val = xdebug_get_zval(execute_data, cur_opcode->op2_type, &cur_opcode->op2); } fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack)); if (XG_TRACE(trace_context) && XINI_TRACE(collect_assignments) && XG_TRACE(trace_handler)->assignment) { XG_TRACE(trace_handler)->assignment(XG_TRACE(trace_context), fse, full_varname, val, right_full_varname, op, file, lineno); } xdfree(full_varname); xdfree(right_full_varname); } return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU); } XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign, "="); XDEBUG_OPCODE_OVERRIDE_ASSIGN(qm_assign, "="); XDEBUG_OPCODE_OVERRIDE_ASSIGN_OP(assign_op); XDEBUG_OPCODE_OVERRIDE_ASSIGN_OP(assign_dim_op); XDEBUG_OPCODE_OVERRIDE_ASSIGN_OP(assign_obj_op); XDEBUG_OPCODE_OVERRIDE_ASSIGN_OP(assign_static_prop_op); XDEBUG_OPCODE_OVERRIDE_ASSIGN(pre_inc, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(post_inc, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(pre_dec, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(post_dec, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(pre_inc_obj, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(post_inc_obj, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(pre_dec_obj, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(post_dec_obj, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign_concat, ".="); XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign_dim, "="); XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign_obj, "="); XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign_ref, "=&"); XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign_obj_ref, "=&"); XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign_static_prop, "="); XDEBUG_OPCODE_OVERRIDE_ASSIGN(assign_static_prop_ref, "=&"); XDEBUG_OPCODE_OVERRIDE_ASSIGN(pre_inc_static_prop, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(pre_dec_static_prop, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(post_inc_static_prop, ""); XDEBUG_OPCODE_OVERRIDE_ASSIGN(post_dec_static_prop, ""); void xdebug_init_tracing_globals(xdebug_tracing_globals_t *xg) { xg->trace_handler = NULL; xg->trace_context = NULL; } void xdebug_tracing_minit(INIT_FUNC_ARGS) { /* Override opcodes for variable assignments in traces */ xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN, xdebug_assign_handler); xdebug_register_with_opcode_multi_handler(ZEND_QM_ASSIGN, xdebug_qm_assign_handler); xdebug_set_opcode_handler(ZEND_ASSIGN_OP, xdebug_assign_op_handler); xdebug_set_opcode_handler(ZEND_ASSIGN_DIM_OP, xdebug_assign_dim_op_handler); xdebug_set_opcode_handler(ZEND_ASSIGN_OBJ_OP, xdebug_assign_obj_op_handler); xdebug_set_opcode_handler(ZEND_ASSIGN_STATIC_PROP_OP, xdebug_assign_static_prop_op_handler); xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_DIM, xdebug_assign_dim_handler); xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_OBJ, xdebug_assign_obj_handler); xdebug_set_opcode_handler(ZEND_ASSIGN_REF, xdebug_assign_ref_handler); xdebug_set_opcode_handler(ZEND_PRE_INC, xdebug_pre_inc_handler); xdebug_set_opcode_handler(ZEND_POST_INC, xdebug_post_inc_handler); xdebug_set_opcode_handler(ZEND_PRE_DEC, xdebug_pre_dec_handler); xdebug_set_opcode_handler(ZEND_POST_DEC, xdebug_post_dec_handler); xdebug_set_opcode_handler(ZEND_PRE_INC_OBJ, xdebug_pre_inc_obj_handler); xdebug_set_opcode_handler(ZEND_POST_INC_OBJ, xdebug_post_inc_obj_handler); xdebug_set_opcode_handler(ZEND_PRE_DEC_OBJ, xdebug_pre_dec_obj_handler); xdebug_set_opcode_handler(ZEND_POST_DEC_OBJ, xdebug_post_dec_obj_handler); xdebug_set_opcode_handler(ZEND_ASSIGN_OBJ_REF, xdebug_assign_obj_ref_handler); xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_STATIC_PROP, xdebug_assign_static_prop_handler); xdebug_set_opcode_handler(ZEND_ASSIGN_STATIC_PROP_REF, xdebug_assign_static_prop_ref_handler); xdebug_set_opcode_handler(ZEND_PRE_INC_STATIC_PROP, xdebug_pre_inc_static_prop_handler); xdebug_set_opcode_handler(ZEND_PRE_DEC_STATIC_PROP, xdebug_pre_dec_static_prop_handler); xdebug_set_opcode_handler(ZEND_POST_INC_STATIC_PROP, xdebug_post_inc_static_prop_handler); xdebug_set_opcode_handler(ZEND_POST_DEC_STATIC_PROP, xdebug_post_dec_static_prop_handler); } void xdebug_tracing_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("XDEBUG_TRACE_APPEND", XDEBUG_TRACE_OPTION_APPEND, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_TRACE_COMPUTERIZED", XDEBUG_TRACE_OPTION_COMPUTERIZED, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_TRACE_FLAMEGRAPH_COST", XDEBUG_TRACE_OPTION_FLAMEGRAPH_COST, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_TRACE_FLAMEGRAPH_MEM", XDEBUG_TRACE_OPTION_FLAMEGRAPH_MEM, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_TRACE_HTML", XDEBUG_TRACE_OPTION_HTML, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("XDEBUG_TRACE_NAKED_FILENAME", XDEBUG_TRACE_OPTION_NAKED_FILENAME, CONST_CS | CONST_PERSISTENT); } void xdebug_tracing_rinit(void) { XG_TRACE(trace_handler) = NULL; XG_TRACE(trace_context) = NULL; xdebug_disable_opcache_optimizer(); } void xdebug_tracing_post_deactivate(void) { if (XG_TRACE(trace_context)) { xdebug_stop_trace(); } XG_TRACE(trace_context) = NULL; } void xdebug_tracing_init_if_requested(zend_op_array *op_array) { if (xdebug_lib_start_with_request(XDEBUG_MODE_TRACING) || xdebug_lib_start_with_trigger(XDEBUG_MODE_TRACING, NULL)) { /* In case we do an auto-trace we are not interested in the return * value, but we still have to free it. */ xdfree(xdebug_start_trace(NULL, op_array->filename, XINI_TRACE(trace_options))); } } void xdebug_tracing_execute_ex(function_stack_entry *fse) { if (fse->filtered_tracing || !XG_TRACE(trace_context)) { return; } if (XG_TRACE(trace_handler)->function_entry) { XG_TRACE(trace_handler)->function_entry(XG_TRACE(trace_context), fse); } } void xdebug_tracing_execute_ex_end(function_stack_entry *fse, zend_execute_data *execute_data, zval *return_value) { zend_op_array *op_array; if (fse->filtered_tracing || !XG_TRACE(trace_context)) { return; } if ((XG_TRACE(trace_handler)->function_exit)) { XG_TRACE(trace_handler)->function_exit(XG_TRACE(trace_context), fse); } /* Store return value in the trace file */ if (!XINI_TRACE(collect_return)) { return; } op_array = &(execute_data->func->op_array); if (!execute_data || !execute_data->return_value) { return; } if (op_array->fn_flags & ZEND_ACC_GENERATOR) { if (XG_TRACE(trace_handler)->generator_return_value) { XG_TRACE(trace_handler)->generator_return_value(XG_TRACE(trace_context), fse, (zend_generator*) execute_data->return_value); } } else { if (XG_TRACE(trace_handler)->return_value) { XG_TRACE(trace_handler)->return_value(XG_TRACE(trace_context), fse, return_value); } } } int xdebug_tracing_execute_internal(function_stack_entry *fse) { if (fse->filtered_tracing || !XG_TRACE(trace_context)) { return 0; } if (fse->function.type != XFUNC_ZEND_PASS && (XG_TRACE(trace_handler)->function_entry)) { XG_TRACE(trace_handler)->function_entry(XG_TRACE(trace_context), fse); return 1; } return 0; } void xdebug_tracing_execute_internal_end(function_stack_entry *fse, zval *return_value) { if (fse->filtered_tracing || !XG_TRACE(trace_context)) { return; } if (fse->function.type != XFUNC_ZEND_PASS && (XG_TRACE(trace_handler)->function_exit)) { XG_TRACE(trace_handler)->function_exit(XG_TRACE(trace_context), fse); } /* Store return value in the trace file */ if (XINI_TRACE(collect_return) && fse->function.type != XFUNC_ZEND_PASS && return_value && XG_TRACE(trace_handler)->return_value) { XG_TRACE(trace_handler)->return_value(XG_TRACE(trace_context), fse, return_value); } } void xdebug_tracing_save_trace_context(void **original_trace_context) { *original_trace_context = XG_TRACE(trace_context); XG_TRACE(trace_context) = NULL; } void xdebug_tracing_restore_trace_context(void *original_trace_context) { XG_TRACE(trace_context) = original_trace_context; } xdebug-3.4.3/src/tracing/tracing.h0000664000175000017500000000703415011062311016275 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef XDEBUG_TRACING_H #define XDEBUG_TRACING_H #include "lib/php-header.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" #include "zend_generators.h" #pragma GCC diagnostic pop typedef struct { void *(*init)(char *fname, zend_string *script_filename, long options); void (*deinit)(void *ctxt); void (*write_header)(void *ctxt); void (*write_footer)(void *ctxt); char *(*get_filename)(void *ctxt); void (*function_entry)(void *ctxt, function_stack_entry *fse); void (*function_exit)(void *ctxt, function_stack_entry *fse); void (*return_value)(void *ctxt, function_stack_entry *fse, zval *return_value); void (*generator_return_value)(void *ctxt, function_stack_entry *fse, zend_generator *generator); void (*assignment)(void *ctxt, function_stack_entry *fse, char *full_varname, zval *value, char *right_full_varname, const char *op, char *file, int lineno); } xdebug_trace_handler_t; typedef struct _xdebug_tracing_globals_t { xdebug_trace_handler_t *trace_handler; void *trace_context; } xdebug_tracing_globals_t; typedef struct _xdebug_tracing_settings_t { char *trace_output_name; zend_long trace_options; zend_long trace_format; zend_bool collect_assignments; zend_bool collect_params; zend_bool collect_return; } xdebug_tracing_settings_t; void xdebug_init_tracing_globals(xdebug_tracing_globals_t *xg); void xdebug_tracing_minit(INIT_FUNC_ARGS); void xdebug_tracing_rinit(void); void xdebug_tracing_post_deactivate(void); void xdebug_tracing_register_constants(INIT_FUNC_ARGS); void xdebug_tracing_init_if_requested(zend_op_array *op_array); void xdebug_tracing_execute_ex(function_stack_entry *fse); void xdebug_tracing_execute_ex_end(function_stack_entry *fse, zend_execute_data *execute_data, zval *return_value); int xdebug_tracing_execute_internal(function_stack_entry *fse); void xdebug_tracing_execute_internal_end(function_stack_entry *fse, zval *return_value); void xdebug_tracing_save_trace_context(void **old_trace_context); void xdebug_tracing_restore_trace_context(void *old_trace_context); char* xdebug_return_trace_stack_retval(function_stack_entry* i, int fnr, zval* retval); char* xdebug_return_trace_stack_generator_retval(function_stack_entry* i, zend_generator* generator); char* xdebug_return_trace_assignment(function_stack_entry *i, char *varname, zval *retval, char *op, char *file, int fileno); void xdebug_trace_function_begin(function_stack_entry *fse); void xdebug_trace_function_end(function_stack_entry *fse); char *xdebug_get_trace_filename(void); #endif xdebug-3.4.3/src/tracing/tracing_private.h0000664000175000017500000000651215011062311020027 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2020 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef __XDEBUG_TRACING_PRIVATE_H__ #define __XDEBUG_TRACING_PRIVATE_H__ #include "tracing.h" #define XG_TRACE(v) (XG(globals.tracing.v)) #define XINI_TRACE(v) (XG(settings.tracing.v)) #define XDEBUG_OPCODE_OVERRIDE_ASSIGN(f,o) \ int xdebug_##f##_handler(zend_execute_data *execute_data) \ { \ return xdebug_common_assign_dim_handler((o), execute_data); \ } #define XDEBUG_OPCODE_OVERRIDE_ASSIGN_OP(f) \ int xdebug_##f##_handler(zend_execute_data *execute_data) \ { \ const char *op = get_assign_operation(execute_data->opline->extended_value); \ return xdebug_common_assign_dim_handler(op, execute_data); \ } int xdebug_assign_handler(zend_execute_data *execute_data); int xdebug_qm_assign_handler(zend_execute_data *execute_data); int xdebug_assign_op_handler(zend_execute_data *execute_data); int xdebug_assign_dim_op_handler(zend_execute_data *execute_data); int xdebug_assign_obj_op_handler(zend_execute_data *execute_data); int xdebug_assign_static_prop_op_handler(zend_execute_data *execute_data); int xdebug_pre_inc_handler(zend_execute_data *execute_data); int xdebug_post_inc_handler(zend_execute_data *execute_data); int xdebug_pre_dec_handler(zend_execute_data *execute_data); int xdebug_post_dec_handler(zend_execute_data *execute_data); int xdebug_pre_inc_obj_handler(zend_execute_data *execute_data); int xdebug_post_inc_obj_handler(zend_execute_data *execute_data); int xdebug_pre_dec_obj_handler(zend_execute_data *execute_data); int xdebug_post_dec_obj_handler(zend_execute_data *execute_data); int xdebug_assign_concat_handler(zend_execute_data *execute_data); int xdebug_assign_dim_handler(zend_execute_data *execute_data); int xdebug_assign_obj_handler(zend_execute_data *execute_data); int xdebug_assign_ref_handler(zend_execute_data *execute_data); int xdebug_assign_obj_ref_handler(zend_execute_data *execute_data); int xdebug_assign_static_prop_handler(zend_execute_data *execute_data); int xdebug_assign_static_prop_ref_handler(zend_execute_data *execute_data); int xdebug_pre_inc_static_prop_handler(zend_execute_data *execute_data); int xdebug_pre_dec_static_prop_handler(zend_execute_data *execute_data); int xdebug_post_inc_static_prop_handler(zend_execute_data *execute_data); int xdebug_post_dec_static_prop_handler(zend_execute_data *execute_data); xdebug_file *xdebug_trace_open_file(char *fname, zend_string *script_filename, long options); #endif xdebug-3.4.3/src/tracing/trace_computerized.c0000664000175000017500000002115315011062311020527 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "php_xdebug.h" #include "tracing_private.h" #include "trace_computerized.h" #include "lib/lib_private.h" #include "lib/var_export_line.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); void *xdebug_trace_computerized_init(char *fname, zend_string *script_filename, long options) { xdebug_trace_computerized_context *tmp_computerized_context; tmp_computerized_context = xdmalloc(sizeof(xdebug_trace_computerized_context)); tmp_computerized_context->trace_file = xdebug_trace_open_file(fname, script_filename, options); if (!tmp_computerized_context->trace_file) { xdfree(tmp_computerized_context); return NULL; } return tmp_computerized_context; } void xdebug_trace_computerized_deinit(void *ctxt) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; xdebug_file_close(context->trace_file); xdebug_file_dtor(context->trace_file); context->trace_file = NULL; xdfree(context); } void xdebug_trace_computerized_write_header(void *ctxt) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; char *str_time; xdebug_file_printf(context->trace_file, "Version: %s\n", XDEBUG_VERSION); xdebug_file_printf(context->trace_file, "File format: 4\n"); str_time = xdebug_nanotime_to_chars(xdebug_get_nanotime(), 6); xdebug_file_printf(context->trace_file, "TRACE START [%s]\n", str_time); xdfree(str_time); xdebug_file_flush(context->trace_file); } void xdebug_trace_computerized_write_footer(void *ctxt) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; char *str_time; uint64_t nanotime; nanotime = xdebug_get_nanotime(); xdebug_file_printf(context->trace_file, "\t\t\t%F\t", XDEBUG_SECONDS_SINCE_START(nanotime)); xdebug_file_printf(context->trace_file, "%zu", zend_memory_usage(0)); xdebug_file_printf(context->trace_file, "\n"); str_time = xdebug_nanotime_to_chars(xdebug_get_nanotime(), 6); xdebug_file_printf(context->trace_file, "TRACE END [%s]\n\n", str_time); xdfree(str_time); xdebug_file_flush(context->trace_file); } char *xdebug_trace_computerized_get_filename(void *ctxt) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; return context->trace_file->name; } static void add_single_value(xdebug_str *str, zval *zv) { xdebug_str *tmp_value = NULL; tmp_value = xdebug_get_zval_value_line(zv, 0, NULL); if (tmp_value) { xdebug_str_add_str(str, tmp_value); xdebug_str_free(tmp_value); } else { xdebug_str_add_literal(str, "???"); } } static void add_arguments(xdebug_str *line_entry, function_stack_entry *fse) { unsigned int j = 0; /* Counter */ int sent_variables = fse->varc; if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) { sent_variables--; } /* Nr of arguments (11) */ xdebug_str_add_fmt(line_entry, "\t%d", sent_variables); /* Arguments (12-...) */ for (j = 0; j < sent_variables; j++) { xdebug_str_addc(line_entry, '\t'); if (!Z_ISUNDEF(fse->var[j].data)) { add_single_value(line_entry, &(fse->var[j].data)); } else { xdebug_str_add_literal(line_entry, "???"); } } } void xdebug_trace_computerized_function_entry(void *ctxt, function_stack_entry *fse) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; char *tmp_name; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str_add_fmt(&str, "%d\t", fse->level); xdebug_str_add_fmt(&str, "%d\t", fse->function_nr); tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT); xdebug_str_add_literal(&str, "0\t"); xdebug_str_add_fmt(&str, "%F\t", XDEBUG_SECONDS_SINCE_START(fse->nanotime)); xdebug_str_add_fmt(&str, "%lu\t", fse->memory); xdebug_str_add_fmt(&str, "%s\t", tmp_name); if (fse->user_defined == XDEBUG_USER_DEFINED) { xdebug_str_add_literal(&str, "1\t"); } else { xdebug_str_add_literal(&str, "0\t"); } xdfree(tmp_name); if (fse->function.include_filename) { if (fse->function.type == XFUNC_EVAL) { zend_string *escaped; escaped = php_addcslashes(fse->function.include_filename, (char*) "'\\\0..\37", 6); xdebug_str_addc(&str, '\''); xdebug_str_add_zstr(&str, escaped); xdebug_str_addc(&str, '\''); zend_string_release(escaped); } else { xdebug_str_add_zstr(&str, fse->function.include_filename); } } /* Filename and Lineno (9, 10) */ xdebug_str_add_fmt(&str, "\t%s\t%d", ZSTR_VAL(fse->filename), fse->lineno); if (XINI_TRACE(collect_params)) { add_arguments(&str, fse); } /* Trailing \n */ xdebug_str_addc(&str, '\n'); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdfree(str.d); } void xdebug_trace_computerized_function_exit(void *ctxt, function_stack_entry *fse) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str_add_fmt(&str, "%d\t", fse->level); xdebug_str_add_fmt(&str, "%d\t", fse->function_nr); xdebug_str_add_literal(&str, "1\t"); xdebug_str_add_fmt(&str, "%F\t", XDEBUG_SECONDS_SINCE_START(xdebug_get_nanotime())); xdebug_str_add_fmt(&str, "%lu\n", zend_memory_usage(0)); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdfree(str.d); } void xdebug_trace_computerized_function_return_value(void *ctxt, function_stack_entry *fse, zval *return_value) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str_add_fmt(&str, "%d\t", fse->level); xdebug_str_add_fmt(&str, "%d\t", fse->function_nr); xdebug_str_add_literal(&str, "R\t\t\t"); add_single_value(&str, return_value); xdebug_str_add_literal(&str, "\n"); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdfree(str.d); } void xdebug_trace_computerized_assignment(void *ctxt, function_stack_entry *fse, char *full_varname, zval *retval, char *right_full_varname, const char *op, char *filename, int lineno) { xdebug_trace_computerized_context *context = (xdebug_trace_computerized_context*) ctxt; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str *tmp_value; xdebug_str_add_fmt(&str, "%d\t", fse->level); /* no function_nr */ xdebug_str_add_literal(&str, "\t"); xdebug_str_add_literal(&str, "A\t"); /* skip time index, memory usage, function name, user defined */ xdebug_str_add_literal(&str, "\t\t\t\t"); /* Filename and Lineno (9, 10) */ xdebug_str_add_fmt(&str, "\t%s\t%d", filename, lineno); xdebug_str_add_fmt(&str, "\t%s", full_varname); if (op[0] != '\0' ) { /* pre/post inc/dec ops are special */ xdebug_str_addc(&str, ' '); xdebug_str_add(&str, op, 0); xdebug_str_addc(&str, ' '); tmp_value = xdebug_get_zval_value_line(retval, 0, NULL); if (tmp_value) { xdebug_str_add_str(&str, tmp_value); xdebug_str_free(tmp_value); } else { xdebug_str_add_literal(&str, "NULL"); } } /* Trailing \n */ xdebug_str_add_literal(&str, "\n"); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdfree(str.d); } xdebug_trace_handler_t xdebug_trace_handler_computerized = { xdebug_trace_computerized_init, xdebug_trace_computerized_deinit, xdebug_trace_computerized_write_header, xdebug_trace_computerized_write_footer, xdebug_trace_computerized_get_filename, xdebug_trace_computerized_function_entry, xdebug_trace_computerized_function_exit, xdebug_trace_computerized_function_return_value, NULL /* xdebug_trace_computerized_generator_return_value */, xdebug_trace_computerized_assignment }; xdebug-3.4.3/src/tracing/trace_computerized.h0000664000175000017500000000240615011062311020534 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef XDEBUG_TRACE_COMPUTERIZED_H #define XDEBUG_TRACE_COMPUTERIZED_H #include "tracing_private.h" typedef struct _xdebug_trace_computerized_context { xdebug_file *trace_file; } xdebug_trace_computerized_context; extern xdebug_trace_handler_t xdebug_trace_handler_computerized; #endif xdebug-3.4.3/src/tracing/trace_flamegraph.c0000664000175000017500000002273115011062311020126 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "php_xdebug.h" #include "tracing_private.h" #include "trace_flamegraph.h" #include "lib/lib_private.h" #include "lib/var_export_line.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); static flamegraph_function *fg_function_ctor() { flamegraph_function *ret; ret = xdmalloc(sizeof(flamegraph_function)); ret->value = 0; ret->prefix = NULL; return ret; } static inline void fg_function_dtor(flamegraph_function *function) { if (function->prefix) { xdebug_str_free(function->prefix); } xdfree(function); } static inline xdebug_str *fg_function_key(const int function_nr) { xdebug_str *key = xdebug_str_new(); xdebug_str_add_fmt(key, "%d", function_nr); return key; } static inline void fg_function_add(const xdebug_trace_flamegraph_context *context, const int function_nr, const flamegraph_function *function) { xdebug_str *key = fg_function_key(function_nr); xdebug_hash_add(context->functions, key->d, key->l, (void*) function); xdebug_str_free(key); } static inline flamegraph_function *fg_function_find(const xdebug_trace_flamegraph_context *context, const int function_nr) { flamegraph_function *function = NULL; xdebug_str *key = fg_function_key(function_nr); xdebug_hash_find(context->functions, key->d, key->l, (void*) &function); xdebug_str_free(key); return function; } static inline void fg_function_delete(const xdebug_trace_flamegraph_context *context, const int function_nr) { xdebug_str *key = fg_function_key(function_nr); xdebug_hash_delete(context->functions, key->d, key->l); xdebug_str_free(key); } /* Find parent function in xdebug stack, which is Fiber-safe. */ static inline function_stack_entry *fg_parent_find() { function_stack_entry *parent_fse; int parent_index = XDEBUG_VECTOR_COUNT(XG_BASE(stack)) - 2; parent_fse = xdebug_vector_element_get(XG_BASE(stack), parent_index); return parent_fse; } /* This function computes the 'self' cost for a function trace output. By only including the * 'inclusive' function cost, then in each stack, all functions will have more or less the same * cost, each top level will have only a few nanosec less than the previous: in this scenario, the * generated flamegraph will look flat, it will not highlight functions that really did cost a lot. * The 'self' cost is simply computed by removing children function call cost from its own inclusive * value. This is true as well for memory cost, in order to identify a potential leak it must * identify the function that allocated memory, if we don't sub children cost from parent cost, once * again the generated flamegraph will look linear and it will be harder to deduce which function * did allocate. */ static inline int compute_inclusive_value(const xdebug_trace_flamegraph_context *context, const function_stack_entry *fse) { int value = 0, current_mem; switch (context->mode) { case XDEBUG_TRACE_OPTION_FLAMEGRAPH_MEM: /* We compare with 'memory' because 'prev_memory' is not memory when starting the * function execution, 'memory' is. */ current_mem = zend_memory_usage(0); if (current_mem < fse->memory) { /* When memory is below 0, flamegraph generator will error, and you won't have a * good visual. This happens when garbage collection happened during this function * and freed something it didn't allocate, I guess. */ value = 0; } else { value = current_mem - fse->memory; } break; case XDEBUG_TRACE_OPTION_FLAMEGRAPH_COST: value = xdebug_get_nanotime() - fse->nanotime; break; } return value; } xdebug_trace_flamegraph_context *xdebug_trace_flamegraph_init(char *fname, zend_string *script_filename, int mode, long options) { xdebug_trace_flamegraph_context *tmp_flamegraph_context; tmp_flamegraph_context = xdmalloc(sizeof(xdebug_trace_flamegraph_context)); tmp_flamegraph_context->trace_file = xdebug_trace_open_file(fname, script_filename, options); if (!tmp_flamegraph_context->trace_file) { xdfree(tmp_flamegraph_context); return NULL; } tmp_flamegraph_context->mode = mode; tmp_flamegraph_context->functions = xdebug_hash_alloc(64, (xdebug_hash_dtor_t) fg_function_dtor); return tmp_flamegraph_context; } void *xdebug_trace_flamegraph_init_cost(char *fname, zend_string *script_filename, long options) { return xdebug_trace_flamegraph_init(fname, script_filename, XDEBUG_TRACE_OPTION_FLAMEGRAPH_COST, options); } void *xdebug_trace_flamegraph_init_mem(char *fname, zend_string *script_filename, long options) { return xdebug_trace_flamegraph_init(fname, script_filename, XDEBUG_TRACE_OPTION_FLAMEGRAPH_MEM, options); } void xdebug_trace_flamegraph_deinit(void *ctxt) { xdebug_trace_flamegraph_context *context = (xdebug_trace_flamegraph_context*) ctxt; xdebug_file_close(context->trace_file); xdebug_file_dtor(context->trace_file); context->trace_file = NULL; if (context->functions) { xdebug_hash_destroy(context->functions); context->functions = NULL; } xdfree(context); } char *xdebug_trace_flamegraph_get_filename(void *ctxt) { xdebug_trace_flamegraph_context *context = (xdebug_trace_flamegraph_context*) ctxt; return context->trace_file->name; } void xdebug_trace_flamegraph_function_entry(void *ctxt, function_stack_entry *fse) { xdebug_trace_flamegraph_context *context = (xdebug_trace_flamegraph_context*) ctxt; function_stack_entry *parent_fse; flamegraph_function *function; flamegraph_function *parent_function; xdebug_str *prefix = xdebug_str_new(); char *tmp_name; tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_ADD_FILE_NAME); function = fg_function_ctor(); parent_fse = fg_parent_find(); if (!parent_fse) { /* No parent means we are top-level, prefix is function name. */ xdebug_str_add_fmt(prefix, tmp_name); } else { /* Find value in our custom hashmap in order to compute prefix. */ parent_function = fg_function_find(context, parent_fse->function_nr); if (!parent_function) { /* No function found is a bug, we should have one. treat it as it was a top-level * function. */ xdebug_str_add_fmt(prefix, tmp_name); } else { xdebug_str_add_fmt(prefix, "%s;%s", parent_function->prefix->d, tmp_name); } } function->prefix = prefix; fg_function_add(context, fse->function_nr, function); xdfree(tmp_name); } void xdebug_trace_flamegraph_function_exit(void *ctxt, function_stack_entry *fse) { xdebug_trace_flamegraph_context *context = (xdebug_trace_flamegraph_context*) ctxt; flamegraph_function *function; flamegraph_function *parent_function; function_stack_entry *parent_fse; xdebug_str str = XDEBUG_STR_INITIALIZER; int inclusive; int self; function = fg_function_find(context, fse->function_nr); if (!function) { /* This should never happen, better be safe than sorry. */ return; } inclusive = compute_inclusive_value(context, fse); self = inclusive - function->value; xdebug_str_add_fmt(&str, "%s %d\n", function->prefix->d, self); /* xdebug_hash_delete() will free the function. */ fg_function_delete(context, fse->function_nr); /* Increment head value (which is now parent) by inclusive cost. */ parent_fse = fg_parent_find(); if (parent_fse) { parent_function = fg_function_find(context, parent_fse->function_nr); if (parent_function) { parent_function->value += inclusive; } } xdebug_file_printf(context->trace_file, "%s", str.d); xdfree(str.d); } xdebug_trace_handler_t xdebug_trace_handler_flamegraph_cost = { xdebug_trace_flamegraph_init_cost, xdebug_trace_flamegraph_deinit, NULL /* xdebug_trace_flamegraph_write_header */, NULL /* xdebug_trace_flamegraph_write_footer */, xdebug_trace_flamegraph_get_filename, xdebug_trace_flamegraph_function_entry, xdebug_trace_flamegraph_function_exit, NULL /* xdebug_trace_flamegraph_function_return_value */, NULL /* xdebug_trace_flamegraph_generator_return_value */, NULL /* xdebug_trace_flamegraph_assignment */ }; xdebug_trace_handler_t xdebug_trace_handler_flamegraph_mem = { xdebug_trace_flamegraph_init_mem, xdebug_trace_flamegraph_deinit, NULL /* xdebug_trace_flamegraph_write_header */, NULL /* xdebug_trace_flamegraph_write_footer */, xdebug_trace_flamegraph_get_filename, xdebug_trace_flamegraph_function_entry, xdebug_trace_flamegraph_function_exit, NULL /* xdebug_trace_flamegraph_function_return_value */, NULL /* xdebug_trace_flamegraph_generator_return_value */, NULL /* xdebug_trace_flamegraph_assignment */ }; xdebug-3.4.3/src/tracing/trace_flamegraph.h0000664000175000017500000000273015011062311020130 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef XDEBUG_TRACE_FLAMEGRAPH_H #define XDEBUG_TRACE_FLAMEGRAPH_H #include "tracing_private.h" typedef struct _flamegraph_function { xdebug_str *prefix; int value; } flamegraph_function; typedef struct _xdebug_trace_flamegraph_context { xdebug_file *trace_file; int mode; xdebug_hash *functions; } xdebug_trace_flamegraph_context; extern xdebug_trace_handler_t xdebug_trace_handler_flamegraph_cost; extern xdebug_trace_handler_t xdebug_trace_handler_flamegraph_mem; #endif xdebug-3.4.3/src/tracing/trace_html.c0000664000175000017500000001125015011062311016756 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "php_xdebug.h" #include "tracing_private.h" #include "trace_html.h" #include "lib/var.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); void *xdebug_trace_html_init(char *fname, zend_string *script_filename, long options) { xdebug_trace_html_context *tmp_html_context; tmp_html_context = xdmalloc(sizeof(xdebug_trace_html_context)); tmp_html_context->trace_file = xdebug_trace_open_file(fname, script_filename, options); if (!tmp_html_context->trace_file) { xdfree(tmp_html_context); return NULL; } return tmp_html_context; } void xdebug_trace_html_deinit(void *ctxt) { xdebug_trace_html_context *context = (xdebug_trace_html_context*) ctxt; xdebug_file_close(context->trace_file); xdebug_file_dtor(context->trace_file); context->trace_file = NULL; xdfree(context); } void xdebug_trace_html_write_header(void *ctxt) { xdebug_trace_html_context *context = (xdebug_trace_html_context*) ctxt; xdebug_file_printf(context->trace_file, "\n"); xdebug_file_printf(context->trace_file, "\t"); xdebug_file_printf(context->trace_file, ""); xdebug_file_printf(context->trace_file, "\n"); xdebug_file_flush(context->trace_file); } void xdebug_trace_html_write_footer(void *ctxt) { xdebug_trace_html_context *context = (xdebug_trace_html_context*) ctxt; xdebug_file_printf(context->trace_file, "
#TimeMemFunctionLocation
\n"); xdebug_file_flush(context->trace_file); } char *xdebug_trace_html_get_filename(void *ctxt) { xdebug_trace_html_context *context = (xdebug_trace_html_context*) ctxt; return context->trace_file->name; } void xdebug_trace_html_function_entry(void *ctxt, function_stack_entry *fse) { xdebug_trace_html_context *context = (xdebug_trace_html_context*) ctxt; char *tmp_name; unsigned int j; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str_add_literal(&str, "\t"); xdebug_str_add_fmt(&str, "%d", fse->function_nr); xdebug_str_add_fmt(&str, "%0.6F", XDEBUG_SECONDS_SINCE_START(fse->nanotime)); xdebug_str_add_fmt(&str, "%lu", fse->memory); xdebug_str_add_literal(&str, ""); for (j = 0; j < fse->level - 1; j++) { xdebug_str_add_literal(&str, "   "); } xdebug_str_add_literal(&str, "->"); tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT); xdebug_str_add_fmt(&str, "%s(", tmp_name); xdfree(tmp_name); if (fse->function.include_filename) { if (fse->function.type == XFUNC_EVAL) { xdebug_str *joined; xdebug_arg *parts; parts = xdebug_arg_ctor(); xdebug_explode("\n", ZSTR_VAL(fse->function.include_filename), parts, 99999); joined = xdebug_join("
", parts, 0, 99999); xdebug_arg_dtor(parts); xdebug_str_add_fmt(&str, "'%s'", joined->d); xdebug_str_free(joined); } else { xdebug_str_add_zstr(&str, fse->function.include_filename); } } xdebug_str_add_fmt(&str, ")%s:%d", ZSTR_VAL(fse->filename), fse->lineno); xdebug_str_add_literal(&str, "\n"); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdfree(str.d); } xdebug_trace_handler_t xdebug_trace_handler_html = { xdebug_trace_html_init, xdebug_trace_html_deinit, xdebug_trace_html_write_header, xdebug_trace_html_write_footer, xdebug_trace_html_get_filename, xdebug_trace_html_function_entry, NULL /* xdebug_trace_html_function_exit */, NULL /* xdebug_trace_html_function_return_value */, NULL /* xdebug_trace_html_generator_return_value */, NULL /* xdebug_trace_html_assignment */ }; xdebug-3.4.3/src/tracing/trace_html.h0000664000175000017500000000233615011062311016770 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef XDEBUG_TRACE_HTML_H #define XDEBUG_TRACE_HTML_H #include "tracing_private.h" typedef struct _xdebug_trace_html_context { xdebug_file *trace_file; } xdebug_trace_html_context; extern xdebug_trace_handler_t xdebug_trace_handler_html; #endif xdebug-3.4.3/src/tracing/trace_textual.c0000664000175000017500000002340715011062311017507 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2023 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "lib/php-header.h" #include "ext/standard/php_string.h" #include "php_xdebug.h" #include "tracing_private.h" #include "trace_textual.h" #include "lib/lib_private.h" #include "lib/var_export_line.h" extern ZEND_DECLARE_MODULE_GLOBALS(xdebug); void *xdebug_trace_textual_init(char *fname, zend_string *script_filename, long options) { xdebug_trace_textual_context *tmp_textual_context; tmp_textual_context = xdmalloc(sizeof(xdebug_trace_textual_context)); tmp_textual_context->trace_file = xdebug_trace_open_file(fname, script_filename, options); if (!tmp_textual_context->trace_file) { xdfree(tmp_textual_context); return NULL; } return tmp_textual_context; } void xdebug_trace_textual_deinit(void *ctxt) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; xdebug_file_close(context->trace_file); xdebug_file_dtor(context->trace_file); context->trace_file = NULL; xdfree(context); } void xdebug_trace_textual_write_header(void *ctxt) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; char *str_time; str_time = xdebug_nanotime_to_chars(xdebug_get_nanotime(), 6); xdebug_file_printf(context->trace_file, "TRACE START [%s]\n", str_time); xdfree(str_time); xdebug_file_flush(context->trace_file); } void xdebug_trace_textual_write_footer(void *ctxt) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; char *str_time; uint64_t nanotime; char *tmp; nanotime = xdebug_get_nanotime(); tmp = xdebug_sprintf("%10.4F ", XDEBUG_SECONDS_SINCE_START(nanotime)); xdebug_file_printf(context->trace_file, "%s", tmp); xdfree(tmp); xdebug_file_printf(context->trace_file, "%10zu", zend_memory_usage(0)); xdebug_file_printf(context->trace_file, "\n"); str_time = xdebug_nanotime_to_chars(nanotime, 6); xdebug_file_printf(context->trace_file, "TRACE END [%s]\n\n", str_time); xdfree(str_time); xdebug_file_flush(context->trace_file); } char *xdebug_trace_textual_get_filename(void *ctxt) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; return context->trace_file->name; } static void add_single_value(xdebug_str *str, zval *zv) { xdebug_str *tmp_value = NULL; tmp_value = xdebug_get_zval_value_line(zv, 0, NULL); if (tmp_value) { xdebug_str_add_str(str, tmp_value); xdebug_str_free(tmp_value); } else { xdebug_str_add_literal(str, "???"); } } static void add_arguments(xdebug_str *line_entry, function_stack_entry *fse) { unsigned int j = 0; /* Counter */ int c = 0; /* Comma flag */ int variadic_opened = 0; int variadic_count = 0; int sent_variables = fse->varc; if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) { sent_variables--; } for (j = 0; j < sent_variables; j++) { if (c) { xdebug_str_add_literal(line_entry, ", "); } else { c = 1; } if (fse->var[j].is_variadic) { xdebug_str_add_literal(line_entry, "..."); variadic_opened = 1; c = 0; } if (fse->var[j].name) { xdebug_str_addc(line_entry, '$'); xdebug_str_add_zstr(line_entry, fse->var[j].name); if (variadic_opened && !fse->var[j].is_variadic) { xdebug_str_add_literal(line_entry, " => "); } else { xdebug_str_add_literal(line_entry, " = "); } } if (fse->var[j].is_variadic) { xdebug_str_add_literal(line_entry, "variadic("); if (Z_ISUNDEF(fse->var[j].data)) { continue; } c = 1; } if (variadic_opened && (!fse->var[j].name || fse->var[j].is_variadic)) { xdebug_str_add_fmt(line_entry, "%d => ", variadic_count++); } if (!Z_ISUNDEF(fse->var[j].data)) { add_single_value(line_entry, &fse->var[j].data); } else { xdebug_str_add_literal(line_entry, "???"); } } if (variadic_opened) { xdebug_str_addc(line_entry, ')'); } } void xdebug_trace_textual_function_entry(void *ctxt, function_stack_entry *fse) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; unsigned int j = 0; /* Counter */ char *tmp_name; xdebug_str str = XDEBUG_STR_INITIALIZER; tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT); xdebug_str_add_fmt(&str, "%10.4F ", XDEBUG_SECONDS_SINCE_START(fse->nanotime)); xdebug_str_add_fmt(&str, "%10lu ", fse->memory); for (j = 0; j < fse->level; j++) { xdebug_str_add_literal(&str, " "); } xdebug_str_add_fmt(&str, "-> %s(", tmp_name); xdfree(tmp_name); if (XINI_TRACE(collect_params)) { add_arguments(&str, fse); } if (fse->function.include_filename) { if (fse->function.type == XFUNC_EVAL) { zend_string *escaped; escaped = php_addcslashes(fse->function.include_filename, (char*) "'\\\0..\37", 6); xdebug_str_addc(&str, '\''); xdebug_str_add_zstr(&str, escaped); xdebug_str_addc(&str, '\''); zend_string_release(escaped); } else { xdebug_str_add_zstr(&str, fse->function.include_filename); } } xdebug_str_add_fmt(&str, ") %s:%d\n", ZSTR_VAL(fse->filename), fse->lineno); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdfree(str.d); } /* Used for normal return values, and generator return values */ static void xdebug_return_trace_stack_common(xdebug_str *str, function_stack_entry *fse) { unsigned int j = 0; /* Counter */ xdebug_str_add_fmt(str, "%10.4F ", XDEBUG_SECONDS_SINCE_START(xdebug_get_nanotime())); xdebug_str_add_fmt(str, "%10lu ", zend_memory_usage(0)); for (j = 0; j < fse->level; j++) { xdebug_str_add_literal(str, " "); } xdebug_str_add_literal(str, " >=> "); } void xdebug_trace_textual_function_return_value(void *ctxt, function_stack_entry *fse, zval *return_value) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str *tmp_value; xdebug_return_trace_stack_common(&str, fse); tmp_value = xdebug_get_zval_value_line(return_value, 0, NULL); if (tmp_value) { xdebug_str_add_str(&str, tmp_value); xdebug_str_free(tmp_value); } xdebug_str_addc(&str, '\n'); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdebug_str_destroy(&str); } void xdebug_trace_textual_generator_return_value(void *ctxt, function_stack_entry *fse, zend_generator *generator) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str *tmp_value = NULL; if (! (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) { return; } if (generator->execute_data == NULL) { return; } /* Generator key */ tmp_value = xdebug_get_zval_value_line(&generator->key, 0, NULL); if (!tmp_value) { return; } xdebug_return_trace_stack_common(&str, fse); xdebug_str_addc(&str, '('); xdebug_str_add_str(&str, tmp_value); xdebug_str_add_literal(&str, " => "); xdebug_str_free(tmp_value); tmp_value = xdebug_get_zval_value_line(&generator->value, 0, NULL); if (tmp_value) { xdebug_str_add_str(&str, tmp_value); xdebug_str_free(tmp_value); } xdebug_str_add_literal(&str, ")\n"); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdebug_str_destroy(&str); } void xdebug_trace_textual_assignment(void *ctxt, function_stack_entry *fse, char *full_varname, zval *retval, char *right_full_varname, const char *op, char *filename, int lineno) { xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt; unsigned int j = 0; xdebug_str str = XDEBUG_STR_INITIALIZER; xdebug_str *tmp_value; xdebug_str_add_literal(&str, " "); for (j = 0; j <= fse->level; j++) { xdebug_str_add_literal(&str, " "); } xdebug_str_add_literal(&str, " => "); xdebug_str_add(&str, full_varname, 0); if (op[0] != '\0' ) { /* pre/post inc/dec ops are special */ xdebug_str_addc(&str, ' '); xdebug_str_add(&str, op, 0); xdebug_str_addc(&str, ' '); if (right_full_varname) { xdebug_str_add(&str, right_full_varname, 0); } else { tmp_value = xdebug_get_zval_value_line(retval, 0, NULL); if (tmp_value) { xdebug_str_add_str(&str, tmp_value); xdebug_str_free(tmp_value); } else { xdebug_str_add_literal(&str, "NULL"); } } } xdebug_str_add_fmt(&str, " %s:%d\n", filename, lineno); xdebug_file_printf(context->trace_file, "%s", str.d); xdebug_file_flush(context->trace_file); xdfree(str.d); } xdebug_trace_handler_t xdebug_trace_handler_textual = { xdebug_trace_textual_init, xdebug_trace_textual_deinit, xdebug_trace_textual_write_header, xdebug_trace_textual_write_footer, xdebug_trace_textual_get_filename, xdebug_trace_textual_function_entry, NULL /*xdebug_trace_textual_function_exit */, xdebug_trace_textual_function_return_value, xdebug_trace_textual_generator_return_value, xdebug_trace_textual_assignment }; xdebug-3.4.3/src/tracing/trace_textual.h0000664000175000017500000000235515011062311017513 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2021 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef XDEBUG_TRACE_TEXTUAL_H #define XDEBUG_TRACE_TEXTUAL_H #include "tracing_private.h" typedef struct _xdebug_trace_textual_context { xdebug_file *trace_file; } xdebug_trace_textual_context; extern xdebug_trace_handler_t xdebug_trace_handler_textual; #endif xdebug-3.4.3/config.m40000664000175000017500000002134215011062311013764 0ustar derickderickdnl config.m4 for extension Xdebug PHP_ARG_ENABLE(xdebug, whether to enable Xdebug support, [ --enable-xdebug Enable Xdebug support]) PHP_ARG_ENABLE(xdebug-dev, whether to enable Xdebug developer build flags, [ --enable-xdebug-dev Xdebug: Enable developer flags],, no) PHP_ARG_WITH(xdebug-compression, [whether to compress profiler files (requires zlib)], [ --without-xdebug-compression Xdebug: Disable compression through zlib],yes,no) m4_include([m4/pkg.m4]) m4_include([m4/clocks.m4]) if test "$PHP_XDEBUG" != "no"; then AC_MSG_CHECKING([for supported PHP version]) PHP_XDEBUG_FOUND_VERSION=`${PHP_CONFIG} --version` PHP_XDEBUG_FOUND_VERNUM=`${PHP_CONFIG} --vernum` if test "$PHP_XDEBUG_FOUND_VERNUM" -lt "80000"; then AC_MSG_ERROR([not supported. Need a PHP version >= 8.0.0 and < 8.5.0 (found $PHP_XDEBUG_FOUND_VERSION)]) else if test "$PHP_XDEBUG_FOUND_VERNUM" -ge "80500"; then AC_MSG_ERROR([not supported. Need a PHP version >= 8.0.0 and < 8.5.0 (found $PHP_XDEBUG_FOUND_VERSION)]) else AC_MSG_RESULT([supported ($PHP_XDEBUG_FOUND_VERSION)]) fi fi AC_DEFINE(HAVE_XDEBUG,1,[ ]) old_CPPFLAGS=$CPPFLAGS CPPFLAGS="$INCLUDES $CPPFLAGS" AC_XDEBUG_CLOCK AC_CHECK_HEADERS([netinet/in.h poll.h sys/poll.h]) case $host_os in linux*) AC_DEFINE(HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT,1,[ do have control socket support? ]) AC_CHECK_HEADERS([linux/rtnetlink.h], [], [ case $host_os in linux-musl*) AC_MSG_ERROR([rtnetlink.h is required, install the linux-headers package: apk add --update linux-headers]) esac AC_MSG_ERROR([rtnetlink.h is required, please make sure it is available by installing the correct package]) ]) esac PHP_CHECK_FUNC(res_ninit, resolv) PHP_CHECK_FUNC(res_nclose, resolv) PHP_CHECK_LIBRARY(m, cos, [ PHP_ADD_LIBRARY(m,, XDEBUG_SHARED_LIBADD) ]) if test "$PHP_XDEBUG_COMPRESSION" != "no"; then PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.9],[ PHP_EVAL_LIBLINE($ZLIB_LIBS, XDEBUG_SHARED_LIBADD) PHP_EVAL_INCLINE($ZLIB_CFLAGS) AC_DEFINE(HAVE_XDEBUG_ZLIB,1,[ do we have zlib support compiled in? ]) ],[ ]) fi CPPFLAGS=$old_CPPFLAGS if test "$PHP_XDEBUG_DEV" = "yes"; then AX_CHECK_COMPILE_FLAG(-Wbool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wbool-conversion") AX_CHECK_COMPILE_FLAG(-Wdeclaration-after-statement, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wdeclaration-after-statement") AX_CHECK_COMPILE_FLAG(-Wdiscarded-qualifiers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wdiscarded-qualifiers") AX_CHECK_COMPILE_FLAG(-Wduplicate-enum, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wduplicate-enum") AX_CHECK_COMPILE_FLAG(-Wempty-body, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wempty-body") AX_CHECK_COMPILE_FLAG(-Wenum-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wenum-compare") AX_CHECK_COMPILE_FLAG(-Werror, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Werror") AX_CHECK_COMPILE_FLAG(-Wextra, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wextra") AX_CHECK_COMPILE_FLAG(-Wformat-nonliteral, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wformat-nonliteral") AX_CHECK_COMPILE_FLAG(-Wformat-security, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wformat-security") AX_CHECK_COMPILE_FLAG(-Wheader-guard, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wheader-guard") AX_CHECK_COMPILE_FLAG(-Wincompatible-pointer-types-discards-qualifiers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wincompatible-pointer-types-discards-qualifiers") AX_CHECK_COMPILE_FLAG(-Wimplicit-fallthrough, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wimplicit-fallthrough") AX_CHECK_COMPILE_FLAG(-Winit-self, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Winit-self") AX_CHECK_COMPILE_FLAG(-Wlogical-not-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-not-parentheses") AX_CHECK_COMPILE_FLAG(-Wlogical-op, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-op") AX_CHECK_COMPILE_FLAG(-Wlogical-op-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-op-parentheses") AX_CHECK_COMPILE_FLAG(-Wloop-analysis, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wloop-analysis") AX_CHECK_COMPILE_FLAG(-Wmaybe-uninitialized, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wmaybe-uninitialized") AX_CHECK_COMPILE_FLAG(-Wmissing-format-attribute, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wmissing-format-attribute") AX_CHECK_COMPILE_FLAG(-Wno-missing-field-initializers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-missing-field-initializers") AX_CHECK_COMPILE_FLAG(-Wno-sign-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-sign-compare") AX_CHECK_COMPILE_FLAG(-Wno-unused-but-set-variable, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-but-set-variable") AX_CHECK_COMPILE_FLAG(-Wno-unused-parameter, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-parameter") AX_CHECK_COMPILE_FLAG(-Wno-variadic-macros, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-variadic-macros") AX_CHECK_COMPILE_FLAG(-Wparentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wparentheses") AX_CHECK_COMPILE_FLAG(-Wpointer-bool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wpointer-bool-conversion") AX_CHECK_COMPILE_FLAG(-Wsizeof-array-argument, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wsizeof-array-argument") AX_CHECK_COMPILE_FLAG(-Wstring-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wstring-conversion") AX_CHECK_COMPILE_FLAG(-Wwrite-strings, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wwrite-strings") AX_CHECK_COMPILE_FLAG(-Wpointer-arith, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wpointer-arith") AX_CHECK_COMPILE_FLAG(-fdiagnostics-show-option, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fdiagnostics-show-option") AX_CHECK_COMPILE_FLAG(-fno-exceptions, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fno-exceptions") AX_CHECK_COMPILE_FLAG(-fno-omit-frame-pointer, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fno-omit-frame-pointer") AX_CHECK_COMPILE_FLAG(-fno-optimize-sibling-calls, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fno-optimize-sibling-calls") AX_CHECK_COMPILE_FLAG(-fsanitize-address, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fsanitize-address") AX_CHECK_COMPILE_FLAG(-fstack-protector, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fstack-protector") MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS" STD_CFLAGS="-g -O0 -Wall" fi PHP_XDEBUG_CFLAGS="$STD_CFLAGS $MAINTAINER_CFLAGS" XDEBUG_BASE_SOURCES="src/base/base.c src/base/ctrl_socket.c src/base/filter.c" XDEBUG_LIB_SOURCES="src/lib/usefulstuff.c src/lib/cmd_parser.c src/lib/compat.c src/lib/crc32.c src/lib/file.c src/lib/hash.c src/lib/headers.c src/lib/lib.c src/lib/llist.c src/lib/log.c src/lib/set.c src/lib/str.c src/lib/timing.c src/lib/var.c src/lib/var_export_html.c src/lib/var_export_line.c src/lib/var_export_text.c src/lib/var_export_xml.c src/lib/xml.c" XDEBUG_COVERAGE_SOURCES="src/coverage/branch_info.c src/coverage/code_coverage.c" XDEBUG_DEBUGGER_SOURCES="src/debugger/com.c src/debugger/debugger.c src/debugger/handler_dbgp.c src/debugger/handlers.c src/debugger/ip_info.c" XDEBUG_DEVELOP_SOURCES="src/develop/develop.c src/develop/monitor.c src/develop/php_functions.c src/develop/stack.c src/develop/superglobals.c" XDEBUG_GCSTATS_SOURCES="src/gcstats/gc_stats.c" XDEBUG_PROFILER_SOURCES="src/profiler/profiler.c" XDEBUG_TRACING_SOURCES="src/tracing/trace_computerized.c src/tracing/trace_flamegraph.c src/tracing/trace_html.c src/tracing/trace_textual.c src/tracing/tracing.c" PHP_NEW_EXTENSION(xdebug, xdebug.c $XDEBUG_BASE_SOURCES $XDEBUG_LIB_SOURCES $XDEBUG_COVERAGE_SOURCES $XDEBUG_DEBUGGER_SOURCES $XDEBUG_DEVELOP_SOURCES $XDEBUG_GCSTATS_SOURCES $XDEBUG_PROFILER_SOURCES $XDEBUG_TRACING_SOURCES, $ext_shared,,$PHP_XDEBUG_CFLAGS,,yes) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/base]) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/lib]) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/coverage]) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/debugger]) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/develop]) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/gcstats]) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/profiler]) PHP_ADD_BUILD_DIR(PHP_EXT_BUILDDIR(xdebug)[/src/tracing]) PHP_SUBST(XDEBUG_SHARED_LIBADD) PHP_ADD_MAKEFILE_FRAGMENT PHP_ADD_INCLUDE($ext_srcdir/src) PHP_ADD_INCLUDE($ext_builddir/src) fi xdebug-3.4.3/config.w320000664000175000017500000000541215011062311014057 0ustar derickderick// vim:ft=javascript ARG_WITH("xdebug", "Xdebug support", "no"); ARG_WITH("xdebug-compression", "whether to compress profiler files (requires zlib)", "no"); if (PHP_XDEBUG != 'no') { var XDEBUG_BASE_SOURCES="base.c filter.c" var XDEBUG_LIB_SOURCES="usefulstuff.c cmd_parser.c compat.c crc32.c file.c hash.c headers.c lib.c llist.c log.c set.c str.c timing.c var.c var_export_html.c var_export_line.c var_export_text.c var_export_xml.c xml.c" var XDEBUG_COVERAGE_SOURCES="branch_info.c code_coverage.c" var XDEBUG_DEBUGGER_SOURCES="com.c debugger.c handler_dbgp.c handlers.c" var XDEBUG_DEVELOP_SOURCES="develop.c monitor.c php_functions.c stack.c superglobals.c" var XDEBUG_GCSTATS_SOURCES="gc_stats.c" var XDEBUG_PROFILER_SOURCES="profiler.c" var XDEBUG_TRACING_SOURCES="trace_computerized.c trace_flamegraph.c trace_html.c trace_textual.c tracing.c" var files = "xdebug.c"; var XDEBUG_PHP_VERSION = 10000 * PHP_VERSION + 100 * PHP_MINOR_VERSION + 1 * PHP_RELEASE_VERSION; if (XDEBUG_PHP_VERSION < 80000) { ERROR("not supported. Need a PHP version >= 8.0.0 and < 8.5.0 (found " + XDEBUG_PHP_VERSION + ")"); } else if (XDEBUG_PHP_VERSION >= 80500) { ERROR("not supported. Need a PHP version >= 8.0.0 and < 8.5.0 (found " + XDEBUG_PHP_VERSION + ")"); } else { MESSAGE("supported (" + XDEBUG_PHP_VERSION + ")"); } if (typeof(ZEND_EXTENSION) == 'undefined') { EXTENSION('xdebug', files); } else { ZEND_EXTENSION('xdebug', files); } ADD_FLAG("CFLAGS_XDEBUG", " /I " + configure_module_dirname + " "); ADD_FLAG("CFLAGS_XDEBUG", " /I " + configure_module_dirname + "/src "); ADD_SOURCES(configure_module_dirname + "/src/base", XDEBUG_BASE_SOURCES, "xdebug"); ADD_SOURCES(configure_module_dirname + "/src/lib", XDEBUG_LIB_SOURCES, "xdebug"); ADD_SOURCES(configure_module_dirname + "/src/coverage", XDEBUG_COVERAGE_SOURCES, "xdebug"); ADD_SOURCES(configure_module_dirname + "/src/debugger", XDEBUG_DEBUGGER_SOURCES, "xdebug"); ADD_SOURCES(configure_module_dirname + "/src/develop", XDEBUG_DEVELOP_SOURCES, "xdebug"); ADD_SOURCES(configure_module_dirname + "/src/gcstats", XDEBUG_GCSTATS_SOURCES, "xdebug"); ADD_SOURCES(configure_module_dirname + "/src/profiler", XDEBUG_PROFILER_SOURCES, "xdebug"); ADD_SOURCES(configure_module_dirname + "/src/tracing", XDEBUG_TRACING_SOURCES, "xdebug"); // PHP_ZLIB is "yes"/"no" for in-tree builds, but boolean for phpize builds var XDEBUG_ZLIB = (!MODE_PHPIZE && PHP_ZLIB == "yes") || (MODE_PHPIZE && PHP_ZLIB); if (PHP_XDEBUG_COMPRESSION != "no") { if (((!XDEBUG_ZLIB) && (CHECK_LIB("zlib_a.lib;zlib.lib", "xdebug", PHP_XDEBUG))) || (PHP_ZLIB_SHARED && CHECK_LIB("zlib.lib", "xdebug", PHP_XDEBUG)) || (XDEBUG_ZLIB && (!PHP_ZLIB_SHARED)) ) { AC_DEFINE('HAVE_XDEBUG_ZLIB', 1); } } AC_DEFINE("HAVE_XDEBUG", 1, "Xdebug support"); } xdebug-3.4.3/CREDITS0000644000175000017500000000006515011062311013272 0ustar derickderickXdebug Derick Rethans, Ilia Alshanetsky, Harald Radi xdebug-3.4.3/LICENSE0000664000175000017500000000572315011062311013267 0ustar derickderick-------------------------------------------------------------------- The Xdebug License, version 1.03 (Based on "The PHP License", version 3.01) Copyright (c) 2003-2022 Derick Rethans. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "Xdebug" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact derick@xdebug.org. 4. Products derived from this software may not be called "Xdebug", nor may "Xdebug" appear in their name, without prior written permission from derick@xdebug.org. 5. Derick Rethans may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by Derick Rethans. No one other than Derick Rethans has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes Xdebug software, freely available from ". THIS SOFTWARE IS PROVIDED BY DERICK RETHANS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- This software consists of voluntary contributions made by some individuals on behalf of Derick Rethans. Derick Rethans can be contacted via e-mail at derick@xdebug.org. For more information on Xdebug, please see . xdebug-3.4.3/xdebug.ini0000664000175000017500000013703015011062311014236 0ustar derickderick; This file is generated by the 'xdebug.org:html/docs/convert.php' robot ; for Xdebug 3.4.3 — do not modify by hand ; ----------------------------------------------------------------------------- ; xdebug.cli_color ; ; Type: integer, Default value: 0 ; ; If this setting is 1, Xdebug will color var_dumps and stack traces output when ; in CLI mode and when the output is a tty. On Windows, the ANSICON [1] tool ; needs to be installed. ; ; [1] http://adoxa.altervista.org/ansicon/ ; ; If the setting is 2, then Xdebug will always color var_dumps and stack trace, ; no matter whether it's connected to a tty or whether ANSICON is installed. In ; this case, you might end up seeing escape codes. ; ; See this article [1] for some more information. ; ; [1] https://derickrethans.nl/cli-color.html ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.cli_color = 0 ; ----------------------------------------------------------------------------- ; xdebug.client_discovery_header ; ; Type: string, Default value: "HTTP_X_FORWARDED_FOR,REMOTE_ADDR" ; ; If xdebug.client_discovery_header is configured to be a non-empty string, then ; the value is used as key in the ``$_SERVER`` superglobal array to determine ; which header to use to find the IP address or hostname to use for 'connecting ; back to'. This setting is only used in combination with ; xdebug.discover_client_host and is otherwise ignored. ; ; For example, if xdebug.client_discovery_header is set to ; ``HTTP_FORWARD_HOST``, then Xdebug will check ; ``$_SERVER['HTTP_FORWARD_HOST']`` to obtain the IP address to use for ; 'connecting back'. ; ; It is possible to configure multiple fallbacks by using a comma separated list ; of values. For example if you want to use ``HTTP_FORWARD_HOST`` first, and ; then also want to check ``REMOTE_ADDR``, then you set ; xdebug.client_discovery_header to ``HTTP_FORWARD_HOST,REMOTE_ADDR``. ; ; .. warning:: ; ; PHP automatically prepends ``HTTP_``, and converts ``-`` to ``_``, for ; received HTTP header names. The ``THIS-IS-MY-HOST`` HTTP header is ; converted into ``$_SERVER['HTTP_THIS_IS_MY_HOST']``. Therefore, the ; xdebug.client_discovery_header needs to be set to ``HTTP_THIS_IS_MY_HOST`` ; to match this. ; ; If you have logging enabled, and set the xdebug.log_level setting to ``10``, ; then Xdebug will list every header, the header value, and the used header (if ; any) when attempting to find the IP address to connect back to. ; ; .. note:: ; ; Xdebug 3.2 and later no longer fall back to the ; ``$_SERVER['HTTP_X_FORWARDED_FOR']`` and ``$_SERVER['REMOTE_ADDR']`` header ; values by default. If you want these headers to be used as well, you ; specifically need to add these to the list of headers, by setting ; xdebug.client_discovery_header to ; ``YOUR_OWN_HEADER,HTTP_X_FORWARDED_FOR,REMOTE_ADDR``. ; ; ;xdebug.client_discovery_header = "HTTP_X_FORWARDED_FOR,REMOTE_ADDR" ; ----------------------------------------------------------------------------- ; xdebug.client_host ; ; Type: string, Default value: localhost ; ; Configures the IP address or hostname where Xdebug will attempt to connect to ; when initiating a debugging connection. This address should be the address of ; the machine where your IDE or debugging client is listening for incoming ; debugging connections. ; ; On non-Windows platforms, it is also possible to configure a Unix domain ; socket [1] which is supported by only a select view debugging clients. In that ; case, instead of the hostname or IP address, use ``unix:///path/to/sock``. ; ; [1] https://en.wikipedia.org/wiki/Unix_domain_socket ; ; If xdebug.discover_client_host is enabled then Xdebug will only use the value ; of this setting in case Xdebug can not connect to an IDE using the information ; it obtained from HTTP headers. In that case, the value of this setting acts as ; a fallback only. ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.client_host = localhost ; ----------------------------------------------------------------------------- ; xdebug.client_port ; ; Type: integer, Default value: 9003 ; ; The port to which Xdebug tries to connect on the remote host. Port ``9003`` is ; the default for both Xdebug and the Command Line Debug Client. As many clients ; use this port number, it is best to leave this setting unchanged. ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.client_port = 9003 ; ----------------------------------------------------------------------------- ; xdebug.cloud_id ; ; Type: string, Default value: ; ; With this setting you configure Xdebug for use with Xdebug Cloud [1]. It needs ; to match one of the tokens from your profile page [2]. ; ; [1] https://xdebug.cloud ; [2] https://xdebug.cloud/profile#tokens ; ; Your IDE needs to be configured with the same token for Xdebug and your IDE to ; communicate through Xdebug Cloud. ; ; | In PhpStorm you can find this setting under: ; | File | Settings | PHP | Debug | Xdebug Cloud for Windows and Linux ; | PhpStorm | Preferences | PHP | Debug | Xdebug Cloud for macOS ; ; ;xdebug.cloud_id = ; ----------------------------------------------------------------------------- ; xdebug.collect_assignments ; ; Type: boolean, Default value: false ; ; This setting, defaulting to 0, controls whether Xdebug should add variable ; assignments to function traces. Assign-by-var ( ``=&``) assignments are ; included too. ; ; ;xdebug.collect_assignments = false ; ----------------------------------------------------------------------------- ; xdebug.collect_params ; ; Introduced in version 3.3 ; ; Type: boolean, Default value: true ; ; If enabled (default), files created with the Function Trace feature will ; include all arguments to functions and methods. ; ; When disabled, the argument to each function and method will not be present in ; the trace files. ; ; ;xdebug.collect_params = true ; ----------------------------------------------------------------------------- ; xdebug.collect_return ; ; Type: boolean, Default value: false ; ; This setting, defaulting to 0, controls whether Xdebug should write the return ; value of function calls to the trace files. ; ; ;xdebug.collect_return = false ; ----------------------------------------------------------------------------- ; xdebug.connect_timeout_ms ; ; Type: integer, Default value: 200 ; ; The amount of time in milliseconds that Xdebug will wait for on an IDE to ; acknowledge an incoming debugging connection. The default value of 200 ms ; should in most cases be enough. In case you often get dropped debugging ; requests, perhaps because you have a high latency network, or a development ; box far away from your IDE, or have a slow firewall, then you can should ; increase this value. ; ; Please note that increasing this value might mean that your requests seem to ; 'hang' in case Xdebug tries to establish a connection, but your IDE is not ; listening. ; ; ;xdebug.connect_timeout_ms = 200 ; ----------------------------------------------------------------------------- ; xdebug.discover_client_host ; ; Type: boolean, Default value: false ; ; If enabled, Xdebug will first try to connect to the client that made the HTTP ; request. It checks the ``$_SERVER['HTTP_X_FORWARDED_FOR']`` and ; ``$_SERVER['REMOTE_ADDR']`` variables to find out which hostname or IP address ; to use. ; ; If xdebug.client_discovery_header is configured, then the ``$_SERVER`` ; variable with that configured name will be checked instead of the default ; variables. ; ; If Xdebug can not connect to a debugging client as found in one of the HTTP ; headers, it will fall back to the hostname or IP address as configured by the ; xdebug.client_host setting. ; ; This setting does not apply for debugging through the CLI, as the ``$_SERVER`` ; header variables are not available there. ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; .. warning:: ; ; Please note that there is no filter available, and anybody who can connect ; to the webserver will then be able to start a debugging session, even if ; their address does not match xdebug.client_host. ; ; ;xdebug.discover_client_host = false ; ----------------------------------------------------------------------------- ; xdebug.dump.* ; ; Type: string, Default value: Empty ; ; * can be any of COOKIE, FILES, GET, POST, REQUEST, SERVER, SESSION. These ; seven settings control which data from the superglobals is shown when an error ; situation occurs. ; ; Each of those php.ini setting can consist of a comma separated list of ; variables from this superglobal to dump, or ``*`` for all of them. Make sure ; you do not add spaces in this setting. ; ; In order to dump the REMOTE_ADDR and the REQUEST_METHOD when an error occurs, ; and all GET parameters, add these settings: ; ; xdebug.dump.SERVER = REMOTE_ADDR,REQUEST_METHOD ; xdebug.dump.GET = * ; ; ;xdebug.dump.* = Empty ; ----------------------------------------------------------------------------- ; xdebug.dump_globals ; ; Type: boolean, Default value: true ; ; When this setting is set to ``true``, Xdebug adds the values of the super ; globals as configured through the xdebug.dump.* to on-screen stack traces and ; the error log (if enabled). ; ; ;xdebug.dump_globals = true ; ----------------------------------------------------------------------------- ; xdebug.dump_once ; ; Type: boolean, Default value: true ; ; Controls whether the values of the superglobals should be dumped on all error ; situations (set to 0) or only on the first (set to 1). ; ; ;xdebug.dump_once = true ; ----------------------------------------------------------------------------- ; xdebug.dump_undefined ; ; Type: boolean, Default value: false ; ; If you want to dump undefined values from the superglobals you should set this ; setting to 1, otherwise leave it set to 0. ; ; ;xdebug.dump_undefined = false ; ----------------------------------------------------------------------------- ; xdebug.file_link_format ; ; Type: string, Default value: ; ; This setting determines the format of the links that are made in the display ; of stack traces where file names are used. This allows IDEs to set up a ; link-protocol that makes it possible to go directly to a line and file by ; clicking on the filenames that Xdebug shows in stack traces. An example format ; might look like: ; ; myide://%f@%l ; ; The possible format specifiers are: ; ; ========= =============== ; Specifier Meaning ; ========= =============== ; %f the filename ; --------- --------------- ; %l the line number ; ========= =============== ; ; For various IDEs/OSses there are some instructions listed on how to make this ; work: ; ; -------- ; PhpStorm ; -------- ; ; In the configuration file, add the following line, including the single ; quotes. This uses PhpStorm's REST API. ; ; xdebug.file_link_format='javascript: var r = new XMLHttpRequest; r.open("get", "http://localhost:63342/api/file/%f:%l");r.send()' ; ; ; ---------------- ; Firefox on Linux ; ---------------- ; ; - Open ; ; about:config ; ; - Add a new boolean setting "network.protocol-handler.expose.xdebug" and set ; it to "false" ; ; - Add the following into a shell script ; ; ``~/bin/ff-xdebug.sh``: ; ; #! /bin/sh ; ; f=`echo $1 | cut -d @ -f 1 | sed 's/xdebug:\/\///'` ; l=`echo $1 | cut -d @ -f 2` ; ; Add to that one of (depending whether you have komodo, gvim or netbeans): ; ; - komodo $f -l $l ; ; - gvim --remote-tab +$l $f ; ; - netbeans "$f:$l" ; ; - Make the script executable with ; ; chmod +x ~/bin/ff-xdebug.sh ; ; - Set the xdebug.file_link_format setting to ; ; xdebug://%f@%l ; ; -------------------- ; Windows and Netbeans ; -------------------- ; ; - Create the file ; ; ``netbeans.bat`` and save it in your path ( ``C:\Windows`` will work): ; ; @echo off ; setlocal enableextensions enabledelayedexpansion ; set NETBEANS=%1 ; set FILE=%~2 ; set FILE=!FILE:%%5C=\! ; %NETBEANS% --nosplash --console suppress --open "%FILE:~19%" ; nircmd win activate process netbeans.exe ; ; **Note:** Remove the last line if you don't have ``nircmd``. ; ; - Save the following code as ; ; ``netbeans_protocol.reg``: ; ; Windows Registry Editor Version 5.00 ; ; [HKEY_CLASSES_ROOT\netbeans] ; "URL Protocol"="" ; @="URL:Netbeans Protocol" ; ; [HKEY_CLASSES_ROOT\netbeans\DefaultIcon] ; @="\"C:\\Program Files\\NetBeans 7.1.1\\bin\\netbeans.exe,1\"" ; ; [HKEY_CLASSES_ROOT\netbeans\shell] ; ; [HKEY_CLASSES_ROOT\netbeans\shell\open] ; ; [HKEY_CLASSES_ROOT\netbeans\shell\open\command] ; @="\"C:\\Windows\\netbeans.bat\" \"C:\\Program Files\\NetBeans 7.1.1\\bin\\netbeans.exe\" \"%1\"" ; ; **Note:** Make sure to change the path to Netbeans (twice), as well as the ; ``netbeans.bat`` batch file if you saved it somewhere else than ; ``C:\Windows\``. ; ; - Double click on the ; ; ``netbeans_protocol.reg`` file to import it into the registry. ; ; - Set the xdebug.file_link_format setting to ; ; xdebug.file_link_format = ; "netbeans://open/?f=%f:%l" ; ; ;xdebug.file_link_format = ; ----------------------------------------------------------------------------- ; xdebug.filename_format ; ; Type: string, Default value: ...%s%n ; ; This setting determines the format with which Xdebug renders filenames in HTML ; stack traces (default: ``...%s%n``) and location information through the ; overloaded xdebug_var_dump() (default: ``%f``). ; ; The possible format specifiers are listed in this table. The example output is ; rendered according to the full path ; ``/var/www/vendor/mail/transport/mta.php``. ; ; ========= ============================================== =========================================================== ; Specifier Meaning Example Output ; ========= ============================================== =========================================================== ; %a Ancester: Two directory elements and filename mail/transport/mta.php ; --------- ---------------------------------------------- ----------------------------------------------------------- ; %f Full path /var/www/vendor/mail/transport/mta.php ; --------- ---------------------------------------------- ----------------------------------------------------------- ; %n Name: Only the file name mta.php ; --------- ---------------------------------------------- ----------------------------------------------------------- ; %p Parent: One directory element and the filename transport/mta.php ; --------- ---------------------------------------------- ----------------------------------------------------------- ; %s Directory separator / ; on Linux, OSX and other Unix-like systems, ``\`` on Windows ; ========= ============================================== =========================================================== ; ; ;xdebug.filename_format = ...%s%n ; ----------------------------------------------------------------------------- ; xdebug.force_display_errors ; ; Type: integer, Default value: 0 ; ; If this setting is set to ``1`` then errors will **always** be displayed, no ; matter what the setting of PHP's display_errors [1] is. ; ; [1] https://www.php.net/manual/errorfunc.configuration.php#ini.display-errors ; ; ;xdebug.force_display_errors = 0 ; ----------------------------------------------------------------------------- ; xdebug.force_error_reporting ; ; Type: integer, Default value: 0 ; ; This setting is a bitmask, like error_reporting [1]. This bitmask will be ; logically ORed with the bitmask represented by error_reporting [2] to dermine ; which errors should be displayed. This setting can only be made in php.ini and ; allows you to force certain errors from being shown no matter what an ; application does with ini_set() [3]. ; ; [1] https://www.php.net/manual/errorfunc.configuration.php#ini.error-reporting ; [2] https://www.php.net/manual/errorfunc.configuration.php#ini.error-reporting ; [3] https://www.php.net/manual/function.ini-set.php ; ; ;xdebug.force_error_reporting = 0 ; ----------------------------------------------------------------------------- ; xdebug.gc_stats_output_name ; ; Type: string, Default value: gcstats.%p ; ; This setting determines the name of the file that is used to dump garbage ; collection statistics into. The setting specifies the format with format ; specifiers, very similar to sprintf() and strftime(). There are several format ; specifiers that can be used to format the file name. ; ; See the xdebug.trace_output_name documentation for the supported specifiers. ; ; ;xdebug.gc_stats_output_name = gcstats.%p ; ----------------------------------------------------------------------------- ; xdebug.halt_level ; ; Type: integer, Default value: 0 ; ; This setting allows you to configure a mask that determines whether, and ; which, notices and/or warnings get converted to errors. You can configure ; notices and warnings that are generated by PHP, and notices and warnings that ; you generate yourself (by means of trigger_error()). For example, to convert ; the warning of strlen() (without arguments) to an error, you would do: ; ; ini_set('xdebug.halt_level', E_WARNING); ; strlen(); ; echo "Hi!\n"; ; ; Which will then result in the showing of the error message, and the abortion ; of the script. ``echo "Hi!\n";`` will not be executed. ; ; The setting is a bit mask, so to convert all notices and warnings into errors ; for all applications, you can set this in php.ini: ; ; xdebug.halt_level=E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE ; ; The bitmask only supports the four level that are mentioned above. ; ; ;xdebug.halt_level = 0 ; ----------------------------------------------------------------------------- ; xdebug.idekey ; ; Type: string, Default value: *complex* ; ; Controls which IDE Key Xdebug should pass on to the debugging client or proxy. ; The IDE Key is only important for use with the DBGp Proxy Tool, although some ; IDEs are incorrectly picky as to what its value is. ; ; The default is based on the ``DBGP_IDEKEY`` environment setting. If it is not ; present, the default falls back to an empty string. ; ; If this setting is set to a non-empty string, it selects its value over ; ``DBGP_IDEKEY`` environment variable as default value. ; ; The internal IDE Key also gets updated through debugging session management ; and overrides the value of this setting as is explained in the Step Debugging ; documentation. ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.idekey = *complex* ; ----------------------------------------------------------------------------- ; xdebug.log ; ; Type: string, Default value: ; ; Configures Xdebug's log file. ; ; Xdebug will log to this file all file creations issues, Step Debugging ; connection attempts, failures, and debug communication. ; ; Enable this functionality by setting the value to a absolute path. Make sure ; that the system user that PHP runs at (such as ``www-data`` if you are running ; with Apache) can create and write to the file. ; ; The file is opened in append-mode, and will therefore not be overwritten by ; default. There is no concurrency protection available. ; ; The log file will include any attempt that Xdebug makes to connect to an IDE: ; ; [2693358] Log opened at 2020-09-02 07:19:09.616195 ; [2693358] [Step Debug] INFO: Connecting to configured address/port: localhost:9003. ; [2693358] [Step Debug] ERR: Could not connect to debugging client. Tried: localhost:9003 (through xdebug.client_host/xdebug.client_port). ; [2693358] [Profiler] ERR: File '/foo/cachegrind.out.2693358' could not be opened. ; [2693358] [Profiler] WARN: /foo: No such file or directory ; [2693358] [Tracing] ERR: File '/foo/trace.1485761369' could not be opened. ; [2693358] [Tracing] WARN: /foo: No such file or directory ; [2693358] Log closed at 2020-09-02 07:19:09.617510 ; ; It includes the opening time ( ``2020-09-02 07:19:09.616195``), the ; IP/Hostname and port Xdebug is trying to connect to ( ``localhost:9003``), and ; whether it succeeded ( ``Connected to client``). The number in brackets ( ; ``[2693358]``) is the Process ID. ; ; It includes: ; ; [2693358] ; process ID in brackets ; ; 2020-09-02 07:19:09.616195 ; opening time ; ; For Step Debugging: ; ; INFO: Connecting to configured address/port: localhost:9003. ; ERR: Could not connect to debugging client. Tried: localhost:9003 (through xdebug.client_host/xdebug.client_port). ; ; For Profiling: ; ; ERR: File '/foo/cachegrind.out.2693358' could not be opened. ; WARN: /foo: No such file or directory ; ; For Function Trace: ; ; ERR: File '/foo/trace.1485761369' could not be opened. ; WARN: /foo: No such file or directory ; ; All warnings and errors are described on the Description of errors page, with ; detailed instructions on how to resolve the problem, if possible. All errors ; are always logged through PHP's internal logging mechanism (configured with ; error_log [1] in ``php.ini``). All warnings and errors also show up in the ; diagnostics log that you can view by calling xdebug_info(). ; ; [1] https://www.php.net/manual/en/errorfunc.configuration.php#ini.error-log ; ; --------------------------- ; Step Debugger Communication ; --------------------------- ; ; The debugging log can also log the communication between Xdebug and an IDE. ; This communication is in XML, and starts with the `` ; ; ; ; ; ; ; The ``fileuri`` attribute lists the entry point of your application, which can ; be useful to compare to ``breakpoint_set`` commands to see if path mappings ; are set-up correctly. ; ; Beyond the `` ; ; ; And continuation commands [1]: ; ; [1] /docs/dbgp#continuation-commands ; ; <- step_into -i 9 ; -> ; ; ; ; ; You can read about DBGP - A common debugger protocol specification at its ; dedicated documation page. ; ; The xdebug.log_level setting controls how much information is logged. ; ; .. warning:: ; ; Many Linux distributions now use systemd, which implements **private tmp** ; directories. This means that when PHP is run through a web server or as ; PHP-FPM, the ``/tmp`` directory is prefixed with something akin to: ; ; /tmp/systemd-private-ea3cfa882b4e478993e1994033fc5feb-apache.service-FfWZRg ; ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.log = ; ----------------------------------------------------------------------------- ; xdebug.log_level ; ; Type: integer, Default value: 7 ; ; Configures which logging messages should be added to the log file. ; ; The log file is configured with the xdebug.log setting. ; ; The following levels are supported: ; ; ===== ============= ================================ ; Level Name Example ; ===== ============= ================================ ; 0 Criticals Errors in the configuration ; ----- ------------- -------------------------------- ; 1 Errors Connection errors ; ----- ------------- -------------------------------- ; 3 Warnings Connection warnings ; ----- ------------- -------------------------------- ; 5 Communication Protocol messages ; ----- ------------- -------------------------------- ; **7** Information Information while connecting ; ----- ------------- -------------------------------- ; 10 Debug Breakpoint resolving information ; ===== ============= ================================ ; ; Criticals, errors, and warnings always show up in the diagnostics log that you ; can view by calling xdebug_info(). ; ; Criticals and errors are additionally logged through PHP's internal logging ; mechanism (configured with error_log [1] in ``php.ini``). ; ; [1] https://www.php.net/manual/en/errorfunc.configuration.php#ini.error-log ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.log_level = 7 ; ----------------------------------------------------------------------------- ; xdebug.max_nesting_level ; ; Type: integer, Default value: 512 ; ; Controls the protection mechanism for infinite recursion protection. The value ; of this setting is the maximum level of nested functions that are allowed ; before the script will be aborted. ; ; When the maximum nesting level is reached, an "Error [1]" exception is thrown. ; ; [1] https://www.php.net/manual/class.error.php ; ; Before Xdebug 3.3, the default value was ``256``. ; ; ;xdebug.max_nesting_level = 512 ; ----------------------------------------------------------------------------- ; xdebug.max_stack_frames ; ; Type: integer, Default value: -1 ; ; Controls how many stack frames are shown in stack traces, both on the command ; line during PHP error stack traces, as well as in the browser for HTML traces. ; ; ;xdebug.max_stack_frames = -1 ; ----------------------------------------------------------------------------- ; xdebug.mode ; ; Type: string, Default value: develop ; ; This setting controls which Xdebug features are enabled. ; ; .. note:: ; ; This setting can only be set in ``php.ini`` or files like ``99-xdebug.ini`` ; that are read when a PHP process starts (directly, or through php-fpm). You ; can not set this value in ``.htaccess`` and ``.user.ini`` files, which are ; read per-request, nor through ``php_admin_value`` as used in Apache VHOSTs ; and PHP-FPM pools. ; ; The following values are accepted: ; ; off ; Nothing is enabled. Xdebug does no work besides checking whether ; functionality is enabled. Use this setting if you want close to 0 ; overhead. ; ; develop ; Enables Development Helpers including the overloaded var_dump(). ; ; coverage ; Enables Code Coverage Analysis to generate code coverage reports, mainly ; in combination with ; ; PHPUnit [1]. ; ; debug ; Enables Step Debugging. This can be used to step through your code while ; it is running, and analyse values of variables. ; ; gcstats ; Enables Garbage Collection Statistics to collect statistics about PHP's ; Garbage Collection Mechanism. ; ; profile ; Enables Profiling, with which you can analyse performance bottlenecks with ; tools like ; ; KCacheGrind [2]. ; ; trace ; Enables the Function Trace feature, which allows you record every function ; call, including arguments, variable assignment, and return value that is ; made during a request to a file. ; ; You can enable multiple modes at the same time by comma separating their ; identifiers as value to xdebug.mode: ``xdebug.mode=develop,trace``. ; ; [1] https://phpunit.readthedocs.io/en/9.0/code-coverage-analysis.html ; [2] /docs/profiler#kcachegrind ; ; -------------------------------- ; XDEBUG_MODE environment variable ; -------------------------------- ; ; You can also set Xdebug's mode by setting the ``XDEBUG_MODE`` environment ; variable on the command-line; this will take precedence over the xdebug.mode ; setting, but will not change the value of the xdebug.mode setting. ; ; .. warning:: ; ; Some web servers have a configuration option to prevent environment ; variables from being propagated to PHP and Xdebug. For example, PHP-FPM has ; a ``clear_env`` [1] configuration setting that is ``on`` by default, which ; you will need to turn ``off`` if you want to use ``XDEBUG_MODE``. Make sure ; that your web server does not clean the environment, or specifically allows ; the ``XDEBUG_MODE`` environment variable to be passed on. [1] ; https://www.php.net/manual/en/install.fpm.configuration.php#clear-env ; ; ;xdebug.mode = develop ; ----------------------------------------------------------------------------- ; xdebug.output_dir ; ; Type: string, Default value: /tmp ; ; The directory where Xdebug will write tracing, profiling, and garbage ; collection statistics to. This directory needs to be writable for the system ; user with which PHP is running. ; ; This setting can be changed in ``php.ini``, ``.htaccess`` (and equivalent ; files), and within a PHP file with ``ini_set()``. ; ; In some cases (when profiling, or when xdebug.start_with_request= ``yes`` with ; tracing), Xdebug creates the file before the script runs. In that case, ; changes made through ``ini_set()`` will not be taken into account. ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.output_dir = /tmp ; ----------------------------------------------------------------------------- ; xdebug.profiler_append ; ; Type: integer, Default value: 0 ; ; When this setting is set to 1, profiler files will not be overwritten when a ; new request would map to the same file (depending on the ; xdebug.profiler_output_name setting. Instead the file will be appended to with ; the new profile. ; ; ;xdebug.profiler_append = 0 ; ----------------------------------------------------------------------------- ; xdebug.profiler_output_name ; ; Type: string, Default value: cachegrind.out.%p ; ; This setting determines the name of the file that is used to dump traces into. ; The setting specifies the format with format specifiers, very similar to ; sprintf() and strftime(). There are several format specifiers that can be used ; to format the file name. ; ; See the xdebug.trace_output_name documentation for the supported specifiers. ; ; .. note:: ; ; This setting can additionally be configured through the ; ``XDEBUG_CONFIG``environment variable [1]. [1] ; /docs/all_settings#XDEBUG_CONFIG ; ; ;xdebug.profiler_output_name = cachegrind.out.%p ; ----------------------------------------------------------------------------- ; xdebug.scream ; ; Type: boolean, Default value: false ; ; If this setting is 1, then Xdebug will disable the @ (shut-up) operator so ; that notices, warnings and errors are no longer hidden. ; ; ;xdebug.scream = false ; ----------------------------------------------------------------------------- ; xdebug.show_error_trace ; ; Type: integer, Default value: 0 ; ; When this setting is set to 1, Xdebug will show a stack trace whenever an ; Error is raised - even if this Error is actually caught. ; ; ;xdebug.show_error_trace = 0 ; ----------------------------------------------------------------------------- ; xdebug.show_exception_trace ; ; Type: integer, Default value: 0 ; ; When this setting is set to 1, Xdebug will show a stack trace whenever an ; Exception or Error is raised - even if this Exception or Error is actually ; caught. ; ; Error 'exceptions' were introduced in PHP 7. ; ; ;xdebug.show_exception_trace = 0 ; ----------------------------------------------------------------------------- ; xdebug.show_local_vars ; ; Type: integer, Default value: 0 ; ; When this setting is set to something != 0 Xdebug's generated stack dumps in ; error situations will also show all variables in the top-most scope. Beware ; that this might generate a lot of information, and is therefore turned off by ; default. ; ; ;xdebug.show_local_vars = 0 ; ----------------------------------------------------------------------------- ; xdebug.start_upon_error ; ; Type: string, Default value: default ; ; Step Debugging can be activated when a PHP Notice or Warning is emitted, or ; when a Throwable [1] (Exception/Error) is thrown, depending on the value of ; this setting: ; ; [1] https://www.php.net/manual/en/class.throwable.php ; ; yes ; Initialise a debugging session when a PHP Notice or Warning is emitted, or ; when a Throwable is thrown. ; ; no ; default ; Do not start a debugging session upon an error situation. ; ; ;xdebug.start_upon_error = default ; ----------------------------------------------------------------------------- ; xdebug.start_with_request ; ; Type: string, Default value: default ; ; A Function Trace, Garbage Collection Statistics, Profiling, or Step Debugging ; can be activated at the start of a PHP request. Whether this happens depends ; on the value of this setting: ; ; yes ; The functionality starts when the PHP request starts, and before any PHP ; code is run. ; ; For example xdebug.mode= ``trace`` and xdebug.start_with_request= ``yes`` ; starts a Function Trace for the whole request. ; ; no ; The functionality does not get activated when the request starts. ; ; You can still start a Function Trace with xdebug_start_trace(), or Garbage ; Collection Statistics with xdebug_start_gcstats(). ; ; Step Debugging and Profiling will never activate with this value. ; ; trigger ; The functionality only gets activated when a specific trigger is present ; when the request starts. ; ; The name of the trigger is ``XDEBUG_TRIGGER``, and Xdebug checks for its ; presence in either ``$_ENV`` (environment variable), ``$_GET`` or ; ``$_POST`` variable, or ``$_COOKIE`` (HTTP cookie name). ; ; There is a legacy fallback to a functionality specific trigger name: ; ``XDEBUG_PROFILE`` (for Profiling), ``XDEBUG_TRACE`` (for a Function ; Trace), and ``XDEBUG_SESSION`` (for Step Debugging). ; ; There is another legacy trigger for Step Debugging only. If you set the ; ``XDEBUG_CONFIG`` environment variable to any value, then the step ; debugger will also get activated. ; ; Debug session management for Step Debugging is also available through ; ``XDEBUG_SESSION_START``. ; ; With xdebug.trigger_value you can control which specific trigger value ; will activate the trigger. If xdebug.trigger_value is set to an empty ; string, **any** value will be accepted. ; ; In this mode it is also possible to activate Step Debugging with ; xdebug_break(). ; ; default ; The ``default`` value depends on xdebug.mode: ; ; - **debug** : ``trigger`` ; ; - **gcstats** : ``no`` ; ; - **profile** : ``yes`` ; ; - **trace** : ``trigger`` ; ; ;xdebug.start_with_request = default ; ----------------------------------------------------------------------------- ; xdebug.trace_format ; ; Type: integer, Default value: 0 ; ; The format of the trace file. ; ; ===== ============================================================================== ; Value Description ; ===== ============================================================================== ; 0 shows a human readable indented trace file with: ; ; *time index*, *memory usage*, *memory delta*, *level*, *function name*, ; *function parameters*, *filename* and *line number*. ; ----- ------------------------------------------------------------------------------ ; 1 writes a computer readable format which has two different records. There are ; different records for entering a stack frame, and leaving a stack frame. The ; table below lists the fields in each type of record. Fields are tab separated. ; ----- ------------------------------------------------------------------------------ ; 2 writes a trace formatted in (simple) HTML. ; ===== ============================================================================== ; ; Fields for the computerized format: ; ; =========== ===== ========== ========== ========== ============ ============= ========================================= =================================== ======== =========== ================ ============================================================ ; Record type 1 2 3 4 5 6 7 8 9 10 11 12 - ... ; =========== ===== ========== ========== ========== ============ ============= ========================================= =================================== ======== =========== ================ ============================================================ ; Entry level function # always '0' time index memory usage function name user-defined (1) or internal function (0) name of the include or require file filename line number no. of arguments arguments (as many as specified in field 11) - tab separated ; ----------- ----- ---------- ---------- ---------- ------------ ------------- ----------------------------------------- ----------------------------------- -------- ----------- ---------------- ------------------------------------------------------------ ; Exit level function # always '1' time index memory usage empty ; ----------- ----- ---------- ---------- ---------- ------------ ------------- ----------------------------------------- ----------------------------------- -------- ----------- ---------------- ------------------------------------------------------------ ; Return level function # always 'R' empty return value empty ; =========== ===== ========== ========== ========== ============ ============= ========================================= =================================== ======== =========== ================ ============================================================ ; ; See the introduction for Function Trace for a few examples. ; ; ;xdebug.trace_format = 0 ; ----------------------------------------------------------------------------- ; xdebug.trace_options ; ; Type: integer, Default value: 0 ; ; This settings accepts a bitfield to enable options: ; ; 1 ; Trace file data will be appended to an already existing file with the same ; name, instead of it being overwritten. ; ; 2 ; Switches the file format to a tab separated format. The format is ; described in the xdebug.trace_format setting as "format 1". ; ; 4 ; Switches to a file format that shows data as an HTML table ; ; 8 ; With this bit set, ; ; ``.xt`` is not added automatically to the end of trace file names. ; ; To combine multiple flags, you can use bitwise-OR ( ``|``). ; ; ``xdebug.trace_options=2|8`` enables both the tab separated format, and stops ; the addition of ``.xt`` to the end of the file name. ; ; ;xdebug.trace_options = 0 ; ----------------------------------------------------------------------------- ; xdebug.trace_output_name ; ; Type: string, Default value: trace.%c ; ; This setting determines the name of the file that is used to dump traces into. ; The setting specifies the format with format specifiers, very similar to ; sprintf() and strftime(). There are several format specifiers that can be used ; to format the file name. The '.xt' extension is always added automatically. ; ; The possible format specifiers are: ; ; ========= ====================================== ================= ==================================================== ; Specifier Meaning Example Format Example Filename ; ========= ====================================== ================= ==================================================== ; %c crc32 of the current working directory trace.%c trace.1258863198.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %p pid trace.%p trace.5174.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %r random number trace.%r trace.072db0.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %s script name 2 cachegrind.out.%s cachegrind.out._home_httpd_html_test_xdebug_test_php ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %t timestamp (seconds) trace.%t trace.1179434742.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %u timestamp (microseconds) trace.%u trace.1179434749_642382.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %H $_SERVER['HTTP_HOST'] trace.%H trace.kossu.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %R $_SERVER['REQUEST_URI'] trace.%R trace._test_xdebug_test_php_var=1_var2=2.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %U $_SERVER['UNIQUE_ID'] trace.%U trace.TRX4n38AAAEAAB9gBFkAAAAB.xt ; ; 3 ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %S session_id (from $_COOKIE if set) trace.%S trace.c70c1ec2375af58f74b390bbdd2a679d.xt ; --------- -------------------------------------- ----------------- ---------------------------------------------------- ; %% literal % trace.%% trace.%%.xt ; ========= ====================================== ================= ==================================================== ; ; 2 This one is only available for trace file names since Xdebug 2.6. ; ; 3 New in version 2.2. This one is set by Apache's mod_unique_id module [1] ; ; [1] http://httpd.apache.org/docs/current/mod/mod_unique_id.html ; ; ;xdebug.trace_output_name = trace.%c ; ----------------------------------------------------------------------------- ; xdebug.trigger_value ; ; Type: string, Default value: "" ; ; This setting can be used when xdebug.start_with_request is set to ``trigger``, ; which is the default for Step Debugging and Function Trace. ; ; In ``trigger`` mode, Xdebug will only start its functionality when the ; ``XDEBUG_TRIGGER`` is set in the environment, or when the ``XDEBUG_TRIGGER`` ; GET, POST, or COOKIE variable is set. ; ; The legacy names ``XDEBUG_SESSION`` (for Step Debugging), ``XDEBUG_PROFILE`` ; (for Profiling), and ``XDEBUG_TRACE`` (for Function Trace) can also be used ; instead of ``XDEBUG_TRIGGER``. ; ; Normally, Xdebug does not look at which value is actually used. If this ; setting is set to a non-empty string, then Xdebug will only trigger if the ; value matches the value of this setting. ; ; With the following settings: ; ; xdebug.mode=profile ; xdebug.start_with_request=trigger ; xdebug.trigger_value=StartProfileForMe ; ; Xdebug's profiler will only start when either the environment variable ; ``XDEBUG_TRIGGER`` is set to ``StartProfileForMe``, the GET or POST variable ; ``XDEBUG_TRIGGER`` is set to ``StartProfileForMe``, or when the cookie ; ``XDEBUG_TRIGGER`` has the value ``StartProfileForMe``. ; ; From Xdebug 3.1, it is possible to configure multiple values by using a comma ; separated list. In that case, Xdebug will trigger if the supplied value ; matches any of the entries that are configured through this setting: ; ; xdebug.trigger_value=StartDebuggerForMe,StartDebuggerForYou ; ; See also: ; ; xdebug.start_with_request#trigger ; For how the triggering mechanism works, and which environment and server ; variables Xdebug acts on. ; ; ;xdebug.trigger_value = "" ; ----------------------------------------------------------------------------- ; xdebug.use_compression ; ; Introduced in version 3.1 ; ; Type: boolean, Default value: true ; ; If enabled, the Function Trace and Profiling features will create GZip ; compressed files as output. This reduces diskspace. ; ; If GZip compression is not supported by Xdebug, because it was not compiled ; in, then Xdebug will add a warning to its log and xdebug_info() diagnostics ; section. ; ; It is enabled by default if Xdebug has GZip support, and disable if Xdebug ; does not have GZip support. ; ; The QCacheGrind tool that you can use to visualise profiling information does ; not support reading GZip compressed profile files, whereas KCacheGrind and ; PhpStorm do. If you are a QCacheGrind user, you should set ; xdebug.use_compression to ``false``. ; ; ;xdebug.use_compression = true ; ----------------------------------------------------------------------------- ; xdebug.var_display_max_children ; ; Type: integer, Default value: 128 ; ; Controls the amount of array children and object's properties are shown when ; variables are displayed with either xdebug_var_dump(), xdebug.show_local_vars ; or when making a Function Trace. ; ; To disable any limitation, use *-1* as value. ; ; This setting does not have any influence on the number of children that is ; send to the client through the Step Debugging feature. ; ; ;xdebug.var_display_max_children = 128 ; ----------------------------------------------------------------------------- ; xdebug.var_display_max_data ; ; Type: integer, Default value: 512 ; ; Controls the maximum string length that is shown when variables are displayed ; with either xdebug_var_dump(), xdebug.show_local_vars or when making a ; Function Trace. ; ; To disable any limitation, use *-1* as value. ; ; This setting does not have any influence on the number of children that is ; send to the client through the Step Debugging feature. ; ; ;xdebug.var_display_max_data = 512 ; ----------------------------------------------------------------------------- ; xdebug.var_display_max_depth ; ; Type: integer, Default value: 3 ; ; Controls how many nested levels of array elements and object properties are ; when variables are displayed with either xdebug_var_dump(), ; xdebug.show_local_vars or when making a Function Trace. ; ; The maximum value you can select is *1023*. You can also use *-1* as value to ; select this maximum number. ; ; This setting does not have any influence on the number of children that is ; send to the client through the Step Debugging feature. ; ; .. warning:: ; ; Setting the value to a high number could potentially result in PHP using up ; all the available memory, so use with caution. ; ; ;xdebug.var_display_max_depth = 3 xdebug-3.4.3/Makefile.frag0000664000175000017500000000516315011062311014636 0ustar derickderickinstall: $(all_targets) $(install_targets) show-install-instructions show-install-instructions: @echo @$(top_srcdir)/build/shtool echo -n -e %B @echo " +----------------------------------------------------------------------+" @echo " | |" @echo " | INSTALLATION INSTRUCTIONS |" @echo " | ========================= |" @echo " | |" @echo " | See https://xdebug.org/install.php#configure-php for instructions |" @echo " | on how to enable Xdebug for PHP. |" @echo " | |" @echo " | Documentation is available online as well: |" @echo " | - A list of all settings: https://xdebug.org/docs-settings.php |" @echo " | - A list of all functions: https://xdebug.org/docs-functions.php |" @echo " | - Profiling instructions: https://xdebug.org/docs-profiling2.php |" @echo " | - Remote debugging: https://xdebug.org/docs-debugger.php |" @echo " | |" @echo " | |" @echo " | NOTE: Please disregard the message |" @echo " | You should add \"extension=xdebug.so\" to php.ini |" @echo " | that is emitted by the PECL installer. This does not work for |" @echo " | Xdebug. |" @echo " | |" @echo " +----------------------------------------------------------------------+" @$(top_srcdir)/build/shtool echo -n -e %b @echo @echo findphp: @echo $(PHP_EXECUTABLE) clean-tests: rm -f tests/*.diff tests/*.exp tests/*.log tests/*.out tests/*.php tests/*.sh tests/*.mem test: @echo "Xdebug can not be tested with 'make test', please refer to:" @echo " https://github.com/xdebug/xdebug#testing" @echo test-coverage: $(MAKE) clean CCACHE_DISABLE=1 EXTRA_CFLAGS=--coverage $(MAKE) all TEST_PHP_ARGS="-n -d zend_extension=$(top_srcdir)/.libs/xdebug.so" php run-xdebug-tests.php test-coverage-lcov: test-coverage lcov -c --directory . --output-file $(top_srcdir)/.coverage.lcov test-coverage-html: test-coverage-lcov genhtml $(top_srcdir)/.coverage.lcov --output-directory=/tmp/html xdebug-3.4.3/CONTRIBUTING.rst0000664000175000017500000000675515011062311014731 0ustar derickderickContributing ============ Xdebug is hosted on Github. The source code can be browsed there and can be checked out with:: git clone https://github.com/xdebug/xdebug.git If you think you want to fix a bug or work on a new feature, then you need to follow the instructions below. Please reach out first to discuss your suggested changes as well. Initial Set-up -------------- - Fork Xdebug on GitHub. - Make sure you have configured your Author Name and Author Email with GIT. Xdebug doesn't accept contributions from accounts with unnatural names. - Clone the repository:: git clone git@github.com:{your username}/xdebug.git - Change into the ``xdebug`` repository:: cd xdebug - Add the original repository as ``upstream`` remote:: git remote add upstream https://github.com/xdebug/xdebug.git git fetch upstream - Add a tracking branch for Xdebug 3.3:: git checkout --track origin/xdebug_3_3 Branches -------- There are two branches in operation: ``master`` This is were all new feature Pull Requests should be targeted at ``xdebug_3_3`` This is were all bug fix Pull Requests should be targeted at. The maintainer will add them to ``master`` too when merging the Pull Request. Working on a Pull Request ------------------------- - Make sure that your ``master`` and ``xdebug_3_3`` branches are up to date with the ``upstream`` repository. - Create an issue in the `issue tracker `_ (if none exists yet). - Switch to the right target branch (``master`` for features, ``xdebug_3_3`` for bug fixes). - Create a feature branch:: git checkout -b issue{issue number}-{description} For example:: git checkout -b issue1893-crash-with-fiber - For a bug fix, write one or more test cases to verify that the problem currently exists, and also to define what the output should be. Xdebug uses PHP's `phpt tests `_. The ``README.rst`` file contains information on how to run the tests. Each of Xdebug's modes has a specific directory where to place tests. For example, for code coverage that is ``tests/coverage``. Test case names should follow the following pattern:: tests/{feature-group}bug0{issue-number}.phpt If you need more than one test, append ``-001`` after the issue number. Pull Requests without tests won't be accepted. - Fix and/or write the code. - Before you submit a PR, make sure each commit is a single logical unit. The main commit that implements the issue, should have as commit message ``Fixed issue #1893: `` followed by the Summary of the issue in the issue tracker. The message should state what the change was about. For example:: Fixed issue #1893: Crash with ext-fiber with xdebug.mode=coverage - Before you submit a PR, make sure to rebase first on the branch that you will be targeting, for example to rebase against the current bug fix branch:: git fetch upstream && git rebase upstream/xdebug_3_3 - Push your changes to your remote repository:: git push origin {branch-name} For example:: git push origin issue1893-crash-with-fiber - Once you're satisfied, generate a pull request. Make sure that the title is in one line, it's fine if it's a few characters larger than what GitHub likes. Do not let the title spill over into the description with ``...```. In the description, explain what you changed, and why, and how your solution is the right one. Feel free to include questions, and pointers to specific things that need close review. xdebug-3.4.3/README.rst0000664000175000017500000001437415011062311013753 0ustar derickderickXdebug ====== .. image:: https://github.com/xdebug/xdebug/workflows/Build/badge.svg :target: https://github.com/xdebug/xdebug/actions?query=workflow%3ABuild .. image:: https://ci.appveyor.com/api/projects/status/glp9xfsmt1p25nkn?svg=true :target: https://ci.appveyor.com/project/derickr/xdebug .. image:: https://circleci.com/gh/xdebug/xdebug/tree/master.svg?style=svg :target: https://circleci.com/gh/xdebug/xdebug .. image:: https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg :target: https://stand-with-ukraine.pp.ua/ Xdebug is a debugging tool for PHP. It provides step-debugging and a whole range of development helpers, such as stack traces, a code profiler, features to dump the full execution of your script to a file, and more. |Repography logo| / Recent activity |Time period| ----- |recent-activity_timeline| |recent-activity_prs| |recent-activity_words| |recent-activity_users| .. |Time period| image:: https://images.repography.com/0/xdebug/xdebug/recent-activity/ef1290ac5bfa674f07dcfa4f915ce6b3_badge.svg :alt: Time period :target: https://repography.com .. |Repography logo| image:: https://images.repography.com/logo.svg :alt: Repography logo :target: https://repography.com .. |recent-activity_timeline| image:: https://images.repography.com/0/xdebug/xdebug/recent-activity/ef1290ac5bfa674f07dcfa4f915ce6b3_timeline.svg :alt: Timeline graph :target: https://github.com/xdebug/xdebug/commits .. |recent-activity_prs| image:: https://images.repography.com/0/xdebug/xdebug/recent-activity/ef1290ac5bfa674f07dcfa4f915ce6b3_prs.svg :alt: Pull request status graph :target: https://github.com/xdebug/xdebug/pulls .. |recent-activity_words| image:: https://images.repography.com/0/xdebug/xdebug/recent-activity/ef1290ac5bfa674f07dcfa4f915ce6b3_words.svg :alt: Trending topics :target: https://github.com/xdebug/xdebug/commits .. |recent-activity_users| image:: https://images.repography.com/0/xdebug/xdebug/recent-activity/ef1290ac5bfa674f07dcfa4f915ce6b3_users.svg :alt: Top contributors :target: https://github.com/xdebug/xdebug/graphs/contributors Requirements ------------ Xdebug requires a `supported version `_ of PHP. For installation it requires the `pecl` tool (available through the `php-pear` package), unless your Linux distribution has an Xdebug package (`php-xdebug`). Installation ------------ On most Linux distributions you can install Xdebug through its package manager. You can also compile from source with the `pecl` tool through `pecl install xdebug`. The latter also works for MacOS as long as PHP is installed with Homebrew. On Windows, you need to `download `_ a binary. Use the `Wizard `_. Unless you have installed Xdebug with a package manager on Linux, you also need to add the following line to your `php.ini` file, or create a new Xdebug specific ini file `xdebug.ini` in the `conf.d` directory. In either case, it needs the following line added:: zend_extension=xdebug For more extensive installation instructions, see the documentation at https://xdebug.org/docs/install Configuration ------------- Most features in Xdebug have to be opted in into. Each feature has a specific opt-in. For example to use the `step debugger `_ you need to set `xdebug.remote_enable=1` in your configuration file. The step debugger requires an IDE (client), of which there are many `available `_. The documentation has instructions for each of Xdebug's features: https://xdebug.org/docs/ and a full list of `settings `_ is also available there. Contributing ------------ Xdebug is written in C, and extensive knowledge of PHP's internals is necessary to be able to contribute. Contributing guidance is available `separately `_. Before you begin to contribute, please reach out first. Either through email (address at the bottom), an issue in the `issue tracker `_ or preferably through IRC on Freenode's #xdebug channel. Testing ------- If you are familiar with compiling PHP extension from source, have a local checkout of Xdebug's GitHub repository, and have compiled Xdebug in that directory following the instructions under `installation `_ you can run Xdebug's tests by running:: php run-xdebug-tests.php The test framework requires that the PHP binary on the path has Xdebug loaded, with remote debugging enabled through `xdebug.mode=debug`. It is possible to skip remote debugging tests by exporting the `SKIP_DBGP_TESTS=1` environment variable. The `SKIP_UNPARALLEL_TESTS=1` can be used to skip tests that can not run in parallel environments, and the `SKIP_SLOW_TESTS=1` environment variable to skip slow tests. The `OPCACHE` environment variable can either be `yes` or `no` and controls whether the test framework enables or disables OpCache. Licensing --------- Xdebug is released under `The Xdebug License `_, which is based on `The PHP License `_. It is an Open Source license (though not explicitly endorsed by the Open Source Initiative). Further Reading --------------- Xdebug has extensive documentation on its `website `_. There are over a hundred settings and many functions documented. Please have a look through the wealth of information that Xdebug can provide to make your every day development with PHP easier. Support ------- For questions regarding Xdebug, please use `StackOverflow `_, and tag your question with `xdebug`. You can also find ad-hoc and sporadic support on IRC: ``freenode/#xdebug``. You can do that with your favourite client, or by using their `webchat `_. If you think that you encountered a bug, please file a detailed bug report at https://bugs.xdebug.org. You are required to create an account, this is so that you can be contacted for additional information and to keep out spam. Derick Rethans — derick@xdebug.org xdebug-3.4.3/run-xdebug-tests.php0000664000175000017500000042162515011062311016216 0ustar derickderick#!/usr/bin/env php | | Preston L. Bannister | | Marcus Boerger | | Derick Rethans | | Sander Roobol | | Andrea Faulds | | (based on version by: Stig Bakken ) | | (based on the PHP 3 test framework by Rasmus Lerdorf) | +----------------------------------------------------------------------+ */ /* $Id: ff8fc1a09b14846e2a5daa9b51cc2b6e97f3ac3c $ */ /* Temporary variables while this file is being refactored. */ /** @var ?JUnit */ $junit = null; /* End temporary variables. */ /* Let there be no top-level code beyond this point: * Only functions and classes, thanks! * * Minimum required PHP version: 7.4.0 */ function show_usage(): void { echo << Run up to simultaneous testing processes in parallel for quicker testing on systems with multiple logical processors. Note that this is experimental feature. -l Read the testfiles to be executed from . After the test has finished all failed tests are written to the same . If the list is empty and no further test is specified then all tests are executed (same as: -r -w ). -r Read the testfiles to be executed from . -w Write a list of all failed tests to . -a Same as -w but append rather then truncating . -W Write a list of all tests and their result status to . -c Look for php.ini in directory or use as ini. -n Pass -n option to the php binary (Do not use a php.ini). -d foo=bar Pass -d option to the php binary (Define INI entry foo with value 'bar'). -g Comma separated list of groups to show during test run (possible values: PASS, FAIL, XFAIL, XLEAK, SKIP, BORK, WARN, LEAK, REDIRECT). -m Test for memory leaks with Valgrind (equivalent to -M memcheck). -M Test for errors with Valgrind tool. -p Specify PHP executable to run. -P Use PHP_BINARY as PHP executable to run (default). -q Quiet, no user interaction (same as environment NO_INTERACTION). -s Write output to . -x Sets 'SKIP_SLOW_TESTS' environmental variable. --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. --verbose -v Verbose mode. --help -h This Help. --temp-source --temp-target [--temp-urlbase ] Write temporary files to by replacing from the filenames to generate with . In general you want to make the path to your source files and some patch in your web page hierarchy with pointing to . --keep-[all|php|skip|after|clean] Do not delete 'all' files, 'php' test file, 'skip', 'after', or 'clean' file. --set-timeout Set timeout for individual tests, where is the number of seconds. The default value is 60 seconds, or 300 seconds when testing for memory leaks. --context Sets the number of lines of surrounding context to print for diffs. The default value is 3. --show-[all|php|skip|clean|after|exp|diff|out|mem] Show 'all' files, 'php' test file, 'skip', 'after', or 'clean' file. You can also use this to show the output 'out', the expected result 'exp', the difference between them 'diff' or the valgrind log 'mem'. The result types get written independent of the log format, however 'diff' only exists when a test fails. --show-slow Show all tests that took longer than milliseconds to run. --no-clean Do not execute clean section if any. --color --no-color Do/Don't colorize the result type in the test result. --repeat [n] Run the tests multiple times in the same process and check the output of the last execution (CLI SAPI only). --bless Bless failed tests using scripts/dev/bless_tests.php. HELP; } /** * One function to rule them all, one function to find them, one function to * bring them all and in the darkness bind them. * This is the entry point and exit point überfunction. It contains all the * code that was previously found at the top level. It could and should be * refactored to be smaller and more manageable. */ function main(): void { /* This list was derived in a naïve mechanical fashion. If a member * looks like it doesn't belong, it probably doesn't; cull at will. */ global $DETAILED, $PHP_FAILED_TESTS, $SHOW_ONLY_GROUPS, $argc, $argv, $cfg, $cfgfiles, $cfgtypes, $conf_passed, $end_time, $environment, $exts_skipped, $exts_tested, $exts_to_test, $failed_tests_file, $ignored_by_ext, $ini_overwrites, $is_switch, $colorize, $log_format, $matches, $no_clean, $no_file_cache, $optionals, $pass_option_n, $pass_options, $pattern_match, $php, $php_cgi, $phpdbg, $preload, $redir_tests, $repeat, $result_tests_file, $slow_min_ms, $start_time, $switch, $temp_source, $temp_target, $test_cnt, $test_dirs, $test_files, $test_idx, $test_list, $test_results, $testfile, $user_tests, $valgrind, $sum_results, $shuffle, $file_cache, $num_repeats, $bless; // Parallel testing global $workers, $workerID; global $context_line_count; // Temporary for the duration of refactoring /** @var JUnit */ global $junit; define('IS_WINDOWS', substr(PHP_OS, 0, 3) == "WIN"); $workerID = 0; if (getenv("TEST_PHP_WORKER")) { $workerID = intval(getenv("TEST_PHP_WORKER")); run_worker(); return; } define('INIT_DIR', getcwd()); // Change into the PHP source directory. if (getenv('TEST_PHP_SRCDIR')) { @chdir(getenv('TEST_PHP_SRCDIR')); } define('TEST_PHP_SRCDIR', getcwd()); check_proc_open_function_exists(); // If timezone is not set, use UTC. if (ini_get('date.timezone') == '') { date_default_timezone_set('UTC'); } // Delete some security related environment variables putenv('SSH_CLIENT=deleted'); putenv('SSH_AUTH_SOCK=deleted'); putenv('SSH_TTY=deleted'); putenv('SSH_CONNECTION=deleted'); set_time_limit(0); ini_set('pcre.backtrack_limit', PHP_INT_MAX); init_output_buffers(); error_reporting(E_ALL); $environment = $_ENV ?? []; // Some configurations like php.ini-development set variables_order="GPCS" // not "EGPCS", in which case $_ENV is NOT populated. Detect if the $_ENV // was empty and handle it by explicitly populating through getenv(). if (empty($environment)) { $environment = getenv(); } if (empty($environment['TEMP'])) { $environment['TEMP'] = sys_get_temp_dir(); if (empty($environment['TEMP'])) { // For example, OpCache on Windows will fail in this case because // child processes (for tests) will not get a TEMP variable, so // GetTempPath() will fallback to c:\windows, while GetTempPath() // will return %TEMP% for parent (likely a different path). The // parent will initialize the OpCache in that path, and child will // fail to reattach to the OpCache because it will be using the // wrong path. die("TEMP environment is NOT set"); } else { if (count($environment) == 1) { // Not having other environment variables, only having TEMP, is // probably ok, but strange and may make a difference in the // test pass rate, so warn the user. echo "WARNING: Only 1 environment variable will be available to tests(TEMP environment variable)" . PHP_EOL; } } } if (IS_WINDOWS && empty($environment["SystemRoot"])) { $environment["SystemRoot"] = getenv("SystemRoot"); } $php = null; $php_cgi = null; $phpdbg = null; if (getenv('TEST_PHP_LOG_FORMAT')) { $log_format = strtoupper(getenv('TEST_PHP_LOG_FORMAT')); } else { $log_format = 'LEODS'; } // Check whether a detailed log is wanted. if (getenv('TEST_PHP_DETAILED')) { $DETAILED = getenv('TEST_PHP_DETAILED'); } else { $DETAILED = 0; } $junit = new JUnit($environment, $workerID); if (getenv('SHOW_ONLY_GROUPS')) { $SHOW_ONLY_GROUPS = explode(",", getenv('SHOW_ONLY_GROUPS')); } else { $SHOW_ONLY_GROUPS = []; } // Check whether user test dirs are requested. if (getenv('TEST_PHP_USER')) { $user_tests = explode(',', getenv('TEST_PHP_USER')); } else { $user_tests = []; } $exts_to_test = []; $ini_overwrites = [ 'output_handler=', 'open_basedir=', 'disable_functions=', 'output_buffering=Off', 'error_reporting=' . E_ALL, 'display_errors=1', 'display_startup_errors=1', 'log_errors=0', 'log_errors_max_len=0', 'html_errors=0', 'track_errors=0', 'report_memleaks=1', 'report_zend_debug=0', 'docref_root=', 'docref_ext=.html', 'error_prepend_string=', 'error_append_string=', 'auto_prepend_file=', 'auto_append_file=', 'ignore_repeated_errors=0', 'precision=14', 'serialize_precision=-1', 'memory_limit=128M', 'opcache.fast_shutdown=0', 'opcache.file_update_protection=0', 'opcache.revalidate_freq=0', 'opcache.jit_hot_loop=1', 'opcache.jit_hot_func=1', 'opcache.jit_hot_return=1', 'opcache.jit_hot_side_exit=1', 'zend.assertions=1', 'zend.exception_ignore_args=0', 'zend.exception_string_param_max_len=15', 'short_open_tag=0', 'pcre.jit=0', ]; $no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0'; define('TRAVIS_CI', (bool) getenv('TRAVIS')); // Determine the tests to be run. $test_files = []; $redir_tests = []; $test_results = []; $PHP_FAILED_TESTS = [ 'BORKED' => [], 'FAILED' => [], 'WARNED' => [], 'LEAKED' => [], 'XFAILED' => [], 'XLEAKED' => [], 'SLOW' => [] ]; // If parameters given assume they represent selected tests to run. $result_tests_file = false; $failed_tests_file = false; $pass_option_n = false; $pass_options = ''; $output_file = INIT_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt'; $just_save_results = false; $valgrind = null; $temp_source = null; $temp_target = null; $conf_passed = null; $no_clean = false; $colorize = true; if (function_exists('sapi_windows_vt100_support') && !sapi_windows_vt100_support(STDOUT, true)) { $colorize = false; } if (array_key_exists('NO_COLOR', $environment)) { $colorize = false; } $selected_tests = false; $slow_min_ms = INF; $preload = false; $file_cache = null; $shuffle = false; $bless = false; $workers = null; $context_line_count = 3; $num_repeats = 1; $cfgtypes = ['show', 'keep']; $cfgfiles = ['skip', 'php', 'after', 'clean', 'out', 'diff', 'exp', 'mem']; $cfg = []; foreach ($cfgtypes as $type) { $cfg[$type] = []; foreach ($cfgfiles as $file) { $cfg[$type][$file] = false; } } if (!isset($argc, $argv) || !$argc) { $argv = [__FILE__]; $argc = 1; } if (getenv('TEST_PHP_ARGS')) { $argv = array_merge($argv, explode(' ', getenv('TEST_PHP_ARGS'))); $argc = count($argv); } for ($i = 1; $i < $argc; $i++) { $is_switch = false; $switch = substr($argv[$i], 1, 1); $repeat = substr($argv[$i], 0, 1) == '-'; while ($repeat) { if (!$is_switch) { $switch = substr($argv[$i], 1, 1); } $is_switch = true; if ($repeat) { foreach ($cfgtypes as $type) { if (strpos($switch, '--' . $type) === 0) { foreach ($cfgfiles as $file) { if ($switch == '--' . $type . '-' . $file) { $cfg[$type][$file] = true; $is_switch = false; break; } } } } } if (!$is_switch) { $is_switch = true; break; } $repeat = false; switch ($switch) { case 'j': $workers = substr($argv[$i], 2); if (!preg_match('/^\d+$/', $workers) || $workers == 0) { error("'$workers' is not a valid number of workers, try e.g. -j16 for 16 workers"); } $workers = intval($workers, 10); // Don't use parallel testing infrastructure if there is only one worker. if ($workers === 1) { $workers = null; } break; case 'r': case 'l': $test_list = file($argv[++$i]); if ($test_list) { foreach ($test_list as $test) { $matches = []; if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { $redir_tests[] = [$matches[1], $matches[2]]; } else { if (strlen($test)) { $test_files[] = trim($test); } } } } if ($switch != 'l') { break; } $i--; // no break case 'w': $failed_tests_file = fopen($argv[++$i], 'w+t'); break; case 'a': $failed_tests_file = fopen($argv[++$i], 'a+t'); break; case 'W': $result_tests_file = fopen($argv[++$i], 'w+t'); break; case 'c': $conf_passed = $argv[++$i]; break; case 'd': $ini_overwrites[] = $argv[++$i]; break; case 'g': $SHOW_ONLY_GROUPS = explode(",", $argv[++$i]); break; //case 'h' case '--keep-all': foreach ($cfgfiles as $file) { $cfg['keep'][$file] = true; } break; //case 'l' case 'm': $valgrind = new RuntestsValgrind($environment); break; case 'M': $valgrind = new RuntestsValgrind($environment, $argv[++$i]); break; case 'n': if (!$pass_option_n) { $pass_options .= ' -n'; } $pass_option_n = true; break; case 'e': $pass_options .= ' -e'; break; case '--preload': $preload = true; $environment['SKIP_PRELOAD'] = 1; break; case '--file-cache-prime': $file_cache = 'prime'; break; case '--file-cache-use': $file_cache = 'use'; break; case '--no-clean': $no_clean = true; break; case '--color': $colorize = true; break; case '--no-color': $colorize = false; break; case 'p': $php = $argv[++$i]; putenv("TEST_PHP_EXECUTABLE=$php"); $environment['TEST_PHP_EXECUTABLE'] = $php; break; case 'P': $php = PHP_BINARY; putenv("TEST_PHP_EXECUTABLE=$php"); $environment['TEST_PHP_EXECUTABLE'] = $php; break; case 'q': putenv('NO_INTERACTION=1'); $environment['NO_INTERACTION'] = 1; break; //case 'r' case 's': $output_file = $argv[++$i]; $just_save_results = true; break; case '--set-timeout': $timeout = $argv[++$i] ?? ''; if (!preg_match('/^\d+$/', $timeout)) { error("'$timeout' is not a valid number of seconds, try e.g. --set-timeout 60 for 1 minute"); } $environment['TEST_TIMEOUT'] = intval($timeout, 10); break; case '--context': $context_line_count = $argv[++$i] ?? ''; if (!preg_match('/^\d+$/', $context_line_count)) { error("'$context_line_count' is not a valid number of lines of context, try e.g. --context 3 for 3 lines"); } $context_line_count = intval($context_line_count, 10); break; case '--show-all': foreach ($cfgfiles as $file) { $cfg['show'][$file] = true; } break; case '--show-slow': $slow_min_ms = $argv[++$i] ?? ''; if (!preg_match('/^\d+$/', $slow_min_ms)) { error("'$slow_min_ms' is not a valid number of milliseconds, try e.g. --show-slow 1000 for 1 second"); } $slow_min_ms = intval($slow_min_ms, 10); break; case '--temp-source': $temp_source = $argv[++$i]; break; case '--temp-target': $temp_target = $argv[++$i]; break; case 'v': case '--verbose': $DETAILED = true; break; case 'x': $environment['SKIP_SLOW_TESTS'] = 1; break; case '--offline': $environment['SKIP_ONLINE_TESTS'] = 1; break; case '--shuffle': $shuffle = true; break; case '--asan': case '--msan': $environment['USE_ZEND_ALLOC'] = 0; $environment['USE_TRACKED_ALLOC'] = 1; $environment['SKIP_ASAN'] = 1; $environment['SKIP_PERF_SENSITIVE'] = 1; if ($switch === '--msan') { $environment['SKIP_MSAN'] = 1; } $lsanSuppressions = __DIR__ . '/azure/lsan-suppressions.txt'; if (file_exists($lsanSuppressions)) { $environment['LSAN_OPTIONS'] = 'suppressions=' . $lsanSuppressions . ':print_suppressions=0'; } break; case '--repeat': $num_repeats = (int) $argv[++$i]; $environment['SKIP_REPEAT'] = 1; break; case '--bless': $bless = true; break; //case 'w' case '-': // repeat check with full switch $switch = $argv[$i]; if ($switch != '-') { $repeat = true; } break; case '--version': echo '$Id: ff8fc1a09b14846e2a5daa9b51cc2b6e97f3ac3c $' . "\n"; exit(1); default: echo "Illegal switch '$switch' specified!\n"; // no break case 'h': case '-help': case '--help': show_usage(); exit(1); } } if (!$is_switch) { $selected_tests = true; $testfile = realpath($argv[$i]); if (!$testfile && strpos($argv[$i], '*') !== false && function_exists('glob')) { if (substr($argv[$i], -5) == '.phpt') { $pattern_match = glob($argv[$i]); } else { if (preg_match("/\*$/", $argv[$i])) { $pattern_match = glob($argv[$i] . '.phpt'); } else { die('Cannot find test file "' . $argv[$i] . '".' . PHP_EOL); } } if (is_array($pattern_match)) { $test_files = array_merge($test_files, $pattern_match); } } else { if (is_dir($testfile)) { find_files($testfile); } else { if (substr($testfile, -5) == '.phpt') { $test_files[] = $testfile; } else { die('Cannot find test file "' . $argv[$i] . '".' . PHP_EOL); } } } } } if ($selected_tests && count($test_files) === 0) { echo "No tests found.\n"; return; } if (!$php) { $php = getenv('TEST_PHP_EXECUTABLE'); } if (!$php) { $php = PHP_BINARY; } if (!$php_cgi) { $php_cgi = getenv('TEST_PHP_CGI_EXECUTABLE'); } if (!$php_cgi) { $php_cgi = get_binary($php, 'php-cgi', 'sapi/cgi/php-cgi'); } if (!$phpdbg) { $phpdbg = getenv('TEST_PHPDBG_EXECUTABLE'); } if (!$phpdbg) { $phpdbg = get_binary($php, 'phpdbg', 'sapi/phpdbg/phpdbg'); } putenv("TEST_PHP_EXECUTABLE=$php"); $environment['TEST_PHP_EXECUTABLE'] = $php; putenv("TEST_PHP_CGI_EXECUTABLE=$php_cgi"); $environment['TEST_PHP_CGI_EXECUTABLE'] = $php_cgi; putenv("TEST_PHPDBG_EXECUTABLE=$phpdbg"); $environment['TEST_PHPDBG_EXECUTABLE'] = $phpdbg; if ($conf_passed !== null) { if (IS_WINDOWS) { $pass_options .= " -c " . escapeshellarg($conf_passed); } else { $pass_options .= " -c '" . realpath($conf_passed) . "'"; } } $test_files = array_unique($test_files); $test_files = array_merge($test_files, $redir_tests); // Run selected tests. $test_cnt = count($test_files); verify_config(); write_information(); if ($test_cnt) { putenv('NO_INTERACTION=1'); usort($test_files, "test_sort"); $start_time = time(); echo "Running selected tests.\n"; $test_idx = 0; run_all_tests($test_files, $environment); $end_time = time(); if ($failed_tests_file) { fclose($failed_tests_file); } if ($result_tests_file) { fclose($result_tests_file); } if (0 == count($test_results)) { echo "No tests were run.\n"; return; } compute_summary(); echo "====================================================================="; echo get_summary(false); if ($output_file != '' && $just_save_results) { save_results($output_file, /* prompt_to_save_results: */ false); } } else { // Compile a list of all test files (*.phpt). $test_files = []; $exts_tested = count($exts_to_test); $exts_skipped = 0; $ignored_by_ext = 0; sort($exts_to_test); $test_dirs = []; $optionals = ['Zend', 'tests', 'ext', 'sapi']; foreach ($optionals as $dir) { if (is_dir($dir)) { $test_dirs[] = $dir; } } // Convert extension names to lowercase foreach ($exts_to_test as $key => $val) { $exts_to_test[$key] = strtolower($val); } foreach ($test_dirs as $dir) { find_files(TEST_PHP_SRCDIR . "/{$dir}", $dir == 'ext'); } foreach ($user_tests as $dir) { find_files($dir, $dir == 'ext'); } $test_files = array_unique($test_files); usort($test_files, "test_sort"); $start_time = time(); show_start($start_time); $test_cnt = count($test_files); $test_idx = 0; run_all_tests($test_files, $environment); $end_time = time(); if ($failed_tests_file) { fclose($failed_tests_file); } if ($result_tests_file) { fclose($result_tests_file); } // Summarize results if (0 == count($test_results)) { echo "No tests were run.\n"; return; } compute_summary(); show_end($end_time); show_summary(); save_results($output_file, /* prompt_to_save_results: */ true); } $junit->saveXML(); if ($bless) { bless_failed_tests($PHP_FAILED_TESTS['FAILED']); } if (getenv('REPORT_EXIT_STATUS') !== '0' && getenv('REPORT_EXIT_STATUS') !== 'no' && ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['LEAKED'])) { exit(1); } } if (!function_exists("hrtime")) { /** * @return array|float|int */ function hrtime(bool $as_num = false) { $t = microtime(true); if ($as_num) { return $t * 1000000000; } $s = floor($t); return [0 => $s, 1 => ($t - $s) * 1000000000]; } } function verify_config(): void { global $php; if (empty($php) || !file_exists($php)) { error('environment variable TEST_PHP_EXECUTABLE must be set to specify PHP executable!'); } if (!is_executable($php)) { error("invalid PHP executable specified by TEST_PHP_EXECUTABLE = $php"); } } function write_information(): void { global $php, $php_cgi, $phpdbg, $php_info, $user_tests, $ini_overwrites, $pass_options, $exts_to_test, $valgrind, $no_file_cache; // Get info from php $info_file = __DIR__ . '/run-test-info.php'; @unlink($info_file); $php_info = ''; save_text($info_file, $php_info); $info_params = []; settings2array($ini_overwrites, $info_params); $info_params = settings2params($info_params); $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`; define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`); if ($php_cgi && $php != $php_cgi) { $php_info_cgi = `$php_cgi $pass_options $info_params $no_file_cache -q "$info_file"`; $php_info_sep = "\n---------------------------------------------------------------------"; $php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep"; } else { $php_cgi_info = ''; } if ($phpdbg) { $phpdbg_info = `$phpdbg $pass_options $info_params $no_file_cache -qrr "$info_file"`; $php_info_sep = "\n---------------------------------------------------------------------"; $phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep"; } else { $phpdbg_info = ''; } if (function_exists('opcache_invalidate')) { opcache_invalidate($info_file, true); } @unlink($info_file); // load list of enabled and loadable extensions save_text($info_file, <<<'PHP' PHP); $exts_to_test = explode(',', `$php $pass_options $info_params $no_file_cache "$info_file"`); // check for extensions that need special handling and regenerate $info_params_ex = [ 'session' => ['session.auto_start=0'], 'tidy' => ['tidy.clean_output=0'], 'zlib' => ['zlib.output_compression=Off'], 'xdebug' => ['xdebug.mode=off','xdebug.start_with_request=default','xdebug.log_level=20'], ]; foreach ($info_params_ex as $ext => $ini_overwrites_ex) { if (in_array($ext, $exts_to_test)) { $ini_overwrites = array_merge($ini_overwrites, $ini_overwrites_ex); } } if (function_exists('opcache_invalidate')) { opcache_invalidate($info_file, true); } @unlink($info_file); // Write test context information. echo " ===================================================================== PHP : $php $php_info $php_cgi_info $phpdbg_info CWD : " . TEST_PHP_SRCDIR . " Extra dirs : "; foreach ($user_tests as $test_dir) { echo "{$test_dir}\n "; } echo " VALGRIND : " . ($valgrind ? $valgrind->getHeader() : 'Not used') . " ===================================================================== "; } function save_results(string $output_file, bool $prompt_to_save_results): void { global $sum_results, $failed_test_summary, $PHP_FAILED_TESTS, $php; if (getenv('NO_INTERACTION') || TRAVIS_CI) { return; } if ($prompt_to_save_results) { /* We got failed Tests, offer the user to save a QA report */ $fp = fopen("php://stdin", "r+"); if ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['WARNED'] || $sum_results['LEAKED']) { echo "\nYou may have found a problem in PHP."; } echo "\nThis report can be saved and used to open an issue on the bug tracker at\n"; echo "https://github.com/php/php-src/issues\n"; echo "This gives us a better understanding of PHP's behavior.\n"; echo "Do you want to save this report in a file? [Yn]: "; flush(); $user_input = fgets($fp, 10); fclose($fp); if (!(strlen(trim($user_input)) == 0 || strtolower($user_input[0]) == 'y')) { return; } } /** * Collect information about the host system for our report * Fetch phpinfo() output so that we can see the PHP environment * Make an archive of all the failed tests */ $failed_tests_data = ''; $sep = "\n" . str_repeat('=', 80) . "\n"; $failed_tests_data .= $failed_test_summary . "\n"; $failed_tests_data .= get_summary(true) . "\n"; if ($sum_results['FAILED']) { foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) { $failed_tests_data .= $sep . $test_info['name'] . $test_info['info']; $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output'])); $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff'])); $failed_tests_data .= $sep . "\n\n"; } } $failed_tests_data .= "\n" . $sep . 'BUILD ENVIRONMENT' . $sep; $failed_tests_data .= "OS:\n" . PHP_OS . " - " . php_uname() . "\n\n"; $ldd = $autoconf = $sys_libtool = $libtool = $compiler = 'N/A'; if (!IS_WINDOWS) { /* If PHP_AUTOCONF is set, use it; otherwise, use 'autoconf'. */ if (getenv('PHP_AUTOCONF')) { $autoconf = shell_exec(getenv('PHP_AUTOCONF') . ' --version'); } else { $autoconf = shell_exec('autoconf --version'); } /* Always use the generated libtool - Mac OSX uses 'glibtool' */ $libtool = shell_exec(INIT_DIR . '/libtool --version'); /* Use shtool to find out if there is glibtool present (MacOSX) */ $sys_libtool_path = shell_exec(__DIR__ . '/build/shtool path glibtool libtool'); if ($sys_libtool_path) { $sys_libtool = shell_exec(str_replace("\n", "", $sys_libtool_path) . ' --version'); } /* Try the most common flags for 'version' */ $flags = ['-v', '-V', '--version']; $cc_status = 0; foreach ($flags as $flag) { system(getenv('CC') . " $flag >/dev/null 2>&1", $cc_status); if ($cc_status == 0) { $compiler = shell_exec(getenv('CC') . " $flag 2>&1"); break; } } $ldd = shell_exec("ldd $php 2>/dev/null"); } $failed_tests_data .= "Autoconf:\n$autoconf\n"; $failed_tests_data .= "Bundled Libtool:\n$libtool\n"; $failed_tests_data .= "System Libtool:\n$sys_libtool\n"; $failed_tests_data .= "Compiler:\n$compiler\n"; $failed_tests_data .= "Bison:\n" . shell_exec('bison --version 2>/dev/null') . "\n"; $failed_tests_data .= "Libraries:\n$ldd\n"; $failed_tests_data .= "\n"; $failed_tests_data .= $sep . "PHPINFO" . $sep; $failed_tests_data .= shell_exec($php . ' -ddisplay_errors=stderr -dhtml_errors=0 -i 2> /dev/null'); file_put_contents($output_file, $failed_tests_data); echo "Report saved to: ", $output_file, "\n"; } function get_binary(string $php, string $sapi, string $sapi_path): ?string { $dir = dirname($php); if (IS_WINDOWS && file_exists("$dir/$sapi.exe")) { return realpath("$dir/$sapi.exe"); } // Sources tree if (file_exists("$dir/../../$sapi_path")) { return realpath("$dir/../../$sapi_path"); } // Installation tree, preserve command prefix/suffix $inst = str_replace('php', $sapi, basename($php)); if (file_exists("$dir/$inst")) { return realpath("$dir/$inst"); } return null; } function find_files(string $dir, bool $is_ext_dir = false, bool $ignore = false): void { global $test_files, $exts_to_test, $ignored_by_ext, $exts_skipped; $o = opendir($dir) or error("cannot open directory: $dir"); while (($name = readdir($o)) !== false) { if (is_dir("{$dir}/{$name}") && !in_array($name, ['.', '..', '.svn'])) { $skip_ext = ($is_ext_dir && !in_array(strtolower($name), $exts_to_test)); if ($skip_ext) { $exts_skipped++; } find_files("{$dir}/{$name}", false, $ignore || $skip_ext); } // Cleanup any left-over tmp files from last run. if (substr($name, -4) == '.tmp') { @unlink("$dir/$name"); continue; } // Otherwise we're only interested in *.phpt files. // (but not those starting with a dot, which are hidden on // many platforms) if (substr($name, -5) == '.phpt' && substr($name, 0, 1) !== '.') { if ($ignore) { $ignored_by_ext++; } else { $testfile = realpath("{$dir}/{$name}"); $test_files[] = $testfile; } } } closedir($o); } /** * @param array|string $name */ function test_name($name): string { if (is_array($name)) { return $name[0] . ':' . $name[1]; } else { return $name; } } /** * @param array|string $a * @param array|string $b */ function test_sort($a, $b): int { $a = test_name($a); $b = test_name($b); $ta = strpos($a, TEST_PHP_SRCDIR . "/tests") === 0 ? 1 + (strpos($a, TEST_PHP_SRCDIR . "/tests/run-test") === 0 ? 1 : 0) : 0; $tb = strpos($b, TEST_PHP_SRCDIR . "/tests") === 0 ? 1 + (strpos($b, TEST_PHP_SRCDIR . "/tests/run-test") === 0 ? 1 : 0) : 0; if ($ta == $tb) { return strcmp($a, $b); } else { return $tb - $ta; } } // // Write the given text to a temporary file, and return the filename. // function save_text(string $filename, string $text, ?string $filename_copy = null): void { global $DETAILED; if ($filename_copy && $filename_copy != $filename) { if (file_put_contents($filename_copy, $text) === false) { error("Cannot open file '" . $filename_copy . "' (save_text)"); } } if (file_put_contents($filename, $text) === false) { error("Cannot open file '" . $filename . "' (save_text)"); } if (1 < $DETAILED) { echo " FILE $filename {{{ $text }}} "; } } // // Write an error in a format recognizable to Emacs or MSVC. // function error_report(string $testname, string $logname, string $tested): void { $testname = realpath($testname); $logname = realpath($logname); switch (strtoupper(getenv('TEST_PHP_ERROR_STYLE'))) { case 'MSVC': echo $testname . "(1) : $tested\n"; echo $logname . "(1) : $tested\n"; break; case 'EMACS': echo $testname . ":1: $tested\n"; echo $logname . ":1: $tested\n"; break; } } /** * @return false|string */ function system_with_timeout( string $commandline, ?array $env = null, ?string $stdin = null, bool $captureStdIn = true, bool $captureStdOut = true, bool $captureStdErr = true ) { global $valgrind; $data = ''; $bin_env = []; foreach ((array) $env as $key => $value) { $bin_env[$key] = $value; } $descriptorspec = []; if ($captureStdIn) { $descriptorspec[0] = ['pipe', 'r']; } if ($captureStdOut) { $descriptorspec[1] = ['pipe', 'w']; } if ($captureStdErr) { $descriptorspec[2] = ['pipe', 'w']; } $proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, ['suppress_errors' => true]); if (!$proc) { return false; } if ($captureStdIn) { if (!is_null($stdin)) { fwrite($pipes[0], $stdin); } fclose($pipes[0]); unset($pipes[0]); } $timeout = $valgrind ? 300 : ($env['TEST_TIMEOUT'] ?? 60); while (true) { /* hide errors from interrupted syscalls */ $r = $pipes; $w = null; $e = null; $n = @stream_select($r, $w, $e, $timeout); if ($n === false) { break; } elseif ($n === 0) { /* timed out */ $data .= "\n ** ERROR: process timed out **\n"; proc_terminate($proc, 9); return $data; } elseif ($n > 0) { if ($captureStdOut) { $line = fread($pipes[1], 8192); } elseif ($captureStdErr) { $line = fread($pipes[2], 8192); } else { $line = ''; } if (strlen($line) == 0) { /* EOF */ break; } $data .= $line; } } $stat = proc_get_status($proc); if ($stat['signaled']) { $data .= "\nTermsig=" . $stat['stopsig'] . "\n"; } if ($stat["exitcode"] > 128 && $stat["exitcode"] < 160) { $data .= "\nTermsig=" . ($stat["exitcode"] - 128) . "\n"; } else if (defined('PHP_WINDOWS_VERSION_MAJOR') && (($stat["exitcode"] >> 28) & 0b1111) === 0b1100) { // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781 $data .= "\nTermsig=" . $stat["exitcode"] . "\n"; } proc_close($proc); return $data; } /** * @param string|array|null $redir_tested */ function run_all_tests(array $test_files, array $env, $redir_tested = null): void { global $test_results, $failed_tests_file, $result_tests_file, $php, $test_idx, $file_cache; global $preload; // Parallel testing global $PHP_FAILED_TESTS, $workers, $workerID, $workerSock; if ($file_cache !== null || $preload) { /* Automatically skip opcache tests in --file-cache and --preload mode, * because opcache generally expects these to run under a default configuration. */ $test_files = array_filter($test_files, function($test) use($preload) { if (!is_string($test)) { return true; } if (false !== strpos($test, 'ext/opcache')) { return false; } if ($preload && false !== strpos($test, 'ext/zend_test/tests/observer')) { return false; } return true; }); } /* Ignore -jN if there is only one file to analyze. */ if ($workers !== null && count($test_files) > 1 && !$workerID) { run_all_tests_parallel($test_files, $env, $redir_tested); return; } foreach ($test_files as $name) { if (is_array($name)) { $index = "# $name[1]: $name[0]"; if ($redir_tested) { $name = $name[0]; } } elseif ($redir_tested) { $index = "# $redir_tested: $name"; } else { $index = $name; } $test_idx++; if ($workerID) { $PHP_FAILED_TESTS = ['BORKED' => [], 'FAILED' => [], 'WARNED' => [], 'LEAKED' => [], 'XFAILED' => [], 'XLEAKED' => [], 'SLOW' => []]; ob_start(); } $result = run_test($php, $name, $env); if ($workerID) { $resultText = ob_get_clean(); } if (!is_array($name) && $result != 'REDIR') { if ($workerID) { send_message($workerSock, [ "type" => "test_result", "name" => $name, "index" => $index, "result" => $result, "text" => $resultText, "PHP_FAILED_TESTS" => $PHP_FAILED_TESTS ]); continue; } $test_results[$index] = $result; if ($failed_tests_file && ($result == 'XFAILED' || $result == 'XLEAKED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) { fwrite($failed_tests_file, "$index\n"); } if ($result_tests_file) { fwrite($result_tests_file, "$result\t$index\n"); } } } } /** The heart of parallel testing. * @param string|array|null $redir_tested */ function run_all_tests_parallel(array $test_files, array $env, $redir_tested): void { global $workers, $test_idx, $test_cnt, $test_results, $failed_tests_file, $result_tests_file, $PHP_FAILED_TESTS, $shuffle, $SHOW_ONLY_GROUPS, $valgrind; global $junit; // The PHP binary running run-tests.php, and run-tests.php itself // This PHP executable is *not* necessarily the same as the tested version $thisPHP = PHP_BINARY; $thisScript = __FILE__; $workerProcs = []; $workerSocks = []; // Each test may specify a list of conflict keys. While a test that conflicts with // key K is running, no other test that conflicts with K may run. Conflict keys are // specified either in the --CONFLICTS-- section, or CONFLICTS file inside a directory. $dirConflictsWith = []; $fileConflictsWith = []; $sequentialTests = []; foreach ($test_files as $i => $file) { $contents = file_get_contents($file); if (preg_match('/^--CONFLICTS--(.+?)^--/ms', $contents, $matches)) { $conflicts = parse_conflicts($matches[1]); } else { // Cache per-directory conflicts in a separate map, so we compute these only once. $dir = dirname($file); if (!isset($dirConflictsWith[$dir])) { $dirConflicts = []; if (file_exists($dir . '/CONFLICTS')) { $contents = file_get_contents($dir . '/CONFLICTS'); $dirConflicts = parse_conflicts($contents); } $dirConflictsWith[$dir] = $dirConflicts; } $conflicts = $dirConflictsWith[$dir]; } // For tests conflicting with "all", no other tests may run in parallel. We'll run these // tests separately at the end, when only one worker is left. if (in_array('all', $conflicts, true)) { $sequentialTests[] = $file; unset($test_files[$i]); } $fileConflictsWith[$file] = $conflicts; } // Some tests assume that they are executed in a certain order. We will be popping from // $test_files, so reverse its order here. This makes sure that order is preserved at least // for tests with a common conflict key. $test_files = array_reverse($test_files); // To discover parallelization issues it is useful to randomize the test order. if ($shuffle) { shuffle($test_files); } // Don't start more workers than test files. $workers = max(1, min($workers, count($test_files))); echo "Spawning $workers workers... "; // We use sockets rather than STDIN/STDOUT for comms because on Windows, // those can't be non-blocking for some reason. $listenSock = stream_socket_server("tcp://127.0.0.1:0") or error("Couldn't create socket on localhost."); $sockName = stream_socket_get_name($listenSock, false); // PHP is terrible and returns IPv6 addresses not enclosed by [] $portPos = strrpos($sockName, ":"); $sockHost = substr($sockName, 0, $portPos); if (false !== strpos($sockHost, ":")) { $sockHost = "[$sockHost]"; } $sockPort = substr($sockName, $portPos + 1); $sockUri = "tcp://$sockHost:$sockPort"; $totalFileCount = count($test_files); $startTime = microtime(true); for ($i = 1; $i <= $workers; $i++) { $proc = proc_open( [$thisPHP, $thisScript], [], // Inherit our stdin, stdout and stderr $pipes, null, $GLOBALS['environment'] + [ "TEST_PHP_WORKER" => $i, "TEST_PHP_URI" => $sockUri, ], [ "suppress_errors" => true, 'create_new_console' => true, ] ); if ($proc === false) { kill_children($workerProcs); error("Failed to spawn worker $i"); } $workerProcs[$i] = $proc; } for ($i = 1; $i <= $workers; $i++) { $workerSock = stream_socket_accept($listenSock, 5); if ($workerSock === false) { kill_children($workerProcs); error("Failed to accept connection from worker."); } $greeting = base64_encode(serialize([ "type" => "hello", "GLOBALS" => $GLOBALS, "constants" => [ "INIT_DIR" => INIT_DIR, "TEST_PHP_SRCDIR" => TEST_PHP_SRCDIR, "TRAVIS_CI" => TRAVIS_CI ] ])) . "\n"; stream_set_timeout($workerSock, 5); if (fwrite($workerSock, $greeting) === false) { kill_children($workerProcs); error("Failed to send greeting to worker."); } $rawReply = fgets($workerSock); if ($rawReply === false) { kill_children($workerProcs); error("Failed to read greeting reply from worker."); } $reply = unserialize(base64_decode($rawReply)); if (!$reply || $reply["type"] !== "hello_reply") { kill_children($workerProcs); error("Greeting reply from worker unexpected or could not be decoded: '$rawReply'"); } stream_set_timeout($workerSock, 0); stream_set_blocking($workerSock, false); $workerID = $reply["workerID"]; $workerSocks[$workerID] = $workerSock; } printf("Done in %.2fs\n", microtime(true) - $startTime); echo "=====================================================================\n"; echo "\n"; $rawMessageBuffers = []; $testsInProgress = 0; // Map from conflict key to worker ID. $activeConflicts = []; // Tests waiting due to conflicts. Map from conflict key to array. $waitingTests = []; escape: while ($test_files || $sequentialTests || $testsInProgress > 0) { $toRead = array_values($workerSocks); $toWrite = null; $toExcept = null; if (stream_select($toRead, $toWrite, $toExcept, 10)) { foreach ($toRead as $workerSock) { $i = array_search($workerSock, $workerSocks); if ($i === false) { kill_children($workerProcs); error("Could not find worker stdout in array of worker stdouts, THIS SHOULD NOT HAPPEN."); } while (false !== ($rawMessage = fgets($workerSock))) { // work around fgets truncating things if (($rawMessageBuffers[$i] ?? '') !== '') { $rawMessage = $rawMessageBuffers[$i] . $rawMessage; $rawMessageBuffers[$i] = ''; } if (substr($rawMessage, -1) !== "\n") { $rawMessageBuffers[$i] = $rawMessage; continue; } $message = unserialize(base64_decode($rawMessage)); if (!$message) { kill_children($workerProcs); $stuff = fread($workerSock, 65536); error("Could not decode message from worker $i: '$rawMessage$stuff'"); } switch ($message["type"]) { case "tests_finished": $testsInProgress--; foreach ($activeConflicts as $key => $workerId) { if ($workerId === $i) { unset($activeConflicts[$key]); if (isset($waitingTests[$key])) { while ($test = array_pop($waitingTests[$key])) { $test_files[] = $test; } unset($waitingTests[$key]); } } } $junit->mergeResults($message["junit"]); // no break case "ready": // Schedule sequential tests only once we are down to one worker. if (count($workerProcs) === 1 && $sequentialTests) { $test_files = array_merge($test_files, $sequentialTests); $sequentialTests = []; } // Batch multiple tests to reduce communication overhead. // - When valgrind is used, communication overhead is relatively small, // so just use a batch size of 1. // - If this is running a small enough number of tests, // reduce the batch size to give batches to more workers. $files = []; $maxBatchSize = $valgrind ? 1 : ($shuffle ? 4 : 32); $averageFilesPerWorker = max(1, (int) ceil($totalFileCount / count($workerProcs))); $batchSize = min($maxBatchSize, $averageFilesPerWorker); while (count($files) <= $batchSize && $file = array_pop($test_files)) { foreach ($fileConflictsWith[$file] as $conflictKey) { if (isset($activeConflicts[$conflictKey])) { $waitingTests[$conflictKey][] = $file; continue 2; } } $files[] = $file; } if ($files) { foreach ($files as $file) { foreach ($fileConflictsWith[$file] as $conflictKey) { $activeConflicts[$conflictKey] = $i; } } $testsInProgress++; send_message($workerSocks[$i], [ "type" => "run_tests", "test_files" => $files, "env" => $env, "redir_tested" => $redir_tested ]); } else { proc_terminate($workerProcs[$i]); unset($workerProcs[$i]); unset($workerSocks[$i]); goto escape; } break; case "test_result": list($name, $index, $result, $resultText) = [$message["name"], $message["index"], $message["result"], $message["text"]]; foreach ($message["PHP_FAILED_TESTS"] as $category => $tests) { $PHP_FAILED_TESTS[$category] = array_merge($PHP_FAILED_TESTS[$category], $tests); } $test_idx++; if (!$SHOW_ONLY_GROUPS) { clear_show_test(); } echo $resultText; if (!$SHOW_ONLY_GROUPS) { show_test($test_idx, count($workerProcs) . "/$workers concurrent test workers running"); } if (!is_array($name) && $result != 'REDIR') { $test_results[$index] = $result; if ($failed_tests_file && ($result == 'XFAILED' || $result == 'XLEAKED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) { fwrite($failed_tests_file, "$index\n"); } if ($result_tests_file) { fwrite($result_tests_file, "$result\t$index\n"); } } break; case "error": kill_children($workerProcs); error("Worker $i reported error: $message[msg]"); break; case "php_error": kill_children($workerProcs); $error_consts = [ 'E_ERROR', 'E_WARNING', 'E_PARSE', 'E_NOTICE', 'E_CORE_ERROR', 'E_CORE_WARNING', 'E_COMPILE_ERROR', 'E_COMPILE_WARNING', 'E_USER_ERROR', 'E_USER_WARNING', 'E_USER_NOTICE', 'E_STRICT', // TODO Cleanup when removed from Zend Engine. 'E_RECOVERABLE_ERROR', 'E_DEPRECATED', 'E_USER_DEPRECATED' ]; $error_consts = array_combine(array_map('constant', $error_consts), $error_consts); error("Worker $i reported unexpected {$error_consts[$message['errno']]}: $message[errstr] in $message[errfile] on line $message[errline]"); // no break default: kill_children($workerProcs); error("Unrecognised message type '$message[type]' from worker $i"); } } } } } if (!$SHOW_ONLY_GROUPS) { clear_show_test(); } kill_children($workerProcs); if ($testsInProgress < 0) { error("$testsInProgress test batches “in progress”, which is less than zero. THIS SHOULD NOT HAPPEN."); } } function send_message($stream, array $message): void { $blocking = stream_get_meta_data($stream)["blocked"]; stream_set_blocking($stream, true); fwrite($stream, base64_encode(serialize($message)) . "\n"); stream_set_blocking($stream, $blocking); } function kill_children(array $children): void { foreach ($children as $child) { if ($child) { proc_terminate($child); } } } function run_worker(): void { global $workerID, $workerSock; global $junit; $sockUri = getenv("TEST_PHP_URI"); $workerSock = stream_socket_client($sockUri, $_, $_, 5) or error("Couldn't connect to $sockUri"); $greeting = fgets($workerSock); $greeting = unserialize(base64_decode($greeting)) or die("Could not decode greeting\n"); if ($greeting["type"] !== "hello") { error("Unexpected greeting of type $greeting[type]"); } set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use ($workerSock): bool { if (error_reporting() & $errno) { send_message($workerSock, compact('errno', 'errstr', 'errfile', 'errline') + [ 'type' => 'php_error' ]); } return true; }); foreach ($greeting["GLOBALS"] as $var => $value) { if ($var !== "workerID" && $var !== "workerSock" && $var !== "GLOBALS") { $GLOBALS[$var] = $value; } } foreach ($greeting["constants"] as $const => $value) { define($const, $value); } send_message($workerSock, [ "type" => "hello_reply", "workerID" => $workerID ]); send_message($workerSock, [ "type" => "ready" ]); while (($command = fgets($workerSock))) { $command = unserialize(base64_decode($command)); $command["env"]["TEST_PHP_WORKER"] = getenv('TEST_PHP_WORKER'); switch ($command["type"]) { case "run_tests": run_all_tests($command["test_files"], $command["env"], $command["redir_tested"]); send_message($workerSock, [ "type" => "tests_finished", "junit" => $junit->isEnabled() ? $junit : null, ]); $junit->clear(); break; default: send_message($workerSock, [ "type" => "error", "msg" => "Unrecognised message type: $command[type]" ]); break 2; } } } // // Show file or result block // function show_file_block(string $file, string $block, ?string $section = null): void { global $cfg; global $colorize; if ($cfg['show'][$file]) { if (is_null($section)) { $section = strtoupper($file); } if ($section === 'DIFF' && $colorize) { // '-' is Light Red for removal, '+' is Light Green for addition $block = preg_replace('/^[0-9]+\-\s.*$/m', "\e[1;31m\\0\e[0m", $block); $block = preg_replace('/^[0-9]+\+\s.*$/m', "\e[1;32m\\0\e[0m", $block); } echo "\n========" . $section . "========\n"; echo rtrim($block); echo "\n========DONE========\n"; } } function skip_test(string $tested, string $tested_file, string $shortname, string $reason) { global $junit; show_result('SKIP', $tested, $tested_file, "reason: $reason"); $junit->initSuite($junit->getSuiteName($shortname)); $junit->markTestAs('SKIP', $shortname, $tested, 0, $reason); return 'SKIPPED'; } // // Run an individual test case. // /** * @param string|array $file */ function run_test(string $php, $file, array $env): string { global $log_format, $ini_overwrites, $PHP_FAILED_TESTS; global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx; global $valgrind, $temp_source, $temp_target, $cfg, $environment; global $no_clean; global $SHOW_ONLY_GROUPS; global $no_file_cache; global $slow_min_ms; global $preload, $file_cache; global $num_repeats; // Parallel testing global $workerID; // Temporary /** @var JUnit */ global $junit; static $skipCache; if (!$skipCache) { $enableSkipCache = !($env['DISABLE_SKIP_CACHE'] ?? '0'); $skipCache = new SkipCache($enableSkipCache, $cfg['keep']['skip']); } $temp_filenames = null; $org_file = $file; $orig_php = $php; $php_cgi = $env['TEST_PHP_CGI_EXECUTABLE'] ?? null; $phpdbg = $env['TEST_PHPDBG_EXECUTABLE'] ?? null; if (is_array($file)) { $file = $file[0]; } if ($DETAILED) { echo " ================= TEST $file "; } $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file); $tested_file = $shortname; try { $test = new TestFile($file, (bool)$IN_REDIRECT); } catch (BorkageException $ex) { show_result("BORK", $ex->getMessage(), $tested_file); $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "{$ex->getMessage()} [$file]", ]; $junit->markTestAs('BORK', $shortname, $tested_file, 0, $ex->getMessage()); return 'BORKED'; } $tested = $test->getName(); if ($num_repeats > 1 && $test->hasSection('FILE_EXTERNAL')) { return skip_test($tested, $tested_file, $shortname, 'Test with FILE_EXTERNAL might not be repeatable'); } if ($test->hasSection('CAPTURE_STDIO')) { $capture = $test->getSection('CAPTURE_STDIO'); $captureStdIn = stripos($capture, 'STDIN') !== false; $captureStdOut = stripos($capture, 'STDOUT') !== false; $captureStdErr = stripos($capture, 'STDERR') !== false; } else { $captureStdIn = true; $captureStdOut = true; $captureStdErr = true; } if ($captureStdOut && $captureStdErr) { $cmdRedirect = ' 2>&1'; } else { $cmdRedirect = ''; } /* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */ if ($test->isCGI()) { if (!$php_cgi) { return skip_test($tested, $tested_file, $shortname, 'CGI not available'); } $php = $php_cgi . ' -C '; $uses_cgi = true; if ($num_repeats > 1) { return skip_test($tested, $tested_file, $shortname, 'CGI does not support --repeat'); } } /* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */ $extra_options = ''; if ($test->hasSection('PHPDBG')) { if (isset($phpdbg)) { $php = $phpdbg . ' -qIb'; // Additional phpdbg command line options for sections that need to // be run straight away. For example, EXTENSIONS, SKIPIF, CLEAN. $extra_options = '-rr'; } else { return skip_test($tested, $tested_file, $shortname, 'phpdbg not available'); } if ($num_repeats > 1) { return skip_test($tested, $tested_file, $shortname, 'phpdbg does not support --repeat'); } } if ($num_repeats > 1) { if ($test->hasSection('CLEAN')) { return skip_test($tested, $tested_file, $shortname, 'Test with CLEAN might not be repeatable'); } if ($test->hasSection('STDIN')) { return skip_test($tested, $tested_file, $shortname, 'Test with STDIN might not be repeatable'); } if ($test->hasSection('CAPTURE_STDIO')) { return skip_test($tested, $tested_file, $shortname, 'Test with CAPTURE_STDIO might not be repeatable'); } } if (!$SHOW_ONLY_GROUPS && !$workerID) { show_test($test_idx, $shortname); } if (is_array($IN_REDIRECT)) { $temp_dir = $test_dir = $IN_REDIRECT['dir']; } else { $temp_dir = $test_dir = realpath(dirname($file)); } if ($temp_source && $temp_target) { $temp_dir = str_replace($temp_source, $temp_target, $temp_dir); } $main_file_name = basename($file, 'phpt'); $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'diff'; $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'log'; $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'exp'; $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'out'; $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'mem'; $sh_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'sh'; $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; $test_file = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; $test_skipif = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; $temp_after = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'after.php'; $test_after = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'after.php'; $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; $test_clean = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; $preload_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'preload.php'; $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'post'; $tmp_relative_file = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $test_file) . 't'; if ($temp_source && $temp_target) { $temp_skipif .= 's'; $temp_file .= 's'; $temp_clean .= 's'; $copy_file = $temp_dir . DIRECTORY_SEPARATOR . basename(is_array($file) ? $file[1] : $file) . '.phps'; if (!is_dir(dirname($copy_file))) { mkdir(dirname($copy_file), 0777, true) or error("Cannot create output directory - " . dirname($copy_file)); } if ($test->hasSection('FILE')) { save_text($copy_file, $test->getSection('FILE')); } $temp_filenames = [ 'file' => $copy_file, 'diff' => $diff_filename, 'log' => $log_filename, 'exp' => $exp_filename, 'out' => $output_filename, 'mem' => $memcheck_filename, 'sh' => $sh_filename, 'php' => $temp_file, 'skip' => $temp_skipif, 'after' => $temp_after, 'clean' => $temp_clean ]; } if (is_array($IN_REDIRECT)) { $tested = $IN_REDIRECT['prefix'] . ' ' . $tested; $tested_file = $tmp_relative_file; $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $tested_file); } // unlink old test results @unlink($diff_filename); @unlink($log_filename); @unlink($exp_filename); @unlink($output_filename); @unlink($memcheck_filename); @unlink($sh_filename); @unlink($temp_file); @unlink($test_file); @unlink($temp_skipif); @unlink($test_skipif); @unlink($tmp_post); @unlink($temp_after); @unlink($test_after); @unlink($temp_clean); @unlink($test_clean); @unlink($preload_filename); // Reset environment from any previous test. $env['REDIRECT_STATUS'] = ''; $env['QUERY_STRING'] = ''; $env['PATH_TRANSLATED'] = ''; $env['SCRIPT_FILENAME'] = ''; $env['REQUEST_METHOD'] = ''; $env['CONTENT_TYPE'] = ''; $env['CONTENT_LENGTH'] = ''; $env['TZ'] = ''; if ($test->sectionNotEmpty('ENV')) { $env_str = str_replace('{PWD}', dirname($file), $test->getSection('ENV')); $env_str = str_replace('{RUNID}', getenv('UNIQ_RUN_ID'), $env_str); $env_str = str_replace('{TEST_PHP_WORKER}', getenv('TEST_PHP_WORKER'), $env_str); $env_str = str_replace('{TMP}', sys_get_temp_dir(), $env_str); foreach (explode("\n", $env_str) as $e) { $e = explode('=', trim($e), 2); if (!empty($e[0]) && isset($e[1])) { $env[$e[0]] = $e[1]; } } } // Default ini settings $ini_settings = $workerID ? ['opcache.cache_id' => "worker$workerID"] : []; // Additional required extensions $extensions = []; if ($test->hasSection('EXTENSIONS')) { $extensions = preg_split("/[\n\r]+/", trim($test->getSection('EXTENSIONS'))); } if (is_array($IN_REDIRECT) && $IN_REDIRECT['EXTENSIONS'] != []) { $extensions = array_merge($extensions, $IN_REDIRECT['EXTENSIONS']); } /* Load required extensions */ if ($extensions != []) { $ext_params = []; settings2array($ini_overwrites, $ext_params); $ext_params = settings2params($ext_params); [$ext_dir, $loaded] = $skipCache->getExtensions("$orig_php $pass_options $extra_options $ext_params $no_file_cache"); $ext_prefix = IS_WINDOWS ? "php_" : ""; $missing = []; foreach ($extensions as $req_ext) { if (!in_array(strtolower($req_ext), $loaded)) { if ($req_ext == 'opcache' || $req_ext == 'xdebug') { $ext_file = $ext_dir . DIRECTORY_SEPARATOR . $ext_prefix . $req_ext . '.' . PHP_SHLIB_SUFFIX; $ini_settings['zend_extension'][] = $ext_file; } else { $ext_file = $ext_dir . DIRECTORY_SEPARATOR . $ext_prefix . $req_ext . '.' . PHP_SHLIB_SUFFIX; $ini_settings['extension'][] = $ext_file; } if (!is_readable($ext_file)) { $missing[] = $req_ext; } } } if ($missing) { $message = 'Required extension' . (count($missing) > 1 ? 's' : '') . ' missing: ' . implode(', ', $missing); return skip_test($tested, $tested_file, $shortname, $message); } } // additional ini overwrites //$ini_overwrites[] = 'setting=value'; settings2array($ini_overwrites, $ini_settings); if (getenv('OPCACHE') !== false) { if (getenv('OPCACHE') == 'yes') { $ini_settings['opcache.enable'] = 1; $ini_settings['opcache.enable_cli'] = 1; $ini_settings['opcache.optimization_level'] = -1; } else { $ini_settings['opcache.enable'] = 0; $ini_settings['opcache.enable_cli'] = 0; $ini_settings['opcache.optimization_level'] = 0; } } $orig_ini_settings = settings2params($ini_settings); if ($file_cache !== null) { $ini_settings['opcache.file_cache'] = '/tmp'; // Make sure warnings still show up on the second run. $ini_settings['opcache.record_warnings'] = '1'; // File cache is currently incompatible with JIT. $ini_settings['opcache.jit'] = '0'; if ($file_cache === 'use') { // Disable timestamp validation in order to fetch from file cache, // even though all the files are re-created. $ini_settings['opcache.validate_timestamps'] = '0'; } } else if ($num_repeats > 1) { // Make sure warnings still show up on the second run. $ini_settings['opcache.record_warnings'] = '1'; } // Any special ini settings // these may overwrite the test defaults... if ($test->hasSection('INI')) { $ini = str_replace('{PWD}', dirname($file), $test->getSection('INI')); $ini = str_replace('{RUNID}', getenv('UNIQ_RUN_ID'), $ini); $ini = str_replace('{TEST_PHP_WORKER}', getenv('TEST_PHP_WORKER'), $ini); $ini = str_replace('{TMP}', sys_get_temp_dir(), $ini); $replacement = IS_WINDOWS ? '"' . PHP_BINARY . ' -r \"while ($in = fgets(STDIN)) echo $in;\" > $1"' : 'tee $1 >/dev/null'; $ini = preg_replace('/{MAIL:(\S+)}/', $replacement, $ini); settings2array(preg_split("/[\n\r]+/", $ini), $ini_settings); if ($num_repeats > 1 && isset($ini_settings['opcache.opt_debug_level'])) { return skip_test($tested, $tested_file, $shortname, 'opt_debug_level tests are not repeatable'); } } $ini_settings = settings2params($ini_settings); $env['TEST_PHP_EXTRA_ARGS'] = $pass_options . ' ' . $ini_settings; // Check if test should be skipped. $info = ''; $warn = false; if ($test->sectionNotEmpty('SKIPIF')) { show_file_block('skip', $test->getSection('SKIPIF')); $extra = !IS_WINDOWS ? "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : ""; if ($valgrind) { $env['USE_ZEND_ALLOC'] = '0'; $env['ZEND_DONT_UNLOAD_MODULES'] = 1; } if (!array_key_exists('XDEBUG_MODE', $env)) { $env['XDEBUG_MODE'] = ""; } /* Remove auto prepend and append settings for SKIPIF */ $skipif_ini_settings = $orig_ini_settings; $skipif_ini_settings = preg_replace( '@-d \"auto_prepend_file=.*?\" @', '', $skipif_ini_settings ); $skipif_ini_settings = preg_replace( '@-d \"auto_append_file=.*?\" @', '', $skipif_ini_settings ); $skipif_ini_settings = preg_replace( '@-d \"xdebug\.log=.*?\" @', '', $skipif_ini_settings ); $skipif_ini_settings .= " -d track_errors=0 -d xdebug.mode=off"; $junit->startTimer($shortname); $startTime = microtime(true); $commandLine = "$extra $php $pass_options $extra_options -q $skipif_ini_settings $no_file_cache -d display_errors=1 -d display_startup_errors=0"; $output = $skipCache->checkSkip($commandLine, $test->getSection('SKIPIF'), $test_skipif, $temp_skipif, $env); $time = microtime(true) - $startTime; $junit->stopTimer($shortname); if ($time > $slow_min_ms / 1000) { $PHP_FAILED_TESTS['SLOW'][] = [ 'name' => $file, 'test_name' => 'SKIPIF of ' . $tested . " [$tested_file]", 'output' => '', 'diff' => '', 'info' => $time, ]; } if (!$cfg['keep']['skip']) { @unlink($test_skipif); } if (!strncasecmp('skip', $output, 4)) { if (preg_match('/^skip\s*(.+)/i', $output, $m)) { show_result('SKIP', $tested, $tested_file, "reason: $m[1]", $temp_filenames); } else { show_result('SKIP', $tested, $tested_file, '', $temp_filenames); } $message = !empty($m[1]) ? $m[1] : ''; $junit->markTestAs('SKIP', $shortname, $tested, null, $message); return 'SKIPPED'; } if (!strncasecmp('info', $output, 4) && preg_match('/^info\s*(.+)/i', $output, $m)) { $info = " (info: $m[1])"; } elseif (!strncasecmp('warn', $output, 4) && preg_match('/^warn\s+(.+)/i', $output, $m)) { $warn = true; /* only if there is a reason */ $info = " (warn: $m[1])"; } elseif (!strncasecmp('xfail', $output, 5)) { // Pretend we have an XFAIL section $test->setSection('XFAIL', ltrim(substr($output, 5))); } elseif ($output !== '') { show_result("BORK", $output, $tested_file, 'reason: invalid output from SKIPIF', $temp_filenames); $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$output [$file]", ]; $junit->markTestAs('BORK', $shortname, $tested, null, $output); return 'BORKED'; } } if (!extension_loaded("zlib") && $test->hasAnySections("GZIP_POST", "DEFLATE_POST")) { $message = "ext/zlib required"; show_result('SKIP', $tested, $tested_file, "reason: $message", $temp_filenames); $junit->markTestAs('SKIP', $shortname, $tested, null, $message); return 'SKIPPED'; } if ($test->hasSection('REDIRECTTEST')) { $test_files = []; $IN_REDIRECT = eval($test->getSection('REDIRECTTEST')); $IN_REDIRECT['via'] = "via [$shortname]\n\t"; $IN_REDIRECT['dir'] = realpath(dirname($file)); $IN_REDIRECT['prefix'] = $tested; $IN_REDIRECT['EXTENSIONS'] = $extensions; if (!empty($IN_REDIRECT['TESTS'])) { if (is_array($org_file)) { $test_files[] = $org_file[1]; } else { $GLOBALS['test_files'] = $test_files; find_files($IN_REDIRECT['TESTS']); foreach ($GLOBALS['test_files'] as $f) { $test_files[] = [$f, $file]; } } $test_cnt += count($test_files) - 1; $test_idx--; show_redirect_start($IN_REDIRECT['TESTS'], $tested, $tested_file); // set up environment $redirenv = array_merge($environment, $IN_REDIRECT['ENV']); $redirenv['REDIR_TEST_DIR'] = realpath($IN_REDIRECT['TESTS']) . DIRECTORY_SEPARATOR; usort($test_files, "test_sort"); run_all_tests($test_files, $redirenv, $tested); show_redirect_ends($IN_REDIRECT['TESTS'], $tested, $tested_file); // a redirected test never fails $IN_REDIRECT = false; $junit->markTestAs('PASS', $shortname, $tested); return 'REDIR'; } else { $bork_info = "Redirect info must contain exactly one TEST string to be used as redirect directory."; show_result("BORK", $bork_info, '', '', $temp_filenames); $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$bork_info [$file]", ]; } } if (is_array($org_file) || $test->hasSection('REDIRECTTEST')) { if (is_array($org_file)) { $file = $org_file[0]; } $bork_info = "Redirected test did not contain redirection info"; show_result("BORK", $bork_info, '', '', $temp_filenames); $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$bork_info [$file]", ]; $junit->markTestAs('BORK', $shortname, $tested, null, $bork_info); return 'BORKED'; } // We've satisfied the preconditions - run the test! if ($test->hasSection('FILE')) { show_file_block('php', $test->getSection('FILE'), 'TEST'); save_text($test_file, $test->getSection('FILE'), $temp_file); } else { $test_file = $temp_file = ""; } if ($test->hasSection('GET')) { $query_string = trim($test->getSection('GET')); } else { $query_string = ''; } $env['REDIRECT_STATUS'] = '1'; if (empty($env['QUERY_STRING'])) { $env['QUERY_STRING'] = $query_string; } if (empty($env['PATH_TRANSLATED'])) { $env['PATH_TRANSLATED'] = $test_file; } if (empty($env['SCRIPT_FILENAME'])) { $env['SCRIPT_FILENAME'] = $test_file; } if ($test->hasSection('COOKIE')) { $env['HTTP_COOKIE'] = trim($test->getSection('COOKIE')); } else { $env['HTTP_COOKIE'] = ''; } $args = $test->hasSection('ARGS') ? ' -- ' . $test->getSection('ARGS') : ''; if ($preload && !empty($test_file)) { save_text($preload_filename, "sectionNotEmpty('POST_RAW')) { $post = trim($test->getSection('POST_RAW')); $raw_lines = explode("\n", $post); $request = ''; $started = false; foreach ($raw_lines as $line) { if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); continue; } if ($started) { $request .= "\n"; } $started = true; $request .= $line; } $env['CONTENT_LENGTH'] = strlen($request); $env['REQUEST_METHOD'] = 'POST'; if (empty($request)) { $junit->markTestAs('BORK', $shortname, $tested, null, 'empty $request'); return 'BORKED'; } save_text($tmp_post, $request); $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; } elseif ($test->sectionNotEmpty('PUT')) { $post = trim($test->getSection('PUT')); $raw_lines = explode("\n", $post); $request = ''; $started = false; foreach ($raw_lines as $line) { if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); continue; } if ($started) { $request .= "\n"; } $started = true; $request .= $line; } $env['CONTENT_LENGTH'] = strlen($request); $env['REQUEST_METHOD'] = 'PUT'; if (empty($request)) { $junit->markTestAs('BORK', $shortname, $tested, null, 'empty $request'); return 'BORKED'; } save_text($tmp_post, $request); $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; } elseif ($test->sectionNotEmpty('POST')) { $post = trim($test->getSection('POST')); $content_length = strlen($post); save_text($tmp_post, $post); $env['REQUEST_METHOD'] = 'POST'; if (empty($env['CONTENT_TYPE'])) { $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; } if (empty($env['CONTENT_LENGTH'])) { $env['CONTENT_LENGTH'] = $content_length; } $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; } elseif ($test->sectionNotEmpty('GZIP_POST')) { $post = trim($test->getSection('GZIP_POST')); $post = gzencode($post, 9, FORCE_GZIP); $env['HTTP_CONTENT_ENCODING'] = 'gzip'; save_text($tmp_post, $post); $content_length = strlen($post); $env['REQUEST_METHOD'] = 'POST'; $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; $env['CONTENT_LENGTH'] = $content_length; $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; } elseif ($test->sectionNotEmpty('DEFLATE_POST')) { $post = trim($test->getSection('DEFLATE_POST')); $post = gzcompress($post, 9); $env['HTTP_CONTENT_ENCODING'] = 'deflate'; save_text($tmp_post, $post); $content_length = strlen($post); $env['REQUEST_METHOD'] = 'POST'; $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; $env['CONTENT_LENGTH'] = $content_length; $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; } else { $env['REQUEST_METHOD'] = 'GET'; $env['CONTENT_TYPE'] = ''; $env['CONTENT_LENGTH'] = ''; $repeat_option = $num_repeats > 1 ? "--repeat $num_repeats" : ""; $cmd = "$php $pass_options $repeat_option $ini_settings -f \"$test_file\" $args$cmdRedirect"; } $orig_cmd = $cmd; if ($valgrind) { $env['USE_ZEND_ALLOC'] = '0'; $env['ZEND_DONT_UNLOAD_MODULES'] = 1; $cmd = $valgrind->wrapCommand($cmd, $memcheck_filename, strpos($test_file, "pcre") !== false); } if (!array_key_exists('XDEBUG_MODE', $env)) { $env['XDEBUG_MODE'] = ""; } if ($DETAILED) { echo " CONTENT_LENGTH = " . $env['CONTENT_LENGTH'] . " CONTENT_TYPE = " . $env['CONTENT_TYPE'] . " PATH_TRANSLATED = " . $env['PATH_TRANSLATED'] . " QUERY_STRING = " . $env['QUERY_STRING'] . " REDIRECT_STATUS = " . $env['REDIRECT_STATUS'] . " REQUEST_METHOD = " . $env['REQUEST_METHOD'] . " SCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . " HTTP_COOKIE = " . $env['HTTP_COOKIE'] . " COMMAND $cmd "; } $junit->startTimer($shortname); $hrtime = hrtime(); $startTime = $hrtime[0] * 1000000000 + $hrtime[1]; $stdin = $test->hasSection('STDIN') ? $test->getSection('STDIN') : null; $out = system_with_timeout($cmd, $env, $stdin, $captureStdIn, $captureStdOut, $captureStdErr); $junit->stopTimer($shortname); $hrtime = hrtime(); $time = $hrtime[0] * 1000000000 + $hrtime[1] - $startTime; if ($time >= $slow_min_ms * 1000000) { $PHP_FAILED_TESTS['SLOW'][] = [ 'name' => $file, 'test_name' => $tested . " [$tested_file]", 'output' => '', 'diff' => '', 'info' => $time / 1000000000, ]; } // Things to run after the test (like, final) if ($test->sectionNotEmpty('AFTER')) { show_file_block('after', $test->getSection('AFTER')); save_text($test_clean, trim($test->getSection('AFTER')), $temp_clean); $extra = !IS_WINDOWS ? "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : ""; $out .= system_with_timeout("$extra $orig_php $pass_options -q $orig_ini_settings $no_file_cache \"$test_clean\"", $env); } // Remember CLEAN output to report borked test if it otherwise passes. $clean_output = null; if ($test->sectionNotEmpty('CLEAN') && (!$no_clean || $cfg['keep']['clean'])) { show_file_block('clean', $test->getSection('CLEAN')); save_text($test_clean, trim($test->getSection('CLEAN')), $temp_clean); if (!$no_clean) { $extra = !IS_WINDOWS ? "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : ""; $clean_output = system_with_timeout("$extra $orig_php $pass_options -q $orig_ini_settings $no_file_cache \"$test_clean\"", $env); } if (!$cfg['keep']['clean']) { @unlink($test_clean); } } $leaked = false; $passed = false; if ($valgrind) { // leak check $leaked = filesize($memcheck_filename) > 0; if (!$leaked) { @unlink($memcheck_filename); } } if ($num_repeats > 1) { // In repeat mode, retain the output before the first execution, // and of the last execution. Do this early, because the trimming below // makes the newline handling complicated. $separator1 = "Executing for the first time...\n"; $separator1_pos = strpos($out, $separator1); if ($separator1_pos !== false) { $separator2 = "Finished execution, repeating...\n"; $separator2_pos = strrpos($out, $separator2); if ($separator2_pos !== false) { $out = substr($out, 0, $separator1_pos) . substr($out, $separator2_pos + strlen($separator2)); } else { $out = substr($out, 0, $separator1_pos) . substr($out, $separator1_pos + strlen($separator1)); } } } // Does the output match what is expected? $output = preg_replace("/\r\n/", "\n", trim($out)); /* when using CGI, strip the headers from the output */ $headers = []; if (!empty($uses_cgi) && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) { $output = trim($match[2]); $rh = preg_split("/[\n\r]+/", $match[1]); foreach ($rh as $line) { if (strpos($line, ':') !== false) { $line = explode(':', $line, 2); $headers[trim($line[0])] = trim($line[1]); } } } $wanted_headers = null; $output_headers = null; $failed_headers = false; if ($test->hasSection('EXPECTHEADERS')) { $want = []; $wanted_headers = []; $lines = preg_split("/[\n\r]+/", $test->getSection('EXPECTHEADERS')); foreach ($lines as $line) { if (strpos($line, ':') !== false) { $line = explode(':', $line, 2); $want[trim($line[0])] = trim($line[1]); $wanted_headers[] = trim($line[0]) . ': ' . trim($line[1]); } } $output_headers = []; foreach ($want as $k => $v) { if (isset($headers[$k])) { $output_headers[] = $k . ': ' . $headers[$k]; } if (!isset($headers[$k]) || $headers[$k] != $v) { $failed_headers = true; } } ksort($wanted_headers); $wanted_headers = implode("\n", $wanted_headers); ksort($output_headers); $output_headers = implode("\n", $output_headers); } show_file_block('out', $output); if ($preload) { $output = trim(preg_replace("/\n?Warning: Can't preload [^\n]*\n?/", "", $output)); } if ($test->hasAnySections('EXPECTF', 'EXPECTREGEX')) { if ($test->hasSection('EXPECTF')) { $wanted = trim($test->getSection('EXPECTF')); } else { $wanted = trim($test->getSection('EXPECTREGEX')); } show_file_block('exp', $wanted); $wanted_re = preg_replace('/\r\n/', "\n", $wanted); if ($test->hasSection('EXPECTF')) { // do preg_quote, but miss out any %r delimited sections $temp = ""; $r = "%r"; $startOffset = 0; $length = strlen($wanted_re); while ($startOffset < $length) { $start = strpos($wanted_re, $r, $startOffset); if ($start !== false) { // we have found a start tag $end = strpos($wanted_re, $r, $start + 2); if ($end === false) { // unbalanced tag, ignore it. $end = $start = $length; } } else { // no more %r sections $start = $end = $length; } // quote a non re portion of the string $temp .= preg_quote(substr($wanted_re, $startOffset, $start - $startOffset), '/'); // add the re unquoted. if ($end > $start) { $temp .= '(' . substr($wanted_re, $start + 2, $end - $start - 2) . ')'; } $startOffset = $end + 2; } $wanted_re = $temp; // Stick to basics $wanted_re = str_replace('%e', '\\' . DIRECTORY_SEPARATOR, $wanted_re); $wanted_re = str_replace('%s', '[^\r\n]+', $wanted_re); $wanted_re = str_replace('%S', '[^\r\n]*', $wanted_re); $wanted_re = str_replace('%a', '.+', $wanted_re); $wanted_re = str_replace('%A', '.*', $wanted_re); $wanted_re = str_replace('%w', '\s*', $wanted_re); $wanted_re = str_replace('%i', '[+-]?\d+', $wanted_re); $wanted_re = str_replace('%d', '\d+', $wanted_re); $wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re); $wanted_re = str_replace('%f', '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', $wanted_re); $wanted_re = str_replace('%c', '.', $wanted_re); $wanted_re = str_replace('%0', '\x00', $wanted_re); // %f allows two points "-.0.0" but that is the best *simple* expression } if (preg_match("/^$wanted_re\$/s", $output)) { $passed = true; } } else { $wanted = trim($test->getSection('EXPECT')); $wanted = preg_replace('/\r\n/', "\n", $wanted); show_file_block('exp', $wanted); // compare and leave on success if (!strcmp($output, $wanted)) { $passed = true; } $wanted_re = null; } if ($passed) { if (!$cfg['keep']['php'] && !$leaked) { @unlink($test_file); @unlink($preload_filename); } @unlink($tmp_post); if (!$leaked && !$failed_headers) { // If the test passed and CLEAN produced output, report test as borked. if ($clean_output) { show_result("BORK", $output, $tested_file, 'reason: invalid output from CLEAN', $temp_filenames); $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$clean_output [$file]", ]; $junit->markTestAs('BORK', $shortname, $tested, null, $clean_output); return 'BORKED'; } if ($test->hasSection('XFAIL')) { $warn = true; $info = " (warn: XFAIL section but test passes)"; } elseif ($test->hasSection('XLEAK')) { $warn = true; $info = " (warn: XLEAK section but test passes)"; } else { show_result("PASS", $tested, $tested_file, '', $temp_filenames); $junit->markTestAs('PASS', $shortname, $tested); return 'PASSED'; } } } // Test failed so we need to report details. if ($failed_headers) { $passed = false; $wanted = $wanted_headers . "\n--HEADERS--\n" . $wanted; $output = $output_headers . "\n--HEADERS--\n" . $output; if (isset($wanted_re)) { $wanted_re = preg_quote($wanted_headers . "\n--HEADERS--\n", '/') . $wanted_re; } } $restype = []; if ($leaked) { $restype[] = $test->hasSection('XLEAK') ? 'XLEAK' : 'LEAK'; } if ($warn) { $restype[] = 'WARN'; } if (!$passed) { if ($test->hasSection('XFAIL')) { $restype[] = 'XFAIL'; $info = ' XFAIL REASON: ' . rtrim($test->getSection('XFAIL')); } elseif ($test->hasSection('XLEAK')) { $restype[] = 'XLEAK'; $info = ' XLEAK REASON: ' . rtrim($test->getSection('XLEAK')); } else { $restype[] = 'FAIL'; } } if (!$passed) { // write .exp if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted) === false) { error("Cannot create expected test output - $exp_filename"); } // write .out if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output) === false) { error("Cannot create test output - $output_filename"); } // write .diff if (!empty($environment['TEST_PHP_DIFF_CMD'])) { $diff = generate_diff_external($environment['TEST_PHP_DIFF_CMD'], $exp_filename, $output_filename); } else { $diff = generate_diff($wanted, $wanted_re, $output); } if (is_array($IN_REDIRECT)) { $orig_shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file); $diff = "# original source file: $orig_shortname\n" . $diff; } show_file_block('diff', $diff); if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff) === false) { error("Cannot create test diff - $diff_filename"); } // write .log if (strpos($log_format, 'L') !== false && file_put_contents($log_filename, " ---- EXPECTED OUTPUT $wanted ---- ACTUAL OUTPUT $output ---- FAILED ") === false) { error("Cannot create test log - $log_filename"); error_report($file, $log_filename, $tested); } } if (!$passed || $leaked) { // write .sh if (strpos($log_format, 'S') !== false) { $env_lines = []; foreach ($env as $env_var => $env_val) { $env_lines[] = "export $env_var=" . escapeshellarg($env_val ?? ""); } $exported_environment = $env_lines ? "\n" . implode("\n", $env_lines) . "\n" : ""; $sh_script = << $file, 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", 'output' => $output_filename, 'diff' => $diff_filename, 'info' => $info, ]; } $diff = empty($diff) ? '' : preg_replace('/\e/', '', $diff); $junit->markTestAs($restype, $shortname, $tested, null, $info, $diff); return $restype[0] . 'ED'; } /** * @return bool|int */ function comp_line(string $l1, string $l2, bool $is_reg) { if ($is_reg) { return preg_match('/^' . $l1 . '$/s', $l2); } else { return !strcmp($l1, $l2); } } function count_array_diff( array $ar1, array $ar2, bool $is_reg, array $w, int $idx1, int $idx2, int $cnt1, int $cnt2, int $steps ): int { $equal = 0; while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { $idx1++; $idx2++; $equal++; $steps--; } if (--$steps > 0) { $eq1 = 0; $st = $steps / 2; for ($ofs1 = $idx1 + 1; $ofs1 < $cnt1 && $st-- > 0; $ofs1++) { $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $ofs1, $idx2, $cnt1, $cnt2, $st); if ($eq > $eq1) { $eq1 = $eq; } } $eq2 = 0; $st = $steps; for ($ofs2 = $idx2 + 1; $ofs2 < $cnt2 && $st-- > 0; $ofs2++) { $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $ofs2, $cnt1, $cnt2, $st); if ($eq > $eq2) { $eq2 = $eq; } } if ($eq1 > $eq2) { $equal += $eq1; } elseif ($eq2 > 0) { $equal += $eq2; } } return $equal; } function generate_array_diff(array $ar1, array $ar2, bool $is_reg, array $w): array { global $context_line_count; $idx1 = 0; $cnt1 = @count($ar1); $idx2 = 0; $cnt2 = @count($ar2); $diff = []; $old1 = []; $old2 = []; $number_len = max(3, strlen((string)max($cnt1 + 1, $cnt2 + 1))); $line_number_spec = '%0' . $number_len . 'd'; /** Mapping from $idx2 to $idx1, including indexes of idx2 that are identical to idx1 as well as entries that don't have matches */ $mapping = []; while ($idx1 < $cnt1 && $idx2 < $cnt2) { $mapping[$idx2] = $idx1; if (comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { $idx1++; $idx2++; continue; } else { $c1 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1 + 1, $idx2, $cnt1, $cnt2, 10); $c2 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2 + 1, $cnt1, $cnt2, 10); if ($c1 > $c2) { $old1[$idx1] = sprintf("{$line_number_spec}- ", $idx1 + 1) . $w[$idx1++]; } elseif ($c2 > 0) { $old2[$idx2] = sprintf("{$line_number_spec}+ ", $idx2 + 1) . $ar2[$idx2++]; } else { $old1[$idx1] = sprintf("{$line_number_spec}- ", $idx1 + 1) . $w[$idx1++]; $old2[$idx2] = sprintf("{$line_number_spec}+ ", $idx2 + 1) . $ar2[$idx2++]; } $last_printed_context_line = $idx1; } } $mapping[$idx2] = $idx1; reset($old1); $k1 = key($old1); $l1 = -2; reset($old2); $k2 = key($old2); $l2 = -2; $old_k1 = -1; $add_context_lines = function (int $new_k1) use (&$old_k1, &$diff, $w, $context_line_count, $number_len) { if ($old_k1 >= $new_k1 || !$context_line_count) { return; } $end = $new_k1 - 1; $range_end = min($end, $old_k1 + $context_line_count); if ($old_k1 >= 0) { while ($old_k1 < $range_end) { $diff[] = str_repeat(' ', $number_len + 2) . $w[$old_k1++]; } } if ($end - $context_line_count > $old_k1) { $old_k1 = $end - $context_line_count; if ($old_k1 > 0) { // Add a '--' to mark sections where the common areas were truncated $diff[] = '--'; } } $old_k1 = max($old_k1, 0); while ($old_k1 < $end) { $diff[] = str_repeat(' ', $number_len + 2) . $w[$old_k1++]; } $old_k1 = $new_k1; }; while ($k1 !== null || $k2 !== null) { if ($k1 == $l1 + 1 || $k2 === null) { $add_context_lines($k1); $l1 = $k1; $diff[] = current($old1); $old_k1 = $k1; $k1 = next($old1) ? key($old1) : null; } elseif ($k2 == $l2 + 1 || $k1 === null) { $add_context_lines($mapping[$k2]); $l2 = $k2; $diff[] = current($old2); $k2 = next($old2) ? key($old2) : null; } elseif ($k1 < $mapping[$k2]) { $add_context_lines($k1); $l1 = $k1; $diff[] = current($old1); $k1 = next($old1) ? key($old1) : null; } else { $add_context_lines($mapping[$k2]); $l2 = $k2; $diff[] = current($old2); $k2 = next($old2) ? key($old2) : null; } } while ($idx1 < $cnt1) { $add_context_lines($idx1 + 1); $diff[] = sprintf("{$line_number_spec}- ", $idx1 + 1) . $w[$idx1++]; } while ($idx2 < $cnt2) { if (isset($mapping[$idx2])) { $add_context_lines($mapping[$idx2] + 1); } $diff[] = sprintf("{$line_number_spec}+ ", $idx2 + 1) . $ar2[$idx2++]; } $add_context_lines(min($old_k1 + $context_line_count + 1, $cnt1 + 1)); if ($context_line_count && $old_k1 < $cnt1 + 1) { // Add a '--' to mark sections where the common areas were truncated $diff[] = '--'; } return $diff; } function generate_diff_external(string $diff_cmd, string $exp_file, string $output_file): string { $retval = shell_exec("{$diff_cmd} {$exp_file} {$output_file}"); return is_string($retval) ? $retval : 'Could not run external diff tool set through TEST_PHP_DIFF_CMD environment variable'; } function generate_diff(string $wanted, ?string $wanted_re, string $output): string { $w = explode("\n", $wanted); $o = explode("\n", $output); $r = is_null($wanted_re) ? $w : explode("\n", $wanted_re); $diff = generate_array_diff($r, $o, !is_null($wanted_re), $w); return implode(PHP_EOL, $diff); } function error(string $message): void { echo "ERROR: {$message}\n"; exit(1); } function settings2array(array $settings, &$ini_settings): void { foreach ($settings as $setting) { if (strpos($setting, '=') !== false) { $setting = explode("=", $setting, 2); $name = trim($setting[0]); $value = trim($setting[1]); if ($name == 'extension' || $name == 'zend_extension') { if (!isset($ini_settings[$name])) { $ini_settings[$name] = []; } $ini_settings[$name][] = $value; } else { $ini_settings[$name] = $value; } } } } function settings2params(array $ini_settings): string { $settings = ''; foreach ($ini_settings as $name => $value) { if (is_array($value)) { foreach ($value as $val) { $val = addslashes($val); $settings .= " -d \"$name=$val\""; } } else { if (IS_WINDOWS && !empty($value) && $value[0] == '"') { $len = strlen($value); if ($value[$len - 1] == '"') { $value[0] = "'"; $value[$len - 1] = "'"; } } else { $value = addslashes($value); } $settings .= " -d \"$name=$value\""; } } return $settings; } function compute_summary(): void { global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results; $n_total = count($test_results); $n_total += $ignored_by_ext; $sum_results = [ 'PASSED' => 0, 'WARNED' => 0, 'SKIPPED' => 0, 'FAILED' => 0, 'BORKED' => 0, 'LEAKED' => 0, 'XFAILED' => 0, 'XLEAKED' => 0 ]; foreach ($test_results as $v) { $sum_results[$v]++; } $sum_results['SKIPPED'] += $ignored_by_ext; $percent_results = []; foreach ($sum_results as $v => $n) { $percent_results[$v] = (100.0 * $n) / $n_total; } } function get_summary(bool $show_ext_summary): string { global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $valgrind; $x_total = $n_total - $sum_results['SKIPPED'] - $sum_results['BORKED']; if ($x_total) { $x_warned = (100.0 * $sum_results['WARNED']) / $x_total; $x_failed = (100.0 * $sum_results['FAILED']) / $x_total; $x_xfailed = (100.0 * $sum_results['XFAILED']) / $x_total; $x_xleaked = (100.0 * $sum_results['XLEAKED']) / $x_total; $x_leaked = (100.0 * $sum_results['LEAKED']) / $x_total; $x_passed = (100.0 * $sum_results['PASSED']) / $x_total; } else { $x_warned = $x_failed = $x_passed = $x_leaked = $x_xfailed = $x_xleaked = 0; } $summary = ''; if ($show_ext_summary) { $summary .= ' ===================================================================== TEST RESULT SUMMARY --------------------------------------------------------------------- Exts skipped : ' . sprintf('%4d', $exts_skipped) . ' Exts tested : ' . sprintf('%4d', $exts_tested) . ' --------------------------------------------------------------------- '; } $summary .= ' Number of tests : ' . sprintf('%4d', $n_total) . ' ' . sprintf('%8d', $x_total); if ($sum_results['BORKED']) { $summary .= ' Tests borked : ' . sprintf('%4d (%5.1f%%)', $sum_results['BORKED'], $percent_results['BORKED']) . ' --------'; } $summary .= ' Tests skipped : ' . sprintf('%4d (%5.1f%%)', $sum_results['SKIPPED'], $percent_results['SKIPPED']) . ' -------- Tests warned : ' . sprintf('%4d (%5.1f%%)', $sum_results['WARNED'], $percent_results['WARNED']) . ' ' . sprintf('(%5.1f%%)', $x_warned) . ' Tests failed : ' . sprintf('%4d (%5.1f%%)', $sum_results['FAILED'], $percent_results['FAILED']) . ' ' . sprintf('(%5.1f%%)', $x_failed); if ($sum_results['XFAILED']) { $summary .= ' Expected fail : ' . sprintf('%4d (%5.1f%%)', $sum_results['XFAILED'], $percent_results['XFAILED']) . ' ' . sprintf('(%5.1f%%)', $x_xfailed); } if ($valgrind) { $summary .= ' Tests leaked : ' . sprintf('%4d (%5.1f%%)', $sum_results['LEAKED'], $percent_results['LEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_leaked); if ($sum_results['XLEAKED']) { $summary .= ' Expected leak : ' . sprintf('%4d (%5.1f%%)', $sum_results['XLEAKED'], $percent_results['XLEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_xleaked); } } $summary .= ' Tests passed : ' . sprintf('%4d (%5.1f%%)', $sum_results['PASSED'], $percent_results['PASSED']) . ' ' . sprintf('(%5.1f%%)', $x_passed) . ' --------------------------------------------------------------------- Time taken : ' . sprintf('%4d seconds', $end_time - $start_time) . ' ===================================================================== '; $failed_test_summary = ''; if (count($PHP_FAILED_TESTS['SLOW'])) { usort($PHP_FAILED_TESTS['SLOW'], function (array $a, array $b): int { return $a['info'] < $b['info'] ? 1 : -1; }); $failed_test_summary .= ' ===================================================================== SLOW TEST SUMMARY --------------------------------------------------------------------- '; foreach ($PHP_FAILED_TESTS['SLOW'] as $failed_test_data) { $failed_test_summary .= sprintf('(%.3f s) ', $failed_test_data['info']) . $failed_test_data['test_name'] . "\n"; } $failed_test_summary .= "=====================================================================\n"; } if (count($PHP_FAILED_TESTS['XFAILED'])) { $failed_test_summary .= ' ===================================================================== EXPECTED FAILED TEST SUMMARY --------------------------------------------------------------------- '; foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) { $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; } $failed_test_summary .= "=====================================================================\n"; } if (count($PHP_FAILED_TESTS['BORKED'])) { $failed_test_summary .= ' ===================================================================== BORKED TEST SUMMARY --------------------------------------------------------------------- '; foreach ($PHP_FAILED_TESTS['BORKED'] as $failed_test_data) { $failed_test_summary .= $failed_test_data['info'] . "\n"; } $failed_test_summary .= "=====================================================================\n"; } if (count($PHP_FAILED_TESTS['FAILED'])) { $failed_test_summary .= ' ===================================================================== FAILED TEST SUMMARY --------------------------------------------------------------------- '; foreach ($PHP_FAILED_TESTS['FAILED'] as $failed_test_data) { $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; } $failed_test_summary .= "=====================================================================\n"; } if (count($PHP_FAILED_TESTS['WARNED'])) { $failed_test_summary .= ' ===================================================================== WARNED TEST SUMMARY --------------------------------------------------------------------- '; foreach ($PHP_FAILED_TESTS['WARNED'] as $failed_test_data) { $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; } $failed_test_summary .= "=====================================================================\n"; } if (count($PHP_FAILED_TESTS['LEAKED'])) { $failed_test_summary .= ' ===================================================================== LEAKED TEST SUMMARY --------------------------------------------------------------------- '; foreach ($PHP_FAILED_TESTS['LEAKED'] as $failed_test_data) { $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; } $failed_test_summary .= "=====================================================================\n"; } if (count($PHP_FAILED_TESTS['XLEAKED'])) { $failed_test_summary .= ' ===================================================================== EXPECTED LEAK TEST SUMMARY --------------------------------------------------------------------- '; foreach ($PHP_FAILED_TESTS['XLEAKED'] as $failed_test_data) { $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; } $failed_test_summary .= "=====================================================================\n"; } if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) { $summary .= $failed_test_summary; } return $summary; } function show_start($start_time): void { echo "TIME START " . date('Y-m-d H:i:s', $start_time) . "\n=====================================================================\n"; } function show_end($end_time): void { echo "=====================================================================\nTIME END " . date('Y-m-d H:i:s', $end_time) . "\n"; } function show_summary(): void { echo get_summary(true); } function show_redirect_start(string $tests, string $tested, string $tested_file): void { global $SHOW_ONLY_GROUPS; if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { echo "REDIRECT $tests ($tested [$tested_file]) begin\n"; } else { clear_show_test(); } } function show_redirect_ends(string $tests, string $tested, string $tested_file): void { global $SHOW_ONLY_GROUPS; if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { echo "REDIRECT $tests ($tested [$tested_file]) done\n"; } else { clear_show_test(); } } function show_test(int $test_idx, string $shortname): void { global $test_cnt; global $line_length; $str = "TEST $test_idx/$test_cnt [$shortname]\r"; $line_length = strlen($str); echo $str; flush(); } function clear_show_test(): void { global $line_length; // Parallel testing global $workerID; if (!$workerID && isset($line_length)) { // Write over the last line to avoid random trailing chars on next echo echo str_repeat(" ", $line_length), "\r"; } } function parse_conflicts(string $text): array { // Strip comments $text = preg_replace('/#.*/', '', $text); return array_map('trim', explode("\n", trim($text))); } function show_result( string $result, string $tested, string $tested_file, string $extra = '', ?array $temp_filenames = null ): void { global $SHOW_ONLY_GROUPS, $colorize; if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) { if ($colorize) { /* Use ANSI escape codes for coloring test result */ switch ( $result ) { case 'PASS': // Light Green $color = "\e[1;32m{$result}\e[0m"; break; case 'FAIL': case 'BORK': case 'LEAK': case 'LEAK&FAIL': // Light Red $color = "\e[1;31m{$result}\e[0m"; break; case 'SKIP': // Dimmed $color = "\e[0;37m{$result}"; break; default: // Yellow $color = "\e[1;33m{$result}\e[0m"; break; } echo "$color $tested [$tested_file] $extra\e[0m\n"; } else { echo "$result $tested [$tested_file] $extra\n"; } } elseif (!$SHOW_ONLY_GROUPS) { clear_show_test(); } } class BorkageException extends Exception { } class JUnit { private bool $enabled = true; private $fp = null; private array $suites = []; private array $rootSuite = self::EMPTY_SUITE + ['name' => 'php']; private const EMPTY_SUITE = [ 'test_total' => 0, 'test_pass' => 0, 'test_fail' => 0, 'test_error' => 0, 'test_skip' => 0, 'test_warn' => 0, 'files' => [], 'execution_time' => 0, ]; /** * @throws Exception */ public function __construct(array $env, int $workerID) { // Check whether a junit log is wanted. $fileName = $env['TEST_PHP_JUNIT'] ?? null; if (empty($fileName)) { $this->enabled = false; return; } if (!$workerID && !$this->fp = fopen($fileName, 'w')) { throw new Exception("Failed to open $fileName for writing."); } } public function isEnabled(): bool { return $this->enabled; } public function clear(): void { $this->rootSuite = self::EMPTY_SUITE + ['name' => 'php']; $this->suites = []; } public function saveXML(): void { if (!$this->enabled) { return; } $xml = '<' . '?' . 'xml version="1.0" encoding="UTF-8"' . '?' . '>' . PHP_EOL; $xml .= sprintf( '' . PHP_EOL, $this->rootSuite['name'], $this->rootSuite['test_total'], $this->rootSuite['test_fail'], $this->rootSuite['test_error'], $this->rootSuite['test_skip'], $this->rootSuite['execution_time'] ); $xml .= $this->getSuitesXML(); $xml .= ''; fwrite($this->fp, $xml); } private function getSuitesXML(string $suite_name = '') { // FIXME: $suite_name gets overwritten $result = ''; foreach ($this->suites as $suite_name => $suite) { $result .= sprintf( '' . PHP_EOL, $suite['name'], $suite['test_total'], $suite['test_fail'], $suite['test_error'], $suite['test_skip'], $suite['execution_time'] ); if (!empty($suite_name)) { foreach ($suite['files'] as $file) { $result .= $this->rootSuite['files'][$file]['xml']; } } $result .= '' . PHP_EOL; } return $result; } public function markTestAs( $type, string $file_name, string $test_name, ?int $time = null, string $message = '', string $details = '' ): void { if (!$this->enabled) { return; } $suite = $this->getSuiteName($file_name); $this->record($suite, 'test_total'); $time = $time ?? $this->getTimer($file_name); $this->record($suite, 'execution_time', $time); $escaped_details = htmlspecialchars($details, ENT_QUOTES, 'UTF-8'); $escaped_details = preg_replace_callback('/[\0-\x08\x0B\x0C\x0E-\x1F]/', function ($c) { return sprintf('[[0x%02x]]', ord($c[0])); }, $escaped_details); $escaped_message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); $escaped_test_name = htmlspecialchars($file_name . ' (' . $test_name . ')', ENT_QUOTES); $this->rootSuite['files'][$file_name]['xml'] = "\n"; if (is_array($type)) { $output_type = $type[0] . 'ED'; $temp = array_intersect(['XFAIL', 'XLEAK', 'FAIL', 'WARN'], $type); $type = reset($temp); } else { $output_type = $type . 'ED'; } if ('PASS' == $type || 'XFAIL' == $type || 'XLEAK' == $type) { $this->record($suite, 'test_pass'); } elseif ('BORK' == $type) { $this->record($suite, 'test_error'); $this->rootSuite['files'][$file_name]['xml'] .= "\n"; } elseif ('SKIP' == $type) { $this->record($suite, 'test_skip'); $this->rootSuite['files'][$file_name]['xml'] .= "$escaped_message\n"; } elseif ('WARN' == $type) { $this->record($suite, 'test_warn'); $this->rootSuite['files'][$file_name]['xml'] .= "$escaped_message\n"; } elseif ('FAIL' == $type) { $this->record($suite, 'test_fail'); $this->rootSuite['files'][$file_name]['xml'] .= "$escaped_details\n"; } else { $this->record($suite, 'test_error'); $this->rootSuite['files'][$file_name]['xml'] .= "$escaped_details\n"; } $this->rootSuite['files'][$file_name]['xml'] .= "\n"; } private function record(string $suite, string $param, $value = 1): void { $this->rootSuite[$param] += $value; $this->suites[$suite][$param] += $value; } private function getTimer(string $file_name) { if (!$this->enabled) { return 0; } if (isset($this->rootSuite['files'][$file_name]['total'])) { return number_format($this->rootSuite['files'][$file_name]['total'], 4); } return 0; } public function startTimer(string $file_name): void { if (!$this->enabled) { return; } if (!isset($this->rootSuite['files'][$file_name]['start'])) { $this->rootSuite['files'][$file_name]['start'] = microtime(true); $suite = $this->getSuiteName($file_name); $this->initSuite($suite); $this->suites[$suite]['files'][$file_name] = $file_name; } } public function getSuiteName(string $file_name): string { return $this->pathToClassName(dirname($file_name)); } private function pathToClassName(string $file_name): string { if (!$this->enabled) { return ''; } $ret = $this->rootSuite['name']; $_tmp = []; // lookup whether we're in the PHP source checkout $max = 5; if (is_file($file_name)) { $dir = dirname(realpath($file_name)); } else { $dir = realpath($file_name); } do { array_unshift($_tmp, basename($dir)); $chk = $dir . DIRECTORY_SEPARATOR . "main" . DIRECTORY_SEPARATOR . "php_version.h"; $dir = dirname($dir); } while (!file_exists($chk) && --$max > 0); if (file_exists($chk)) { if ($max) { array_shift($_tmp); } foreach ($_tmp as $p) { $ret .= "." . preg_replace(",[^a-z0-9]+,i", ".", $p); } return $ret; } return $this->rootSuite['name'] . '.' . str_replace([DIRECTORY_SEPARATOR, '-'], '.', $file_name); } public function initSuite(string $suite_name): void { if (!$this->enabled) { return; } if (!empty($this->suites[$suite_name])) { return; } $this->suites[$suite_name] = self::EMPTY_SUITE + ['name' => $suite_name]; } /** * @throws Exception */ public function stopTimer(string $file_name): void { if (!$this->enabled) { return; } if (!isset($this->rootSuite['files'][$file_name]['start'])) { throw new Exception("Timer for $file_name was not started!"); } if (!isset($this->rootSuite['files'][$file_name]['total'])) { $this->rootSuite['files'][$file_name]['total'] = 0; } $start = $this->rootSuite['files'][$file_name]['start']; $this->rootSuite['files'][$file_name]['total'] += microtime(true) - $start; unset($this->rootSuite['files'][$file_name]['start']); } public function mergeResults(?JUnit $other): void { if (!$this->enabled || !$other) { return; } $this->mergeSuites($this->rootSuite, $other->rootSuite); foreach ($other->suites as $name => $suite) { if (!isset($this->suites[$name])) { $this->suites[$name] = $suite; continue; } $this->mergeSuites($this->suites[$name], $suite); } } private function mergeSuites(array &$dest, array $source): void { $dest['test_total'] += $source['test_total']; $dest['test_pass'] += $source['test_pass']; $dest['test_fail'] += $source['test_fail']; $dest['test_error'] += $source['test_error']; $dest['test_skip'] += $source['test_skip']; $dest['test_warn'] += $source['test_warn']; $dest['execution_time'] += $source['execution_time']; $dest['files'] += $source['files']; } } class SkipCache { private bool $enable; private bool $keepFile; private array $skips = []; private array $extensions = []; private int $hits = 0; private int $misses = 0; private int $extHits = 0; private int $extMisses = 0; public function __construct(bool $enable, bool $keepFile) { $this->enable = $enable; $this->keepFile = $keepFile; } public function checkSkip(string $php, string $code, string $checkFile, string $tempFile, array $env): string { // Extension tests frequently use something like $dir"; if (isset($this->skips[$key][$code])) { $this->hits++; if ($this->keepFile) { save_text($checkFile, $code, $tempFile); } return $this->skips[$key][$code]; } save_text($checkFile, $code, $tempFile); $result = trim(system_with_timeout("$php \"$checkFile\"", $env)); if (strpos($result, 'nocache') === 0) { $result = ''; } else if ($this->enable) { $this->skips[$key][$code] = $result; } $this->misses++; if (!$this->keepFile) { @unlink($checkFile); } return $result; } public function getExtensions(string $php): array { if (isset($this->extensions[$php])) { $this->extHits++; return $this->extensions[$php]; } $extDir = `$php -d display_errors=0 -r "echo ini_get('extension_dir');"`; $extensions = explode(",", `$php -d display_errors=0 -r "echo implode(',', get_loaded_extensions());"`); $extensions = array_map('strtolower', $extensions); if (in_array('zend opcache', $extensions)) { $extensions[] = 'opcache'; } $result = [$extDir, $extensions]; $this->extensions[$php] = $result; $this->extMisses++; return $result; } // public function __destruct() // { // echo "Skips: {$this->hits} hits, {$this->misses} misses.\n"; // echo "Extensions: {$this->extHits} hits, {$this->extMisses} misses.\n"; // echo "Cache distribution:\n"; // // foreach ($this->skips as $php => $cache) { // echo "$php: " . count($cache) . "\n"; // } // } } class RuntestsValgrind { protected $version = ''; protected $header = ''; protected $version_3_8_0 = false; protected $tool = null; public function getVersion(): string { return $this->version; } public function getHeader(): string { return $this->header; } public function __construct(array $environment, string $tool = 'memcheck') { $this->tool = $tool; $header = system_with_timeout("valgrind --tool={$this->tool} --version", $environment); if (!$header) { error("Valgrind returned no version info for {$this->tool}, cannot proceed.\n". "Please check if Valgrind is installed and the tool is named correctly."); } $count = 0; $version = preg_replace("/valgrind-(\d+)\.(\d+)\.(\d+)([.\w_-]+)?(\s+)/", '$1.$2.$3', $header, 1, $count); if ($count != 1) { error("Valgrind returned invalid version info (\"{$header}\") for {$this->tool}, cannot proceed."); } $this->version = $version; $this->header = sprintf( "%s (%s)", trim($header), $this->tool); $this->version_3_8_0 = version_compare($version, '3.8.0', '>='); } public function wrapCommand(string $cmd, string $memcheck_filename, bool $check_all): string { $vcmd = "valgrind -q --tool={$this->tool} --trace-children=yes"; if ($check_all) { $vcmd .= ' --smc-check=all'; } /* --vex-iropt-register-updates=allregs-at-mem-access is necessary for phpdbg watchpoint tests */ if ($this->version_3_8_0) { return "$vcmd --vex-iropt-register-updates=allregs-at-mem-access --log-file=$memcheck_filename $cmd"; } return "$vcmd --vex-iropt-precise-memory-exns=yes --log-file=$memcheck_filename $cmd"; } } class TestFile { private string $fileName; private array $sections = ['TEST' => '']; private const ALLOWED_SECTIONS = [ 'EXPECT', 'EXPECTF', 'EXPECTREGEX', 'EXPECTREGEX_EXTERNAL', 'EXPECT_EXTERNAL', 'EXPECTF_EXTERNAL', 'EXPECTHEADERS', 'POST', 'POST_RAW', 'GZIP_POST', 'DEFLATE_POST', 'PUT', 'GET', 'COOKIE', 'ARGS', 'FILE', 'FILEEOF', 'FILE_EXTERNAL', 'REDIRECTTEST', 'CAPTURE_STDIO', 'STDIN', 'CGI', 'PHPDBG', 'INI', 'ENV', 'EXTENSIONS', 'SKIPIF', 'XFAIL', 'XLEAK', 'AFTER', 'CLEAN', 'CREDITS', 'DESCRIPTION', 'CONFLICTS', 'WHITESPACE_SENSITIVE', ]; /** * @throws BorkageException */ public function __construct(string $fileName, bool $inRedirect) { $this->fileName = $fileName; $this->readFile(); $this->validateAndProcess($inRedirect); } public function hasSection(string $name): bool { return isset($this->sections[$name]); } public function hasAllSections(string ...$names): bool { foreach ($names as $section) { if (!isset($this->sections[$section])) { return false; } } return true; } public function hasAnySections(string ...$names): bool { foreach ($names as $section) { if (isset($this->sections[$section])) { return true; } } return false; } public function sectionNotEmpty(string $name): bool { return !empty($this->sections[$name]); } /** * @throws Exception */ public function getSection(string $name): string { if (!isset($this->sections[$name])) { throw new Exception("Section $name not found"); } return $this->sections[$name]; } public function getName(): string { return trim($this->getSection('TEST')); } public function isCGI(): bool { return $this->sectionNotEmpty('CGI') || $this->sectionNotEmpty('GET') || $this->sectionNotEmpty('POST') || $this->sectionNotEmpty('GZIP_POST') || $this->sectionNotEmpty('DEFLATE_POST') || $this->sectionNotEmpty('POST_RAW') || $this->sectionNotEmpty('PUT') || $this->sectionNotEmpty('COOKIE') || $this->sectionNotEmpty('EXPECTHEADERS'); } /** * TODO Refactor to make it not needed */ public function setSection(string $name, string $value): void { $this->sections[$name] = $value; } /** * Load the sections of the test file * @throws BorkageException */ private function readFile(): void { $fp = fopen($this->fileName, "rb") or error("Cannot open test file: {$this->fileName}"); if (!feof($fp)) { $line = fgets($fp); if ($line === false) { throw new BorkageException("cannot read test"); } } else { throw new BorkageException("empty test [{$this->fileName}]"); } if (strncmp('--TEST--', $line, 8)) { throw new BorkageException("tests must start with --TEST-- [{$this->fileName}]"); } $section = 'TEST'; $secfile = false; $secdone = false; while (!feof($fp)) { $line = fgets($fp); if ($line === false) { break; } // Match the beginning of a section. if (preg_match('/^--([_A-Z]+)--/', $line, $r)) { $section = (string) $r[1]; if (isset($this->sections[$section]) && $this->sections[$section]) { throw new BorkageException("duplicated $section section"); } // check for unknown sections if (!in_array($section, self::ALLOWED_SECTIONS)) { throw new BorkageException('Unknown section "' . $section . '"'); } $this->sections[$section] = ''; $secfile = $section == 'FILE' || $section == 'FILEEOF' || $section == 'FILE_EXTERNAL'; $secdone = false; continue; } // Add to the section text. if (!$secdone) { $this->sections[$section] .= $line; } // End of actual test? if ($secfile && preg_match('/^===DONE===\s*$/', $line)) { $secdone = true; } } fclose($fp); } /** * @throws BorkageException */ private function validateAndProcess(bool $inRedirect): void { // the redirect section allows a set of tests to be reused outside of // a given test dir if ($this->hasSection('REDIRECTTEST')) { if ($inRedirect) { throw new BorkageException("Can't redirect a test from within a redirected test"); } return; } if (!$this->hasSection('PHPDBG') && $this->hasSection('FILE') + $this->hasSection('FILEEOF') + $this->hasSection('FILE_EXTERNAL') != 1) { throw new BorkageException("missing section --FILE--"); } if ($this->hasSection('FILEEOF')) { $this->sections['FILE'] = preg_replace("/[\r\n]+$/", '', $this->sections['FILEEOF']); unset($this->sections['FILEEOF']); } foreach (['FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX'] as $prefix) { // For grepping: FILE_EXTERNAL, EXPECT_EXTERNAL, EXPECTF_EXTERNAL, EXPECTREGEX_EXTERNAL $key = $prefix . '_EXTERNAL'; if ($this->hasSection($key)) { // don't allow tests to retrieve files from anywhere but this subdirectory $dir = dirname($this->fileName); $fileName = $dir . '/' . trim(str_replace('..', '', $this->getSection($key))); if (file_exists($fileName)) { $this->sections[$prefix] = file_get_contents($fileName); } else { throw new BorkageException("could not load --" . $key . "-- " . $dir . '/' . trim($fileName)); } } } if (($this->hasSection('EXPECT') + $this->hasSection('EXPECTF') + $this->hasSection('EXPECTREGEX')) != 1) { throw new BorkageException("missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--"); } if ($this->hasSection('PHPDBG') && !$this->hasSection('STDIN')) { $this->sections['STDIN'] = $this->sections['PHPDBG'] . "\n"; } } } function init_output_buffers(): void { // Delete as much output buffers as possible. while (@ob_end_clean()) { } if (ob_get_level()) { echo "Not all buffers were deleted.\n"; } } function check_proc_open_function_exists(): void { if (!function_exists('proc_open')) { echo << #include #else #include "win32/time.h" #include #endif #include "TSRM.h" #include "SAPI.h" #include "zend_extensions.h" #include "main/php_ini.h" #include "ext/standard/head.h" #include "ext/standard/html.h" #include "ext/standard/info.h" #include "ext/standard/php_string.h" #include "php_globals.h" #include "main/php_output.h" #include "ext/standard/php_var.h" #include "php_xdebug.h" #include "php_xdebug_arginfo.h" #include "base/base.h" #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT # include "base/ctrl_socket.h" #endif #include "base/filter.h" #include "coverage/code_coverage.h" #include "develop/monitor.h" #include "develop/stack.h" #include "develop/superglobals.h" #include "debugger/com.h" #include "gcstats/gc_stats.h" #include "lib/usefulstuff.h" #include "lib/lib.h" #include "lib/llist.h" #include "lib/log.h" #include "lib/mm.h" #include "lib/var_export_html.h" #include "lib/var_export_line.h" #include "lib/var_export_text.h" #include "profiler/profiler.h" #include "tracing/tracing.h" static zend_result (*xdebug_orig_post_startup_cb)(void); static zend_result xdebug_post_startup(void); int xdebug_include_or_eval_handler(zend_execute_data *execute_data); /* True globals */ int zend_xdebug_initialised = 0; int xdebug_global_mode = 0; zend_module_entry xdebug_module_entry = { STANDARD_MODULE_HEADER, "xdebug", ext_functions, PHP_MINIT(xdebug), PHP_MSHUTDOWN(xdebug), PHP_RINIT(xdebug), PHP_RSHUTDOWN(xdebug), PHP_MINFO(xdebug), XDEBUG_VERSION, NO_MODULE_GLOBALS, ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xdebug), STANDARD_MODULE_PROPERTIES_EX }; ZEND_DECLARE_MODULE_GLOBALS(xdebug) #if COMPILE_DL_XDEBUG ZEND_GET_MODULE(xdebug) # ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE(); # endif #endif static PHP_INI_MH(OnUpdateServer) { DUMP_TOK(server); } static PHP_INI_MH(OnUpdateGet) { DUMP_TOK(get); } static PHP_INI_MH(OnUpdatePost) { DUMP_TOK(post); } static PHP_INI_MH(OnUpdateCookie) { DUMP_TOK(cookie); } static PHP_INI_MH(OnUpdateFiles) { DUMP_TOK(files); } static PHP_INI_MH(OnUpdateEnv) { DUMP_TOK(env); } static PHP_INI_MH(OnUpdateRequest) { DUMP_TOK(request); } static PHP_INI_MH(OnUpdateSession) { DUMP_TOK(session); } static PHP_INI_MH(OnUpdateStartWithRequest) { if (!new_value) { return FAILURE; } if (!xdebug_lib_set_start_with_request(ZSTR_VAL(new_value))) { return FAILURE; } return SUCCESS; } static PHP_INI_MH(OnUpdateStartUponError) { if (!new_value) { return FAILURE; } if (!xdebug_lib_set_start_upon_error(ZSTR_VAL(new_value))) { return FAILURE; } return SUCCESS; } static PHP_INI_MH(OnUpdateRemovedSetting) { if (! (EG(error_reporting) & E_DEPRECATED)) { return SUCCESS; } if (new_value && ZSTR_LEN(new_value) > 0 && strncmp("This setting", ZSTR_VAL(new_value), 11) != 0) { xdebug_log_ex( XLOG_CHAN_CONFIG, XLOG_CRIT, "REMOVED", "The setting '%s' has been removed, see the upgrading guide at %supgrade_guide#changed-%s", ZSTR_VAL(entry->name), xdebug_lib_docs_base(), ZSTR_VAL(entry->name) ); } return FAILURE; } static PHP_INI_MH(OnUpdateChangedSetting) { if (! (EG(error_reporting) & E_DEPRECATED)) { return SUCCESS; } if (new_value && ZSTR_LEN(new_value) > 0 && strncmp("This setting", ZSTR_VAL(new_value), 11) != 0) { xdebug_log_ex( XLOG_CHAN_CONFIG, XLOG_CRIT, "CHANGED", "The setting '%s' has been renamed, see the upgrading guide at %supgrade_guide#changed-%s", ZSTR_VAL(entry->name), xdebug_lib_docs_base(), ZSTR_VAL(entry->name) ); } return FAILURE; } #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT static PHP_INI_MH(OnUpdateCtrlSocket) { if (!new_value) { return FAILURE; } if (!xdebug_lib_set_control_socket_granularity(ZSTR_VAL(new_value))) { return FAILURE; } return SUCCESS; } #endif #ifdef P_tmpdir # define XDEBUG_TEMP_DIR P_tmpdir #else # ifdef PHP_WIN32 # define XDEBUG_TEMP_DIR "C:\\Windows\\Temp" # else # define XDEBUG_TEMP_DIR "/tmp" # endif #endif ZEND_INI_DISP(display_removed_setting) { ZEND_PUTS("(setting removed in Xdebug 3)"); } ZEND_INI_DISP(display_changed_setting) { ZEND_PUTS("(setting renamed in Xdebug 3)"); } #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT ZEND_INI_DISP(display_control_socket) { switch (XINI_BASE(control_socket_granularity)) { case XDEBUG_CONTROL_SOCKET_OFF: ZEND_PUTS("off"); break; case XDEBUG_CONTROL_SOCKET_DEFAULT: php_printf("time: %ldms", XINI_BASE(control_socket_threshold_ms)); break; case XDEBUG_CONTROL_SOCKET_TIME: php_printf("time: %ldms", XINI_BASE(control_socket_threshold_ms)); break; } } #endif #define XDEBUG_REMOVED_INI_ENTRY(n) PHP_INI_ENTRY_EX(("" # n), "This setting has been removed, see the upgrading guide at https://xdebug.org/docs/upgrade_guide#removed-" # n, PHP_INI_ALL, OnUpdateRemovedSetting, display_removed_setting) #define XDEBUG_CHANGED_INI_ENTRY(n) PHP_INI_ENTRY_EX(("" # n), "This setting has been changed, see the upgrading guide at https://xdebug.org/docs/upgrade_guide#changed-" # n, PHP_INI_ALL, OnUpdateChangedSetting, display_changed_setting) static const char *xdebug_start_with_request_types[5] = { "", "default", "yes", "no", "trigger" }; ZEND_INI_DISP(display_start_with_request) { char *value; if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { value = ZSTR_VAL(ini_entry->orig_value); } else if (ini_entry->value) { value = ZSTR_VAL(ini_entry->value); } else { value = NULL; } if (value) { ZEND_PUTS(xdebug_start_with_request_types[xdebug_lib_get_start_with_request()]); } else { ZEND_PUTS("?"); } } static const char *xdebug_start_upon_error_types[4] = { "", "default", "yes", "no" }; ZEND_INI_DISP(display_start_upon_error) { char *value; if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { value = ZSTR_VAL(ini_entry->orig_value); } else if (ini_entry->value) { value = ZSTR_VAL(ini_entry->value); } else { value = NULL; } if (value) { ZEND_PUTS(xdebug_start_upon_error_types[xdebug_lib_get_start_upon_error()]); } else { ZEND_PUTS("?"); } } #if HAVE_XDEBUG_ZLIB # define USE_COMPRESSION_DEFAULT "1" #else # define USE_COMPRESSION_DEFAULT "0" #endif PHP_INI_BEGIN() /* Library settings */ STD_PHP_INI_ENTRY("xdebug.mode", "develop", PHP_INI_SYSTEM, OnUpdateString, settings.library.requested_mode, zend_xdebug_globals, xdebug_globals) PHP_INI_ENTRY_EX( "xdebug.start_with_request", "default", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStartWithRequest, display_start_with_request) PHP_INI_ENTRY_EX( "xdebug.start_upon_error", "default", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStartUponError, display_start_upon_error) STD_PHP_INI_ENTRY("xdebug.output_dir", XDEBUG_TEMP_DIR, PHP_INI_ALL, OnUpdateString, settings.library.output_dir, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.use_compression", USE_COMPRESSION_DEFAULT, PHP_INI_ALL, OnUpdateBool, settings.library.use_compression, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.trigger_value", "", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, settings.library.trigger_value, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.file_link_format", "", PHP_INI_ALL, OnUpdateString, settings.library.file_link_format, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.filename_format", "", PHP_INI_ALL, OnUpdateString, settings.library.filename_format, zend_xdebug_globals, xdebug_globals) #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT PHP_INI_ENTRY_EX("xdebug.control_socket", "default", PHP_INI_ALL, OnUpdateCtrlSocket, display_control_socket) #endif STD_PHP_INI_ENTRY("xdebug.log", "", PHP_INI_ALL, OnUpdateString, settings.library.log, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.log_level", XLOG_DEFAULT, PHP_INI_ALL, OnUpdateLong, settings.library.log_level, zend_xdebug_globals, xdebug_globals) /* Variable display settings */ STD_PHP_INI_ENTRY("xdebug.var_display_max_children", "128", PHP_INI_ALL, OnUpdateLong, settings.library.display_max_children, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.var_display_max_data", "512", PHP_INI_ALL, OnUpdateLong, settings.library.display_max_data, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.var_display_max_depth", "3", PHP_INI_ALL, OnUpdateLong, settings.library.display_max_depth, zend_xdebug_globals, xdebug_globals) /* Base settings */ STD_PHP_INI_ENTRY("xdebug.max_nesting_level", "512", PHP_INI_ALL, OnUpdateLong, settings.base.max_nesting_level, zend_xdebug_globals, xdebug_globals) /* Develop settings */ STD_PHP_INI_ENTRY("xdebug.cli_color", "0", PHP_INI_ALL, OnUpdateLong, settings.develop.cli_color, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.force_display_errors", "0", PHP_INI_SYSTEM, OnUpdateBool, settings.develop.force_display_errors, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.force_error_reporting", "0", PHP_INI_SYSTEM, OnUpdateLong, settings.develop.force_error_reporting, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.halt_level", "0", PHP_INI_ALL, OnUpdateLong, settings.develop.halt_level, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.max_stack_frames", "-1", PHP_INI_ALL, OnUpdateLong, settings.develop.max_stack_frames, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.show_error_trace", "0", PHP_INI_ALL, OnUpdateBool, settings.develop.show_error_trace, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.show_exception_trace", "0", PHP_INI_ALL, OnUpdateBool, settings.develop.show_ex_trace, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.show_local_vars", "0", PHP_INI_ALL, OnUpdateBool, settings.develop.show_local_vars, zend_xdebug_globals, xdebug_globals) /* Dump superglobals settings */ PHP_INI_ENTRY("xdebug.dump.COOKIE", NULL, PHP_INI_ALL, OnUpdateCookie) PHP_INI_ENTRY("xdebug.dump.ENV", NULL, PHP_INI_ALL, OnUpdateEnv) PHP_INI_ENTRY("xdebug.dump.FILES", NULL, PHP_INI_ALL, OnUpdateFiles) PHP_INI_ENTRY("xdebug.dump.GET", NULL, PHP_INI_ALL, OnUpdateGet) PHP_INI_ENTRY("xdebug.dump.POST", NULL, PHP_INI_ALL, OnUpdatePost) PHP_INI_ENTRY("xdebug.dump.REQUEST", NULL, PHP_INI_ALL, OnUpdateRequest) PHP_INI_ENTRY("xdebug.dump.SERVER", NULL, PHP_INI_ALL, OnUpdateServer) PHP_INI_ENTRY("xdebug.dump.SESSION", NULL, PHP_INI_ALL, OnUpdateSession) STD_PHP_INI_BOOLEAN("xdebug.dump_globals", "1", PHP_INI_ALL, OnUpdateBool, settings.develop.dump_globals, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.dump_once", "1", PHP_INI_ALL, OnUpdateBool, settings.develop.dump_once, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.dump_undefined", "0", PHP_INI_ALL, OnUpdateBool, settings.develop.dump_undefined, zend_xdebug_globals, xdebug_globals) /* Profiler settings */ STD_PHP_INI_ENTRY("xdebug.profiler_output_name", "cachegrind.out.%p", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, settings.profiler.profiler_output_name, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.profiler_append", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, settings.profiler.profiler_append, zend_xdebug_globals, xdebug_globals) /* Xdebug Cloud */ STD_PHP_INI_ENTRY("xdebug.cloud_id", "", PHP_INI_SYSTEM, OnUpdateString, settings.debugger.cloud_id, zend_xdebug_globals, xdebug_globals) /* Step debugger settings */ STD_PHP_INI_ENTRY("xdebug.client_host", "localhost", PHP_INI_ALL, OnUpdateString, settings.debugger.client_host, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.client_port", XDEBUG_CLIENT_PORT_S, PHP_INI_ALL, OnUpdateLong, settings.debugger.client_port, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.discover_client_host", "0", PHP_INI_ALL, OnUpdateBool, settings.debugger.discover_client_host, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.client_discovery_header", "HTTP_X_FORWARDED_FOR,REMOTE_ADDR", PHP_INI_ALL, OnUpdateString, settings.debugger.client_discovery_header, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.idekey", "", PHP_INI_ALL, OnUpdateString, settings.debugger.ide_key_setting, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.connect_timeout_ms", "200", PHP_INI_ALL, OnUpdateLong, settings.debugger.connect_timeout_ms, zend_xdebug_globals, xdebug_globals) /* Scream support */ STD_PHP_INI_BOOLEAN("xdebug.scream", "0", PHP_INI_ALL, OnUpdateBool, settings.develop.do_scream, zend_xdebug_globals, xdebug_globals) /* GC Stats support */ STD_PHP_INI_ENTRY("xdebug.gc_stats_output_name", "gcstats.%p", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, settings.gc_stats.output_name, zend_xdebug_globals, xdebug_globals) /* Tracing settings */ STD_PHP_INI_ENTRY("xdebug.trace_output_name", "trace.%c", PHP_INI_ALL, OnUpdateString, settings.tracing.trace_output_name, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.trace_format", "0", PHP_INI_ALL, OnUpdateLong, settings.tracing.trace_format, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_ENTRY("xdebug.trace_options", "0", PHP_INI_ALL, OnUpdateLong, settings.tracing.trace_options, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.collect_assignments", "0", PHP_INI_ALL, OnUpdateBool, settings.tracing.collect_assignments, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.collect_params", "1", PHP_INI_ALL, OnUpdateBool, settings.tracing.collect_params, zend_xdebug_globals, xdebug_globals) STD_PHP_INI_BOOLEAN("xdebug.collect_return", "0", PHP_INI_ALL, OnUpdateBool, settings.tracing.collect_return, zend_xdebug_globals, xdebug_globals) /* Removed/Changed settings */ XDEBUG_CHANGED_INI_ENTRY(xdebug.auto_trace) XDEBUG_REMOVED_INI_ENTRY(xdebug.collect_includes) XDEBUG_REMOVED_INI_ENTRY(xdebug.collect_vars) XDEBUG_CHANGED_INI_ENTRY(xdebug.coverage_enable) XDEBUG_CHANGED_INI_ENTRY(xdebug.default_enable) XDEBUG_CHANGED_INI_ENTRY(xdebug.gc_stats_enable) XDEBUG_CHANGED_INI_ENTRY(xdebug.gc_stats_output_dir) XDEBUG_REMOVED_INI_ENTRY(xdebug.overload_var_dump) XDEBUG_CHANGED_INI_ENTRY(xdebug.profiler_enable) XDEBUG_CHANGED_INI_ENTRY(xdebug.profiler_enable_trigger) XDEBUG_CHANGED_INI_ENTRY(xdebug.profiler_enable_trigger_value) XDEBUG_CHANGED_INI_ENTRY(xdebug.profiler_output_dir) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_autostart) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_connect_back) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_enable) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_host) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_log) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_log_level) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_mode) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_port) XDEBUG_CHANGED_INI_ENTRY(xdebug.remote_timeout) XDEBUG_REMOVED_INI_ENTRY(xdebug.show_mem_delta) XDEBUG_CHANGED_INI_ENTRY(xdebug.trace_output_dir) XDEBUG_CHANGED_INI_ENTRY(xdebug.trace_enable_trigger) XDEBUG_CHANGED_INI_ENTRY(xdebug.trace_enable_trigger_value) PHP_INI_END() static void xdebug_init_base_globals(xdebug_base_globals_t *xg) { xg->stack = NULL; xg->in_debug_info = 0; xg->output_is_tty = OUTPUT_NOT_CHECKED; xg->in_execution = 0; xg->in_var_serialisation = 0; xg->error_reporting_override = 0; xg->error_reporting_overridden = 0; xg->filter_type_code_coverage = XDEBUG_FILTER_NONE; xg->filter_type_stack = XDEBUG_FILTER_NONE; xg->filter_type_tracing = XDEBUG_FILTER_NONE; xg->filters_code_coverage = NULL; xg->filters_stack = NULL; xg->filters_tracing = NULL; xg->php_version_compile_time = PHP_VERSION; xg->php_version_run_time = zend_get_module_version("standard"); xdebug_nanotime_init(xg); } static void php_xdebug_init_globals(zend_xdebug_globals *xg) { memset(&xg->globals, 0, sizeof(xg->globals)); xdebug_init_library_globals(&xg->globals.library); xdebug_init_base_globals(&xg->globals.base); if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { xdebug_init_coverage_globals(&xg->globals.coverage); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_init_debugger_globals(&xg->globals.debugger); } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_init_develop_globals(&xg->globals.develop); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_init_profiler_globals(&xg->globals.profiler); } if (XDEBUG_MODE_IS(XDEBUG_MODE_GCSTATS)) { xdebug_init_gc_stats_globals(&xg->globals.gc_stats); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_init_tracing_globals(&xg->globals.tracing); } } static void php_xdebug_shutdown_globals(zend_xdebug_globals *xg) { if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_deinit_develop_globals(&xg->globals.develop); } } static void xdebug_env_config(void) { char *config = getenv("XDEBUG_CONFIG"); xdebug_arg *parts; int i; /* XDEBUG_CONFIG format: XDEBUG_CONFIG=var=val var=val */ if (!config) { return; } parts = xdebug_arg_ctor(); xdebug_explode(" ", config, parts, -1); for (i = 0; i < parts->c; ++i) { const char *name = NULL; char *envvar = parts->args[i]; char *envval = NULL; char *eq = strchr(envvar, '='); if (!eq || !*eq) { continue; } *eq = 0; envval = eq + 1; if (!*envval) { continue; } if (strcasecmp(envvar, "discover_client_host") == 0) { name = "xdebug.discover_client_host"; } else if (strcasecmp(envvar, "client_port") == 0) { name = "xdebug.client_port"; } else if (strcasecmp(envvar, "client_host") == 0) { name = "xdebug.client_host"; } else if (strcasecmp(envvar, "cloud_id") == 0) { name = "xdebug.cloud_id"; } else if (strcasecmp(envvar, "idekey") == 0) { name = "xdebug.idekey"; } else if (strcasecmp(envvar, "output_dir") == 0) { name = "xdebug.output_dir"; } else if (strcasecmp(envvar, "profiler_output_name") == 0) { name = "xdebug.profiler_output_name"; } else if (strcasecmp(envvar, "log") == 0) { name = "xdebug.log"; } else if (strcasecmp(envvar, "log_level") == 0) { name = "xdebug.log_level"; } else if (strcasecmp(envvar, "cli_color") == 0) { name = "xdebug.cli_color"; } if (name) { zend_string *ini_name = zend_string_init(name, strlen(name), 0); zend_string *ini_val = zend_string_init(envval, strlen(envval), 0); zend_alter_ini_entry(ini_name, ini_val, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE); zend_string_release(ini_val); zend_string_release(ini_name); } } xdebug_arg_dtor(parts); } int xdebug_is_output_tty(void) { if (XG_BASE(output_is_tty) == OUTPUT_NOT_CHECKED) { #ifndef PHP_WIN32 XG_BASE(output_is_tty) = isatty(STDOUT_FILENO); #else XG_BASE(output_is_tty) = getenv("ANSICON") != NULL; #endif } return (XG_BASE(output_is_tty)); } PHP_MINIT_FUNCTION(xdebug) { ZEND_INIT_MODULE_GLOBALS(xdebug, php_xdebug_init_globals, php_xdebug_shutdown_globals); REGISTER_INI_ENTRIES(); xdebug_coverage_register_constants(INIT_FUNC_ARGS_PASSTHRU); xdebug_filter_register_constants(INIT_FUNC_ARGS_PASSTHRU); xdebug_tracing_register_constants(INIT_FUNC_ARGS_PASSTHRU); /* Locking in mode as it currently is */ if (!xdebug_lib_set_mode(XG(settings.library.requested_mode))) { xdebug_lib_set_mode("develop"); } if (XDEBUG_MODE_IS_OFF()) { return SUCCESS; } xdebug_library_minit(); xdebug_base_minit(INIT_FUNC_ARGS_PASSTHRU); if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_debugger_minit(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_develop_minit(INIT_FUNC_ARGS_PASSTHRU); } if (XDEBUG_MODE_IS(XDEBUG_MODE_GCSTATS)) { xdebug_gcstats_minit(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_minit(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_tracing_minit(INIT_FUNC_ARGS_PASSTHRU); } /* Overload the "include_or_eval" opcode if the mode is 'debug' or 'trace' */ if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG) || XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_register_with_opcode_multi_handler(ZEND_INCLUDE_OR_EVAL, xdebug_include_or_eval_handler); } /* Coverage must be last, as it has a catch all override for opcodes */ if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { xdebug_coverage_minit(INIT_FUNC_ARGS_PASSTHRU); } if (zend_xdebug_initialised == 0) { zend_error(E_WARNING, "Xdebug MUST be loaded as a Zend extension"); } return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(xdebug) { if (XDEBUG_MODE_IS_OFF()) { #ifdef ZTS ts_free_id(xdebug_globals_id); #endif return SUCCESS; } if (XDEBUG_MODE_IS(XDEBUG_MODE_GCSTATS)) { xdebug_gcstats_mshutdown(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_mshutdown(); } xdebug_library_mshutdown(); #ifdef ZTS ts_free_id(xdebug_globals_id); #else php_xdebug_shutdown_globals(&xdebug_globals); #endif return SUCCESS; } static void xdebug_init_auto_globals(void) { zend_is_auto_global_str((char*) ZEND_STRL("_ENV")); zend_is_auto_global_str((char*) ZEND_STRL("_GET")); zend_is_auto_global_str((char*) ZEND_STRL("_POST")); zend_is_auto_global_str((char*) ZEND_STRL("_COOKIE")); zend_is_auto_global_str((char*) ZEND_STRL("_REQUEST")); zend_is_auto_global_str((char*) ZEND_STRL("_FILES")); zend_is_auto_global_str((char*) ZEND_STRL("_SERVER")); zend_is_auto_global_str((char*) ZEND_STRL("_SESSION")); } PHP_RINIT_FUNCTION(xdebug) { #if defined(ZTS) && defined(COMPILE_DL_XDEBUG) ZEND_TSRMLS_CACHE_UPDATE(); #endif if (XDEBUG_MODE_IS_OFF()) { return SUCCESS; } /* Get xdebug ini entries from the environment also, this can override the idekey if one is set */ xdebug_env_config(); xdebug_library_rinit(); if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { xdebug_coverage_rinit(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_debugger_rinit(); if (xdebug_debugger_bailout_if_no_exec_requested()) { zend_bailout(); } } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_develop_rinit(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_GCSTATS)) { xdebug_gcstats_rinit(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_rinit(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_tracing_rinit(); } xdebug_init_auto_globals(); /* Only enabled extended info when it is not disabled */ CG(compiler_options) = CG(compiler_options) | ZEND_COMPILE_EXTENDED_STMT; xdebug_base_rinit(); return SUCCESS; } ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xdebug) { if (XDEBUG_MODE_IS_OFF()) { return SUCCESS; } if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { xdebug_coverage_post_deactivate(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_debugger_post_deactivate(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_develop_post_deactivate(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_PROFILING)) { xdebug_profiler_post_deactivate(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_TRACING)) { xdebug_tracing_post_deactivate(); } xdebug_base_post_deactivate(); xdebug_library_post_deactivate(); return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(xdebug) { if (XDEBUG_MODE_IS_OFF()) { return SUCCESS; } if (XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) { xdebug_develop_rshutdown(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_GCSTATS)) { xdebug_gcstats_rshutdown(); } xdebug_base_rshutdown(); return SUCCESS; } PHP_MINFO_FUNCTION(xdebug) { xdebug_print_info(); if (zend_xdebug_initialised == 0) { php_info_print_table_start(); php_info_print_table_header(1, "XDEBUG NOT LOADED AS ZEND EXTENSION"); php_info_print_table_end(); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_debugger_minfo(); } DISPLAY_INI_ENTRIES(); } ZEND_DLEXPORT void xdebug_statement_call(zend_execute_data *frame) { zend_op_array *op_array = &frame->func->op_array; int lineno; if (XDEBUG_MODE_IS_OFF()) { return; } if (!EG(current_execute_data)) { return; } lineno = EG(current_execute_data)->opline->lineno; if (XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) { xdebug_coverage_count_line_if_active(op_array, op_array->filename, lineno); } if (XDEBUG_MODE_IS(XDEBUG_MODE_STEP_DEBUG)) { xdebug_debugger_statement_call(op_array->filename, lineno); } #if HAVE_XDEBUG_CONTROL_SOCKET_SUPPORT xdebug_control_socket_dispatch(); #endif } ZEND_DLEXPORT int xdebug_zend_startup(zend_extension *extension) { xdebug_library_zend_startup(); xdebug_debugger_zend_startup(); zend_xdebug_initialised = 1; xdebug_orig_post_startup_cb = zend_post_startup_cb; zend_post_startup_cb = xdebug_post_startup; return zend_startup_module(&xdebug_module_entry); } static zend_result xdebug_post_startup(void) { if (xdebug_orig_post_startup_cb) { int (*cb)(void) = xdebug_orig_post_startup_cb; xdebug_orig_post_startup_cb = NULL; if (cb() != SUCCESS) { return FAILURE; } } xdebug_base_post_startup(); return SUCCESS; } ZEND_DLEXPORT void xdebug_zend_shutdown(zend_extension *extension) { xdebug_debugger_zend_shutdown(); xdebug_library_zend_shutdown(); } ZEND_DLEXPORT void xdebug_init_oparray(zend_op_array *op_array) { if (XDEBUG_MODE_IS_OFF()) { return; } xdebug_coverage_init_oparray(op_array); } #ifndef ZEND_EXT_API #define ZEND_EXT_API ZEND_DLEXPORT #endif ZEND_EXT_API zend_extension_version_info extension_version_info = { ZEND_EXTENSION_API_NO, (char*) ZEND_EXTENSION_BUILD_ID }; ZEND_DLEXPORT zend_extension zend_extension_entry = { (char*) XDEBUG_NAME, (char*) XDEBUG_VERSION, (char*) XDEBUG_AUTHOR, (char*) XDEBUG_URL_FAQ, (char*) XDEBUG_COPYRIGHT_SHORT, xdebug_zend_startup, xdebug_zend_shutdown, NULL, /* activate_func_t */ NULL, /* deactivate_func_t */ NULL, /* message_handler_func_t */ NULL, /* op_array_handler_func_t */ xdebug_statement_call, /* statement_handler_func_t */ NULL, /* fcall_begin_handler_func_t */ NULL, /* fcall_end_handler_func_t */ xdebug_init_oparray, /* op_array_ctor_func_t */ NULL, /* op_array_dtor_func_t */ STANDARD_ZEND_EXTENSION_PROPERTIES }; #endif xdebug-3.4.3/php_xdebug.h0000664000175000017500000000657315011062311014564 0ustar derickderick/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002-2025 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.01 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | https://xdebug.org/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | derick@xdebug.org so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #ifndef PHP_XDEBUG_H #define PHP_XDEBUG_H #define XDEBUG_NAME "Xdebug" #define XDEBUG_VERSION "3.4.3" #define XDEBUG_AUTHOR "Derick Rethans" #define XDEBUG_COPYRIGHT "Copyright (c) 2002-2025 by Derick Rethans" #define XDEBUG_COPYRIGHT_SHORT "Copyright (c) 2002-2025" #define XDEBUG_URL "https://xdebug.org" #define XDEBUG_URL_FAQ "https://xdebug.org/docs/faq#api" #include "lib/php-header.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "base/base_globals.h" #include "coverage/branch_info.h" #include "coverage/code_coverage.h" #include "debugger/debugger.h" #include "develop/develop.h" #include "lib/lib.h" #include "gcstats/gc_stats.h" #include "profiler/profiler.h" #include "tracing/tracing.h" #include "lib/compat.h" #include "lib/hash.h" #include "lib/llist.h" #include "lib/vector.h" #include "lib/timing.h" extern zend_module_entry xdebug_module_entry; #define phpext_xdebug_ptr &xdebug_module_entry #define OUTPUT_NOT_CHECKED -1 #define OUTPUT_IS_TTY 1 #define OUTPUT_NOT_TTY 0 #ifdef PHP_WIN32 #define PHP_XDEBUG_API __declspec(dllexport) #else #define PHP_XDEBUG_API #endif #ifdef ZTS #include "TSRM.h" #endif #include "main/SAPI.h" #define XDEBUG_ALLOWED_HALT_LEVELS (E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE ) PHP_MINIT_FUNCTION(xdebug); PHP_MSHUTDOWN_FUNCTION(xdebug); PHP_RINIT_FUNCTION(xdebug); PHP_RSHUTDOWN_FUNCTION(xdebug); PHP_MINFO_FUNCTION(xdebug); ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xdebug); int xdebug_is_output_tty(); ZEND_BEGIN_MODULE_GLOBALS(xdebug) struct { xdebug_base_globals_t base; xdebug_coverage_globals_t coverage; xdebug_debugger_globals_t debugger; xdebug_develop_globals_t develop; xdebug_gc_stats_globals_t gc_stats; xdebug_library_globals_t library; xdebug_profiler_globals_t profiler; xdebug_tracing_globals_t tracing; } globals; struct { xdebug_base_settings_t base; xdebug_coverage_settings_t coverage; xdebug_debugger_settings_t debugger; xdebug_develop_settings_t develop; xdebug_gc_stats_settings_t gc_stats; xdebug_library_settings_t library; xdebug_profiler_settings_t profiler; xdebug_tracing_settings_t tracing; } settings; ZEND_END_MODULE_GLOBALS(xdebug) #ifdef ZTS #define XG(v) TSRMG(xdebug_globals_id, zend_xdebug_globals *, v) #else #define XG(v) (xdebug_globals.v) #endif #define XG_BASE(v) (XG(globals.base.v)) #define XINI_BASE(v) (XG(settings.base.v)) #endif xdebug-3.4.3/php_xdebug_arginfo.h0000664000175000017500000002146115011062311016262 0ustar derickderick/* This is a generated file, edit the .stub.php file instead. * Stub hash: abb3d6c16210384b6ac37292e3e46713125c8b18 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xdebug_break, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_call_class, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, depth, IS_LONG, 0, "2") ZEND_END_ARG_INFO() #define arginfo_xdebug_call_file arginfo_xdebug_call_class #define arginfo_xdebug_call_function arginfo_xdebug_call_class #define arginfo_xdebug_call_line arginfo_xdebug_call_class #define arginfo_xdebug_code_coverage_started arginfo_xdebug_break #define arginfo_xdebug_connect_to_client arginfo_xdebug_break ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_debug_zval, 0, 0, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, varname, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_xdebug_debug_zval_stdout arginfo_xdebug_debug_zval ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_dump_superglobals, 0, 0, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xdebug_get_code_coverage, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_get_collected_errors, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, emptyList, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xdebug_get_function_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xdebug_get_function_stack, 0, 0, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() #define arginfo_xdebug_get_gc_run_count arginfo_xdebug_get_function_count #define arginfo_xdebug_get_gc_total_collected_roots arginfo_xdebug_get_function_count #define arginfo_xdebug_get_gcstats_filename arginfo_xdebug_dump_superglobals #define arginfo_xdebug_get_headers arginfo_xdebug_get_code_coverage #define arginfo_xdebug_get_monitored_functions arginfo_xdebug_get_code_coverage #define arginfo_xdebug_get_profiler_filename arginfo_xdebug_dump_superglobals #define arginfo_xdebug_get_stack_depth arginfo_xdebug_get_function_count #define arginfo_xdebug_get_tracefile_name arginfo_xdebug_dump_superglobals ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_info, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, category, IS_STRING, 0, "null") ZEND_END_ARG_INFO() #define arginfo_xdebug_is_debugger_active arginfo_xdebug_break #define arginfo_xdebug_memory_usage arginfo_xdebug_get_function_count ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xdebug_notify, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_xdebug_peak_memory_usage arginfo_xdebug_get_function_count ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_print_function_stack, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "\"user triggered\"") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_set_filter, 0, 0, 3) ZEND_ARG_TYPE_INFO(0, group, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, listType, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, configuration, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_start_code_coverage, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_xdebug_start_error_collection arginfo_xdebug_dump_superglobals ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_start_function_monitor, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, listOfFunctionsToMonitor, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_start_gcstats, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, gcstatsFile, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xdebug_start_trace, 0, 0, IS_STRING, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, traceFile, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_stop_code_coverage, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cleanUp, _IS_BOOL, 0, "true") ZEND_END_ARG_INFO() #define arginfo_xdebug_stop_error_collection arginfo_xdebug_dump_superglobals #define arginfo_xdebug_stop_function_monitor arginfo_xdebug_dump_superglobals #define arginfo_xdebug_stop_gcstats arginfo_xdebug_dump_superglobals #define arginfo_xdebug_stop_trace arginfo_xdebug_dump_superglobals ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xdebug_time_index, 0, 0, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_xdebug_var_dump, 0, 0, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, variable, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_FUNCTION(xdebug_break); ZEND_FUNCTION(xdebug_call_class); ZEND_FUNCTION(xdebug_call_file); ZEND_FUNCTION(xdebug_call_function); ZEND_FUNCTION(xdebug_call_line); ZEND_FUNCTION(xdebug_code_coverage_started); ZEND_FUNCTION(xdebug_connect_to_client); ZEND_FUNCTION(xdebug_debug_zval); ZEND_FUNCTION(xdebug_debug_zval_stdout); ZEND_FUNCTION(xdebug_dump_superglobals); ZEND_FUNCTION(xdebug_get_code_coverage); ZEND_FUNCTION(xdebug_get_collected_errors); ZEND_FUNCTION(xdebug_get_function_count); ZEND_FUNCTION(xdebug_get_function_stack); ZEND_FUNCTION(xdebug_get_gc_run_count); ZEND_FUNCTION(xdebug_get_gc_total_collected_roots); ZEND_FUNCTION(xdebug_get_gcstats_filename); ZEND_FUNCTION(xdebug_get_headers); ZEND_FUNCTION(xdebug_get_monitored_functions); ZEND_FUNCTION(xdebug_get_profiler_filename); ZEND_FUNCTION(xdebug_get_stack_depth); ZEND_FUNCTION(xdebug_get_tracefile_name); ZEND_FUNCTION(xdebug_info); ZEND_FUNCTION(xdebug_is_debugger_active); ZEND_FUNCTION(xdebug_memory_usage); ZEND_FUNCTION(xdebug_notify); ZEND_FUNCTION(xdebug_peak_memory_usage); ZEND_FUNCTION(xdebug_print_function_stack); ZEND_FUNCTION(xdebug_set_filter); ZEND_FUNCTION(xdebug_start_code_coverage); ZEND_FUNCTION(xdebug_start_error_collection); ZEND_FUNCTION(xdebug_start_function_monitor); ZEND_FUNCTION(xdebug_start_gcstats); ZEND_FUNCTION(xdebug_start_trace); ZEND_FUNCTION(xdebug_stop_code_coverage); ZEND_FUNCTION(xdebug_stop_error_collection); ZEND_FUNCTION(xdebug_stop_function_monitor); ZEND_FUNCTION(xdebug_stop_gcstats); ZEND_FUNCTION(xdebug_stop_trace); ZEND_FUNCTION(xdebug_time_index); ZEND_FUNCTION(xdebug_var_dump); static const zend_function_entry ext_functions[] = { ZEND_FE(xdebug_break, arginfo_xdebug_break) ZEND_FE(xdebug_call_class, arginfo_xdebug_call_class) ZEND_FE(xdebug_call_file, arginfo_xdebug_call_file) ZEND_FE(xdebug_call_function, arginfo_xdebug_call_function) ZEND_FE(xdebug_call_line, arginfo_xdebug_call_line) ZEND_FE(xdebug_code_coverage_started, arginfo_xdebug_code_coverage_started) ZEND_FE(xdebug_connect_to_client, arginfo_xdebug_connect_to_client) ZEND_FE(xdebug_debug_zval, arginfo_xdebug_debug_zval) ZEND_FE(xdebug_debug_zval_stdout, arginfo_xdebug_debug_zval_stdout) ZEND_FE(xdebug_dump_superglobals, arginfo_xdebug_dump_superglobals) ZEND_FE(xdebug_get_code_coverage, arginfo_xdebug_get_code_coverage) ZEND_FE(xdebug_get_collected_errors, arginfo_xdebug_get_collected_errors) ZEND_FE(xdebug_get_function_count, arginfo_xdebug_get_function_count) ZEND_FE(xdebug_get_function_stack, arginfo_xdebug_get_function_stack) ZEND_FE(xdebug_get_gc_run_count, arginfo_xdebug_get_gc_run_count) ZEND_FE(xdebug_get_gc_total_collected_roots, arginfo_xdebug_get_gc_total_collected_roots) ZEND_FE(xdebug_get_gcstats_filename, arginfo_xdebug_get_gcstats_filename) ZEND_FE(xdebug_get_headers, arginfo_xdebug_get_headers) ZEND_FE(xdebug_get_monitored_functions, arginfo_xdebug_get_monitored_functions) ZEND_FE(xdebug_get_profiler_filename, arginfo_xdebug_get_profiler_filename) ZEND_FE(xdebug_get_stack_depth, arginfo_xdebug_get_stack_depth) ZEND_FE(xdebug_get_tracefile_name, arginfo_xdebug_get_tracefile_name) ZEND_FE(xdebug_info, arginfo_xdebug_info) ZEND_FE(xdebug_is_debugger_active, arginfo_xdebug_is_debugger_active) ZEND_FE(xdebug_memory_usage, arginfo_xdebug_memory_usage) ZEND_FE(xdebug_notify, arginfo_xdebug_notify) ZEND_FE(xdebug_peak_memory_usage, arginfo_xdebug_peak_memory_usage) ZEND_FE(xdebug_print_function_stack, arginfo_xdebug_print_function_stack) ZEND_FE(xdebug_set_filter, arginfo_xdebug_set_filter) ZEND_FE(xdebug_start_code_coverage, arginfo_xdebug_start_code_coverage) ZEND_FE(xdebug_start_error_collection, arginfo_xdebug_start_error_collection) ZEND_FE(xdebug_start_function_monitor, arginfo_xdebug_start_function_monitor) ZEND_FE(xdebug_start_gcstats, arginfo_xdebug_start_gcstats) ZEND_FE(xdebug_start_trace, arginfo_xdebug_start_trace) ZEND_FE(xdebug_stop_code_coverage, arginfo_xdebug_stop_code_coverage) ZEND_FE(xdebug_stop_error_collection, arginfo_xdebug_stop_error_collection) ZEND_FE(xdebug_stop_function_monitor, arginfo_xdebug_stop_function_monitor) ZEND_FE(xdebug_stop_gcstats, arginfo_xdebug_stop_gcstats) ZEND_FE(xdebug_stop_trace, arginfo_xdebug_stop_trace) ZEND_FE(xdebug_time_index, arginfo_xdebug_time_index) ZEND_FE(xdebug_var_dump, arginfo_xdebug_var_dump) ZEND_FE_END };