From 41b3623296c2203acced1a9363c3ef7abd08a158 Mon Sep 17 00:00:00 2001 From: Winlin Date: Wed, 21 Aug 2024 15:39:01 +0800 Subject: [PATCH] API: Support new HTTP API for VALGRIND. v6.0.149 (#4150) New features for valgrind: 1. ST: Support /api/v1/valgrind for leaking check. 2. ST: Support /api/v1/valgrind?check=full|added|changed|new|quick To use Valgrind to detect memory leaks in SRS, even though Valgrind hooks are supported in ST, there are still many false positives. A more reasonable approach is to have Valgrind report incremental memory leaks. This way, global and static variables can be avoided, and detection can be achieved without exiting the program. Follow these steps: 1. Compile SRS with Valgrind support: `./configure --valgrind=on && make` 2. Start SRS with memory leak detection enabled: `valgrind --leak-check=full ./objs/srs -c conf/console.conf` 3. Trigger memory detection by using curl to access the API and generate calibration data. There will still be many false positives, but these can be ignored: `curl http://127.0.0.1:1985/api/v1/valgrind?check=added` 4. Perform load testing or test the suspected leaking functionality, such as RTMP streaming: `ffmpeg -re -i doc/source.flv -c copy -f flv rtmp://127.0.0.1/live/livestream` 5. Stop streaming and wait for SRS to clean up the Source memory, approximately 30 seconds. 6. Perform incremental memory leak detection. The reported leaks will be very accurate at this point: `curl http://127.0.0.1:1985/api/v1/valgrind?check=added` > Note: To avoid interference from the HTTP request itself on Valgrind, SRS uses a separate coroutine to perform periodic checks. Therefore, after accessing the API, you may need to wait a few seconds for the detection to be triggered. --------- Co-authored-by: Jacob Su --- trunk/auto/auto_headers.sh | 5 ++ trunk/doc/CHANGELOG.md | 1 + trunk/src/app/srs_app_gb28181.hpp | 10 +-- trunk/src/app/srs_app_http_api.cpp | 100 ++++++++++++++++++++++++++ trunk/src/app/srs_app_http_api.hpp | 17 +++++ trunk/src/app/srs_app_http_conn.hpp | 2 +- trunk/src/app/srs_app_recv_thread.hpp | 2 +- trunk/src/app/srs_app_rtmp_conn.hpp | 2 +- trunk/src/app/srs_app_server.cpp | 10 ++- trunk/src/app/srs_app_st.hpp | 2 +- trunk/src/core/srs_core_version6.hpp | 2 +- 11 files changed, 142 insertions(+), 11 deletions(-) diff --git a/trunk/auto/auto_headers.sh b/trunk/auto/auto_headers.sh index 1998a9374..82d77186d 100755 --- a/trunk/auto/auto_headers.sh +++ b/trunk/auto/auto_headers.sh @@ -171,6 +171,11 @@ if [ $SRS_SANITIZER_LOG == YES ]; then else srs_undefine_macro "SRS_SANITIZER_LOG" $SRS_AUTO_HEADERS_H fi +if [ $SRS_VALGRIND == YES ]; then + srs_define_macro "SRS_VALGRIND" $SRS_AUTO_HEADERS_H +else + srs_undefine_macro "SRS_VALGRIND" $SRS_AUTO_HEADERS_H +fi ##################################################################################### # for embeded. diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 2aedae016..5d6b42374 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 6.0 Changelog +* v6.0, 2024-08-21, Merge [#4150](https://github.com/ossrs/srs/pull/4150): API: Support new HTTP API for VALGRIND. v6.0.149 (#4150) * v6.0, 2024-08-15, Merge [#4144](https://github.com/ossrs/srs/pull/4144): HTTP-FLV: Crash when multiple viewers. v6.0.148 (#4144) * v6.0, 2024-08-15, Merge [#4142](https://github.com/ossrs/srs/pull/4142): Config: Add more utest for env config. v6.0.147 (#4142) * v6.0, 2024-08-14, Merge [#4141](https://github.com/ossrs/srs/pull/4141): Live: Crash for invalid live stream state when unmount HTTP. v6.0.146 (#4141) diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index be6250948..3ac91a3da 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -188,7 +188,7 @@ public: void on_media_transport(SrsSharedResource media); // Get the candidate for SDP generation, the public IP address for device to connect to. std::string pip(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); private: @@ -305,7 +305,7 @@ private: public: virtual const SrsContextId& get_id(); virtual std::string desc(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); private: @@ -333,7 +333,7 @@ public: // Interface ISrsStartable public: virtual srs_error_t start(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); private: @@ -362,7 +362,7 @@ public: // Interface ISrsStartable public: virtual srs_error_t start(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); private: @@ -422,7 +422,7 @@ public: public: virtual const SrsContextId& get_id(); virtual std::string desc(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); private: diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index c8bc71010..b1bd339a4 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -31,6 +31,11 @@ using namespace std; #include #include +#ifdef SRS_VALGRIND +#include +#include +#endif + #if defined(__linux__) || defined(SRS_OSX) #include #endif @@ -267,6 +272,7 @@ srs_error_t SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r urls->set("clusters", SrsJsonAny::str("origin cluster server API")); urls->set("perf", SrsJsonAny::str("System performance stat")); urls->set("tcmalloc", SrsJsonAny::str("tcmalloc api with params ?page=summary|api")); + urls->set("valgrind", SrsJsonAny::str("valgrind api with params ?check=full|added|changed|new|quick")); SrsJsonObject* tests = SrsJsonAny::object(); obj->set("tests", tests); @@ -1090,6 +1096,100 @@ srs_error_t SrsGoApiTcmalloc::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess } #endif +#ifdef SRS_VALGRIND + +SrsGoApiValgrind::SrsGoApiValgrind() +{ + trd_ = NULL; +} + +SrsGoApiValgrind::~SrsGoApiValgrind() +{ + srs_freep(trd_); +} + +srs_error_t SrsGoApiValgrind::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + srs_error_t err = srs_success; + + if (!trd_) { + trd_ = new SrsSTCoroutine("valgrind", this, _srs_context->get_id()); + if ((err = trd_->start()) != srs_success) { + return srs_error_wrap(err, "start"); + } + } + + string check = r->query_get("check"); + srs_trace("query check=%s", check.c_str()); + + // Must be full|added|changed|new|quick, set to full for other values. + if (check != "full" && check != "added" && check != "changed" && check != "new" && check != "quick") { + srs_warn("force set check=%s to full", check.c_str()); + check = "full"; + } + + // By default, response the json style response. + SrsUniquePtr obj(SrsJsonAny::object()); + + obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); + + SrsJsonObject* res = SrsJsonAny::object(); + res->set("check", SrsJsonAny::str(check.c_str())); + res->set("help", SrsJsonAny::str("?check=full|added|changed|new|quick")); + res->set("see", SrsJsonAny::str("https://valgrind.org/docs/manual/mc-manual.html")); + obj->set("data", res); + + // Does a memory check later. + if (check == "full") { + res->set("call", SrsJsonAny::str("VALGRIND_DO_LEAK_CHECK")); + } else if (check == "quick") { + res->set("call", SrsJsonAny::str("VALGRIND_DO_QUICK_LEAK_CHECK")); + } else if (check == "added") { + res->set("call", SrsJsonAny::str("VALGRIND_DO_ADDED_LEAK_CHECK")); + } else if (check == "changed") { + res->set("call", SrsJsonAny::str("VALGRIND_DO_CHANGED_LEAK_CHECK")); + } else if (check == "new") { + res->set("call", SrsJsonAny::str("VALGRIND_DO_NEW_LEAK_CHECK")); + } + task_ = check; + + return srs_api_response(w, r, obj->dumps()); +} + +srs_error_t SrsGoApiValgrind::cycle() +{ + srs_error_t err = srs_success; + + while (true) { + if ((err = trd_->pull()) != srs_success) { + return srs_error_wrap(err, "pull"); + } + + std::string check = task_; + task_ = ""; + + if (!check.empty()) { + srs_trace("do memory check=%s", check.c_str()); + } + + if (check == "full") { + VALGRIND_DO_LEAK_CHECK; + } else if (check == "quick") { + VALGRIND_DO_QUICK_LEAK_CHECK; + } else if (check == "added") { + VALGRIND_DO_ADDED_LEAK_CHECK; + } else if (check == "changed") { + VALGRIND_DO_CHANGED_LEAK_CHECK; + } else if (check == "new") { + VALGRIND_DO_NEW_LEAK_CHECK; + } + + srs_usleep(3 * SRS_UTIME_SECONDS); + } + + return err; +} +#endif SrsGoApiMetrics::SrsGoApiMetrics() { diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 8bcb30e50..8789d42e9 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -216,6 +216,23 @@ public: }; #endif +#ifdef SRS_VALGRIND +class SrsGoApiValgrind : public ISrsHttpHandler, public ISrsCoroutineHandler +{ +private: + SrsCoroutine* trd_; + std::string task_; +public: + SrsGoApiValgrind(); + virtual ~SrsGoApiValgrind(); +public: + virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +// Interface ISrsCoroutineHandler +public: + virtual srs_error_t cycle(); +}; +#endif + class SrsGoApiMetrics : public ISrsHttpHandler { private: diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index 57f984a49..9d1cc1055 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -94,7 +94,7 @@ public: // Interface ISrsStartable public: virtual srs_error_t start(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); private: diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 5fa011db5..7d29a4b39 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -204,7 +204,7 @@ public: virtual srs_error_t start(); public: virtual srs_error_t pull(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); }; diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index 8c86627fe..9a772ffb7 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -181,7 +181,7 @@ public: // when client cycle thread stop, invoke the on_thread_stop(), which will use server // To remove the client by server->remove(this). virtual srs_error_t start(); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: // The thread cycle function, // when serve connection completed, terminate the loop which will terminate the thread, diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index aff62f02e..16bc99e8c 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -773,9 +773,17 @@ srs_error_t SrsServer::http_handle() // The test api for get tcmalloc stats. // @see Memory Introspection in https://gperftools.github.io/gperftools/tcmalloc.html if ((err = http_api_mux->handle("/api/v1/tcmalloc", new SrsGoApiTcmalloc())) != srs_success) { - return srs_error_wrap(err, "handle tests errors"); + return srs_error_wrap(err, "handle tcmalloc errors"); } #endif + +#ifdef SRS_VALGRIND + // The test api for valgrind. See VALGRIND_DO_LEAK_CHECK in https://valgrind.org/docs/manual/mc-manual.html + if ((err = http_api_mux->handle("/api/v1/valgrind", new SrsGoApiValgrind())) != srs_success) { + return srs_error_wrap(err, "handle valgrind errors"); + } +#endif + // metrics by prometheus if ((err = http_api_mux->handle("/metrics", new SrsGoApiMetrics())) != srs_success) { return srs_error_wrap(err, "handle tests errors"); diff --git a/trunk/src/app/srs_app_st.hpp b/trunk/src/app/srs_app_st.hpp index d7315b20c..11f2c2195 100644 --- a/trunk/src/app/srs_app_st.hpp +++ b/trunk/src/app/srs_app_st.hpp @@ -291,7 +291,7 @@ public: public: virtual const SrsContextId& cid(); virtual void set_cid(const SrsContextId& cid); -// Interface ISrsOneCycleThreadHandler +// Interface ISrsCoroutineHandler public: virtual srs_error_t cycle(); // Interface ISrsResource diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp index 33a00d8c9..9e3f4b4a1 100644 --- a/trunk/src/core/srs_core_version6.hpp +++ b/trunk/src/core/srs_core_version6.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 6 #define VERSION_MINOR 0 -#define VERSION_REVISION 148 +#define VERSION_REVISION 149 #endif