Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion runtime-light/stdlib/diagnostics/functions-call-stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

#include "runtime-light/stdlib/diagnostics/logs.h"

#define DUMP_BUILTIN_CALL_STATS(builtin_name, builtin_call) (kphp::log::debug("built-in called: " builtin_name), builtin_call)
#define DUMP_BUILTIN_CALL_STATS(builtin_name, builtin_call) (kphp::log::debug("std function called: " builtin_name), builtin_call)
63 changes: 63 additions & 0 deletions tests/aggregate_std_function_invocations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import csv
import argparse
import pathlib
import collections

SCRIPT_DIR = pathlib.Path(__file__).parent
DEFAULT_OUTPUT = SCRIPT_DIR / 'tmp' / 'std_function_invocations.csv'

def main():
parser = argparse.ArgumentParser(
description="Aggregates std function invocations from multiple JSON files into a single CSV report."
)

parser.add_argument(
'inputs',
nargs='+',
type=pathlib.Path,
help='Paths to the JSON files generated by test groups',
)

parser.add_argument(
'--output', '-o',
type=pathlib.Path,
default=DEFAULT_OUTPUT,
help=f'Path to the output CSV file (default: {DEFAULT_OUTPUT})',
)

args = parser.parse_args()
total_counts = collections.defaultdict(int)

for file_path in args.inputs:
if file_path.exists():
try:
data = json.loads(file_path.read_text(encoding='utf-8'))
if isinstance(data, dict):
for func_name, count in data.items():
total_counts[func_name] += count
else:
print(f"Warning: {file_path} content is not a dictionary. Skipping.")
except json.JSONDecodeError:
print(f"Error: {file_path} is not a valid JSON file. Skipping.")
else:
print(f"Info: {file_path} not found. It might have been disabled.")

args.output.parent.mkdir(parents=True, exist_ok=True)

try:
with args.output.open('w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Built-in', 'Call count'])

sorted_results = sorted(total_counts.items(), key=lambda item: (item[1], item[0]))

for func, count in sorted_results:
writer.writerow([func, count])

print(f"Success! Aggregated report saved to: {args.output.absolute()}")
except Exception as e:
print(f"Error saving report: {e}")

if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -353,15 +353,11 @@ rpc_queue_next
rpc_server_fetch_request
rpc_server_store_response
rpc_tl_pending_queries_count
rpc_tl_query
rpc_tl_query_result
rpc_tl_query_result_synchronously
rsort
rtrim
sched_yield
sched_yield_sleep
serialize
set_detect_incorrect_encoding_names_warning
set_fail_rpc_on_int32_overflow
set_json_log_on_timeout_mode
set_migration_php8_warning
Expand Down Expand Up @@ -413,9 +409,6 @@ tan
time
to_array_debug
trim
typed_rpc_tl_query
typed_rpc_tl_query_result
typed_rpc_tl_query_result_synchronously
uasort
UberH3$$geoToH3
UberH3$$h3ToParent
Expand Down Expand Up @@ -454,3 +447,81 @@ warning
wordwrap
zstd_compress
zstd_uncompress
flush
getopt
is_readable
kphp_extended_instance_cache_metrics_init
md5_file
fgetcsv
fgets
file_exists
file_put_contents
prepare_search_query
set_json_log_demangle_stacktrace
strftime
ini_set
get_webserver_stats
thread_pool_test_load
chmod
getimagesize
copy
dirname
feof
filemtime
fputcsv
fseek
ftell
is_dir
mkdir
rename
rewind
scandir
tempnam
stream_context_create
kphp_job_worker_fetch_request
kphp_job_worker_start
kphp_job_worker_start_multi
kphp_job_worker_start_no_reply
kphp_job_worker_store_response
kphp_backtrace
kphp_turn_on_host_tag_in_inner_statshouse_metrics_toggle
memory_get_static_usage
openssl_x509_checkpurpose
openssl_x509_verify
preg_last_error
new_rpc_connection
store_finish
fetch_lookup_int
gethostbynamel
localtime
system
kphp_tracing_func_enter_branch
kphp_tracing_get_current_active_span
kphp_tracing_get_level
kphp_tracing_get_root_span
kphp_tracing_init
kphp_tracing_register_enums_provider
kphp_tracing_register_on_finish
kphp_tracing_register_rpc_details_provider
msgpack_deserialize_safe
kphp_tracing_set_level
kphp_tracing_start_span
KphpDiv$$assignTraceCtx
KphpDiv$$generateTraceCtxForChild
preg_split
KphpSpan$$addAttributeBool
KphpSpan$$addAttributeFloat
KphpSpan$$addAttributeInt
KphpSpan$$addAttributeString
KphpSpan$$addEvent
KphpSpan$$exclude
KphpSpan$$finish
KphpSpan$$finishWithError
KphpSpan$$updateName
KphpSpanEvent$$addAttributeInt
KphpSpanEvent$$addAttributeString
ignore_user_abort
send_http_103_early_hints
profiler_set_function_label
vk_dot_product
vk_flex
19 changes: 18 additions & 1 deletion tests/kphp_ci_pipeline_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import math
import multiprocessing
import os
import pathlib
import shlex
import signal
import subprocess
import sys
Expand All @@ -13,6 +15,10 @@
from python.lib.nocc_for_kphp_tester import nocc_env


K2_KPHP_TRACKED_BUILTINS_LIST_PATH = pathlib.Path(__file__).parent / "k2_kphp_tracked_builtins_list.txt"
K2_KPHP_TRACKED_BUILTINS_LIST = K2_KPHP_TRACKED_BUILTINS_LIST_PATH.read_text()


class TestStatus(Enum):
FAILED = red("failed")
PASSED = green("passed")
Expand Down Expand Up @@ -348,9 +354,11 @@ def _calculate_pytest_jobs_count(default_percent: int = 95) -> int:
name="k2-kphp-tests",
description="run k2-kphp tests with cxx={}".format(args.cxx_name),
cmd="KPHP_TESTS_POLYFILLS_REPO={kphp_polyfills_repo} "
"KPHP_TRACKED_BUILTINS_LIST={K2_KPHP_TRACKED_BUILTINS_LIST} "
"{kphp_runner} -j{jobs} --cxx-name {cxx_name} --k2-bin {k2_bin}".format(
jobs=n_cpu,
kphp_polyfills_repo=kphp_polyfills_repo,
K2_KPHP_TRACKED_BUILTINS_LIST=shlex.quote(K2_KPHP_TRACKED_BUILTINS_LIST),
kphp_runner=kphp_test_runner,
cxx_name=args.cxx_name,
k2_bin=args.k2_bin,
Expand Down Expand Up @@ -427,10 +435,17 @@ def _calculate_pytest_jobs_count(default_percent: int = 95) -> int:
runner.add_test_group(
name="k2-functional-tests",
description="run k2-kphp functional tests with cxx={}".format(args.cxx_name),
cmd="KPHP_TESTS_POLYFILLS_REPO={kphp_polyfills_repo} KPHP_CXX={cxx_name} K2_BIN={k2_bin} K2_MONITORING_ABORT_HANGING_GLOBAL_TASK=false K2_MONITORING_ABORT_HANGING_REQUEST_TASK=false python3 -m pytest --basetemp={base_tempdir} --tb=native -n{jobs} {functional_tests_dir}".format(
cmd="KPHP_TESTS_POLYFILLS_REPO={kphp_polyfills_repo} "
"KPHP_CXX={cxx_name} "
"K2_BIN={k2_bin} "
"K2_MONITORING_ABORT_HANGING_GLOBAL_TASK=false "
"K2_MONITORING_ABORT_HANGING_REQUEST_TASK=false "
"KPHP_TRACKED_BUILTINS_LIST={K2_KPHP_TRACKED_BUILTINS_LIST} "
"python3 -m pytest --basetemp={base_tempdir} --tb=native -n{jobs} {functional_tests_dir}".format(
kphp_polyfills_repo=kphp_polyfills_repo,
cxx_name=args.cxx_name,
k2_bin=args.k2_bin,
K2_KPHP_TRACKED_BUILTINS_LIST=shlex.quote(K2_KPHP_TRACKED_BUILTINS_LIST),
jobs=n_cpu,
functional_tests_dir=functional_tests_dir,
base_tempdir=os.path.expanduser(
Expand Down Expand Up @@ -474,6 +489,7 @@ def _calculate_pytest_jobs_count(default_percent: int = 95) -> int:
"KPHP_TESTS_INTERGRATION_TESTS_ENABLED=1 "
"KPHP_CXX={cxx_name} "
"K2_BIN={k2_bin} "
"KPHP_TRACKED_BUILTINS_LIST={K2_KPHP_TRACKED_BUILTINS_LIST} "
"python3 -m pytest --tb=native -n{jobs} {tests_dir}".format(
jobs=n_cpu,
lib_dir=os.path.join(runner_dir, "python"),
Expand All @@ -482,6 +498,7 @@ def _calculate_pytest_jobs_count(default_percent: int = 95) -> int:
kphp_polyfills_repo=kphp_polyfills_repo,
cxx_name=args.cxx_name,
k2_bin=args.k2_bin,
K2_KPHP_TRACKED_BUILTINS_LIST=shlex.quote(K2_KPHP_TRACKED_BUILTINS_LIST),
tests_dir=" ".join([
os.path.join(args.kphp_tests_repo, "python/tests/k2_rpc_client/"),
os.path.join(args.kphp_tests_repo, "python/tests/k2_rpc_server/"),
Expand Down
38 changes: 31 additions & 7 deletions tests/kphp_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@
import math
import multiprocessing
import os
import pathlib
import re
import signal
import sys
from functools import partial
from multiprocessing.dummy import Pool as ThreadPool
import typing

from python.lib.colors import red, green, yellow, blue, cyan
from python.lib.file_utils import search_php_bin
from python.lib.nocc_for_kphp_tester import nocc_start_daemon_in_background
from python.lib.kphp_run_once import KphpRunOnce
from python.lib import std_function
from python.lib import tcp

TCP_SERVER_TAG_PREFIX = "tcp_server:"

FILE = pathlib.Path(__file__)
TMP_DIR = FILE.with_name("{}_tmp".format(FILE.stem))


class TestFile:
def __init__(self, file_path, test_tmp_dir, tags, env_vars: dict, out_regexps=None, forbidden_regexps=None):
Expand Down Expand Up @@ -55,13 +61,16 @@ def is_php8(self):
def is_available_for_k2(self):
return "k2_skip" not in self.tags

def make_kphp_once_runner(self, use_nocc, cxx_name, k2_bin):
def make_kphp_once_runner(
self, use_nocc, cxx_name, k2_bin, std_function_invocations: typing.Optional[std_function.Invocations]
):
tester_dir = os.path.abspath(os.path.dirname(__file__))
return KphpRunOnce(
php_script_path=self.file_path,
working_dir=os.path.abspath(os.path.join(self.test_tmp_dir, "working_dir")),
artifacts_dir=os.path.abspath(os.path.join(self.test_tmp_dir, "artifacts")),
php_bin=search_php_bin(php_version=self.php_version),
std_function_invocations=std_function_invocations,
extra_include_dirs=[os.path.join(tester_dir, "php_include")],
vkext_dir=os.path.abspath(os.path.join(tester_dir, os.path.pardir, "objs", "vkext")),
use_nocc=use_nocc,
Expand All @@ -75,7 +84,6 @@ def set_up_env_for_k2(self):
self.env_vars["KPHP_ENABLE_GLOBAL_VARS_MEMORY_STATS"] = "0"
self.env_vars["KPHP_PROFILER"] = "0"
self.env_vars["KPHP_FORCE_LINK_RUNTIME"] = "1"
self.env_vars["KPHP_TRACKED_BUILTINS_LIST"] = KphpRunOnce.K2_KPHP_TRACKED_BUILTINS_LIST


def make_test_file(file_path, test_tmp_dir, test_tags):
Expand Down Expand Up @@ -152,12 +160,11 @@ def test_files_from_list(tests_dir, test_list):

def collect_tests(tests_dir, test_tags, test_list):
tests = []
tmp_dir = "{}_tmp".format(__file__[:-3])
file_it = test_files_from_list(tests_dir, test_list) if test_list else test_files_from_dir(tests_dir)
for root, file in file_it:
if file.endswith(".php") or file.endswith(".phpt"):
test_file_path = os.path.join(root, file)
test_tmp_dir = os.path.join(tmp_dir, os.path.relpath(test_file_path, os.path.dirname(tests_dir)))
test_tmp_dir = os.path.join(TMP_DIR, os.path.relpath(test_file_path, os.path.dirname(tests_dir)))
test_tmp_dir = test_tmp_dir[:-4] if test_tmp_dir.endswith(".php") else test_tmp_dir[:-5]
test_file = make_test_file(test_file_path, test_tmp_dir, test_tags)
if test_file:
Expand Down Expand Up @@ -350,11 +357,13 @@ def run_ok_test(test: TestFile, runner):
return TestResult.passed(test, runner.artifacts)


def run_test(use_nocc, cxx_name, k2_bin, test: TestFile):
def run_test(
use_nocc, cxx_name, k2_bin, std_function_invocations: typing.Optional[std_function.Invocations], test: TestFile
):
if not os.path.exists(test.file_path):
return TestResult.failed(test, None, "can't find test file")

runner = test.make_kphp_once_runner(use_nocc, cxx_name, k2_bin)
runner = test.make_kphp_once_runner(use_nocc, cxx_name, k2_bin, std_function_invocations)
runner.remove_artifacts_dir()
if k2_bin is not None:
test.set_up_env_for_k2()
Expand Down Expand Up @@ -393,17 +402,32 @@ def run_all_tests(tests_dir, jobs, test_tags, no_report, passed_list, test_list,
"tag" if len(test_tags) == 1 else "tags"))
sys.exit(1)

if "KPHP_TRACKED_BUILTINS_LIST" in os.environ:
std_function_invocations = std_function.Invocations(os.environ["KPHP_TRACKED_BUILTINS_LIST"])
else:
std_function_invocations = None

results = []
with ThreadPool(jobs) as pool:
tests_completed = 0
for test_result in pool.imap_unordered(partial(run_test, use_nocc, cxx_name, k2_bin), tests):
for test_result in pool.imap_unordered(partial(run_test, use_nocc, cxx_name, k2_bin, std_function_invocations), tests):
if hack_reference_exit:
print(yellow("Testing process was interrupted"), flush=True)
break
tests_completed = tests_completed + 1
test_result.print_short_report(len(tests), tests_completed)
results.append(test_result)

if std_function_invocations:
std_function_invocations_output_dir = TMP_DIR / "artifacts"
std_function_invocations_output_dir.mkdir(parents=True, exist_ok=True)

std_function_invocations_filename = "std_function_invocations.json"
std_function_invocations_output_path = std_function_invocations_output_dir / std_function_invocations_filename

with open(std_function_invocations_output_path, "w", encoding="utf-8") as f:
std_function_invocations.dump(f)

print("\nTesting results:", flush=True)

skipped = len(tests) - len(results)
Expand Down
Loading
Loading