Commit Graph
10 Commits
Author SHA1 Message Date
Calvin EllisonandCalvin Ellison 4fa2630a5b [Core] Avoid O(n^2) header dedup when cloning EF_UNIQ_HEADERS events (#3061)
* events: avoid O(n^2) header dedup when cloning EF_UNIQ_HEADERS events

switch_event_dup() copies a source event header by header via
switch_event_add_header_string(). When the source carries
EF_UNIQ_HEADERS (SWITCH_EVENT_CHANNEL_DATA / REQUEST_PARAMS / MESSAGE --
e.g. a channel's variable list), the destination inherits the flag, so
switch_event_base_add_header() runs a full switch_event_del_header()
linear scan of the partially-built list on every add to enforce
uniqueness. Cloning an N-header event is therefore O(n^2).

That scan is redundant while cloning: the source already enforced
uniqueness on insert, so copying it cannot introduce duplicates
regardless of the per-add scan. Clear EF_UNIQ_HEADERS on the
destination for the duration of the copy loop, then restore it.

This is a hot path. switch_channel_execute_on() -- invoked on ring,
answer, media, transfer, park, record and playback -- calls
switch_core_get_variables() and switch_channel_get_variables(), each of
which dup()s an EF_UNIQ_HEADERS event. On a production voicemail server
carrying ~191 channel variables per call, switch_event_del_header_val()
accounted for 47-69% of all FreeSWITCH CPU time in on-box perf profiles.

Microbenchmark (tests/unit/switch_event.c, switch_event_dup of an
N-header CHANNEL_DATA event, 4000 iterations, time per dup):

    N      before     after
    50     4.80 us    3.19 us
    100   11.34 us    5.88 us
    191   28.29 us   10.99 us   (2.6x)
    400  107.05 us   23.27 us   (4.6x)

"before" scales ~O(n^2); "after" is linear. Behaviour is unchanged: the
clone has identical headers, values and order, keeps EF_UNIQ_HEADERS,
and still enforces uniqueness on subsequent adds (covered by the test).

Signed-off-by: Calvin Ellison <cellison@youmail.com>

* tests: cover switch_event_dup uniqueness and add a dup benchmark

Adds a dup_uniq_bench case to the switch_event unit test. For an
EF_UNIQ_HEADERS (CHANNEL_DATA) source event it verifies the clone
preserves header count, values and the EF_UNIQ_HEADERS flag, and that
re-setting an existing key does not produce a duplicate in the clone.
It also prints a switch_event_dup timing sweep across header counts,
used to validate the O(n^2) -> O(n) change in switch_event_dup().

Signed-off-by: Calvin Ellison <cellison@youmail.com>

* tests: add dup_faithful_copy regression for switch_event_dup

Covers the EF_UNIQ_HEADERS edge case for the O(n^2)->O(n) dup change: a well-formed EF_UNIQ source stays unique through dup, and a malformed source already holding duplicate names (only possible if headers were added before the flag was set) is copied faithfully rather than silently collapsed to the last value.

Signed-off-by: Calvin Ellison <cellison@youmail.com>

* tests: gate dup_uniq_bench behind #ifdef BENCHMARK

The dup_uniq_bench timing sweep is a benchmark rather than a pass/fail correctness test. Guard the whole test with #ifdef BENCHMARK (matching the existing benchmark in this file) so it stays out of normal test runs; dup_faithful_copy continues to cover correctness of the switch_event_dup change.

Signed-off-by: Calvin Ellison <cellison@youmail.com>

* switch_event: clarify EF_UNIQ_HEADERS dup comment, brace one-line test loops

Address review feedback on the switch_event_dup change:

- src/switch_event.c: the fast-path comment claimed todup's headers are "already unique" unconditionally. That only holds when EF_UNIQ_HEADERS is set (names are deduped on insert); reword to scope the claim to that case and to the verbatim-copy intent.

- tests/unit/switch_event.c: expand one-line loops to braced form per the SignalWire coding guidelines.

Signed-off-by: Calvin Ellison <cellison@youmail.com>

---------

Signed-off-by: Calvin Ellison <cellison@youmail.com>
Co-authored-by: Calvin Ellison <cellison@youmail.com>
2026-09-04 19:43:23 +03:00
Dmitry Verenitsin 30cdb387ae Merge commit from fork
The positive-offset branch of `${variable:offset}` expansion advanced
`sub_val` past the cloned heap buffer without bounds-checking `offset`
against `strlen(sub_val)`. Subsequent `strlen()` and `strcat()` then
read adjacent heap memory into the output buffer.
2026-08-08 18:18:50 +03:00
s3rj1k 91f12b5920 [Unit-tests] Fix previously defined AM_* in tests. 2021-02-23 20:59:26 +03:00
Andrey Volk 2865603ad2 [Unit-tests] Fix xml error not finding freeswitch.xml on FST_MINCORE_BEGIN 2019-11-26 01:54:43 +04:00
Seven Du d76cac15fc FS-11615 fix compiler warning 2019-01-17 15:38:13 +08:00
Chris Rienzo 0c8f5ed60d FS-11453 [core] remove dependency to libtap for unit tests
move all core unit tests to tests/unit
2018-12-20 10:18:28 -05:00
William King 7724270d89 FS-8233 convert unit tests frameworks to non-recursive makefiles 2015-10-08 13:01:04 -07:00
William King 4845587be9 FS-7820 using a more appropriate function for printing diags 2015-10-07 10:35:46 -07:00
William King a54e04df00 FS-7820 updated switch_event.c to have benchmark framework, and updated .gitignore file to ignore binary test files 2015-07-10 15:31:11 -07:00
William King 8cb75a6000 FS-7820 rename test file to better track the core functionality it will test 2015-07-10 15:08:58 -07:00