diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 6b8ac5064..04b9c5a16 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -977,6 +977,7 @@ void CurlEasyRequest::resetState(void) mTimeoutPolicy = NULL; mTimeout = NULL; mHandleEventsTarget = NULL; + mResult = CURLE_FAILED_INIT; applyDefaultOptions(); } @@ -1322,6 +1323,34 @@ void BufferedCurlEasyRequest::bad_socket(void) mResponder = NULL; } +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) +static AIPerServicePtr sConnections[64]; + +void BufferedCurlEasyRequest::connection_established(int connectionnr) +{ + PerService_rat per_service_r(*mPerServicePtr); + int n = per_service_r->connection_established(); + llassert(sConnections[connectionnr] == NULL); // Only one service can use a connection at a time. + llassert_always(connectionnr < 64); + sConnections[connectionnr] = mPerServicePtr; + Dout(dc::curlio, (void*)get_lockobj() << " Connection established (#" << connectionnr << "). Now " << n << " connections [" << (void*)&*per_service_r << "]."); + llassert(sConnections[connectionnr] != NULL); +} + +void BufferedCurlEasyRequest::connection_closed(int connectionnr) +{ + if (sConnections[connectionnr] == NULL) + { + Dout(dc::curlio, "Closing connection that never connected (#" << connectionnr << ")."); + return; + } + PerService_rat per_service_r(*sConnections[connectionnr]); + int n = per_service_r->connection_closed(); + sConnections[connectionnr] = NULL; + Dout(dc::curlio, (void*)get_lockobj() << " Connection closed (#" << connectionnr << "); " << n << " connections remaining [" << (void*)&*per_service_r << "]."); +} +#endif + void BufferedCurlEasyRequest::resetState(void) { llassert(!mResponder); diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index c99264db8..8a58e2c98 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -75,6 +75,7 @@ AIPerService::AIPerService(void) : mConcurrentConnections(CurlConcurrentConnectionsPerService), mApprovedRequests(0), mTotalAdded(0), + mEstablishedConnections(0), mUsedCT(0), mCTInUse(0) { @@ -230,6 +231,7 @@ AIPerServicePtr AIPerService::instance(std::string const& servicename) if (iter == instance_map_w->end()) { iter = instance_map_w->insert(instance_map_type::value_type(servicename, new RefCountedThreadSafePerService)).first; + Dout(dc::curlio, "Created new service \"" << servicename << "\" [" << (void*)&*PerService_rat(*iter->second) << "]"); } // Note: the creation of AIPerServicePtr MUST be protected by the lock on sInstanceMap (see release()). return iter->second; diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index f81236997..188d383bf 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -169,6 +169,7 @@ class AIPerService { int mConcurrentConnections; // The maximum number of allowed concurrent connections to this service. int mApprovedRequests; // The number of approved requests for this service by approveHTTPRequestFor that were not added to the command queue yet. int mTotalAdded; // Number of active easy handles with this service. + int mEstablishedConnections; // Number of connected sockets to this service. U32 mUsedCT; // Bit mask with one bit per capability type. A '1' means the capability was in use since the last resetUsedCT(). U32 mCTInUse; // Bit mask with one bit per capability type. A '1' means the capability is in use right now. @@ -203,6 +204,9 @@ class AIPerService { } } public: + int connection_established(void) { mEstablishedConnections++; return mEstablishedConnections; } + int connection_closed(void) { mEstablishedConnections--; return mEstablishedConnections; } + static bool is_approved(AICapabilityType capability_type) { return (((U32)1 << capability_type) & approved_mask); } static U32 CT2mask(AICapabilityType capability_type) { return (U32)1 << capability_type; } void resetUsedCt(void) { mUsedCT = mCTInUse; } diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index af8b6b267..cbb9a293d 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -457,6 +457,9 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { // So far the only such error I've seen is HTTP_BAD_REQUEST. bool upload_error_status(void) const { return mStatus == HTTP_BAD_REQUEST; } + // Returns true if the request was a success. + bool success(void) const { return mResult == CURLE_OK && mStatus >= 200 && mStatus < 400; } + // Return true when prepRequest was already called and the object has not been // invalidated as a result of calling timed_out(). bool isValid(void) const { return mResponder; } @@ -466,6 +469,12 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { // Return true if any data was received. bool received_data(void) const { return mTotalRawBytes > 0; } + +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) + // Connection accounting for debug purposes. + void connection_established(int connectionnr); + void connection_closed(int connectionnr); +#endif }; inline ThreadSafeBufferedCurlEasyRequest* CurlEasyRequest::get_lockobj(void) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index f6f971516..64a6b69fe 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -1845,12 +1845,11 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons { AICurlEasyRequest_wat curl_easy_request_w(**iter); bool downloaded_something = curl_easy_request_w->received_data(); + bool success = curl_easy_request_w->success(); res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle); capability_type = curl_easy_request_w->capability_type(); per_service = curl_easy_request_w->getPerServicePtr(); - CURLcode code; - curl_easy_request_w->getResult(&code, NULL); - PerService_wat(*per_service)->removed_from_multi_handle(capability_type, downloaded_something, code == CURLE_OK); // (About to be) removed from mAddedEasyRequests. + PerService_wat(*per_service)->removed_from_multi_handle(capability_type, downloaded_something, success); // (About to be) removed from mAddedEasyRequests. #ifdef SHOW_ASSERT curl_easy_request_w->mRemovedPerCommand = as_per_command; #endif @@ -2356,6 +2355,58 @@ int debug_callback(CURL* handle, curl_infotype infotype, char* buf, size_t size, { request->mDebugIsHeadOrGetMethod = true; } + if (infotype == CURLINFO_TEXT) + { + if (!strncmp(buf, "STATE: WAITCONNECT => ", 22)) + { + if (buf[22] == 'P' || buf[22] == 'D') // PROTOCONNECT or DO. + { + int n = size - 1; + while (buf[n] != ')') + { + llassert(n > 56); + --n; + } + int connectionnr = 0; + int factor = 1; + do + { + llassert(n > 56); + --n; + connectionnr += factor * (buf[n] - '0'); + factor *= 10; + } + while(buf[n - 1] != '#'); + // A new connection was established. + request->connection_established(connectionnr); + } + else + { + llassert(buf[22] == 'C'); // COMPLETED (connection failure). + } + } + else if (!strncmp(buf, "Closing connection", 18)) + { + int n = size - 1; + while (!std::isdigit(buf[n])) + { + llassert(n > 20); + --n; + } + int connectionnr = 0; + int factor = 1; + do + { + llassert(n > 19); + connectionnr += factor * (buf[n] - '0'); + factor *= 10; + --n; + } + while(buf[n] != '#'); + // A connection was closed. + request->connection_closed(connectionnr); + } + } #ifdef DEBUG_CURLIO if (!debug_curl_print_debug(handle)) diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp index 60e9fc4e6..58bc2f689 100644 --- a/indra/newview/aihttpview.cpp +++ b/indra/newview/aihttpview.cpp @@ -80,6 +80,7 @@ void AIServiceBar::draw() U32 is_used; U32 is_inuse; int total_added; + int established_connections; int concurrent_connections; size_t bandwidth; { @@ -87,6 +88,7 @@ void AIServiceBar::draw() is_used = per_service_r->is_used(); is_inuse = per_service_r->is_inuse(); total_added = per_service_r->mTotalAdded; + established_connections = per_service_r->mEstablishedConnections; concurrent_connections = per_service_r->mConcurrentConnections; bandwidth = per_service_r->bandwidth().truncateData(AIHTTPView::getTime_40ms()); cts = per_service_r->mCapabilityType; // Not thread-safe, but we're only reading from it and only using the results to show in a debug console. @@ -145,7 +147,11 @@ void AIServiceBar::draw() start += LLFontGL::getFontMonospace()->getWidth(text); } start = mHTTPView->updateColumn(mc_col, start); +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) + text = llformat(" | %d,%d/%d", total_added, established_connections, concurrent_connections); +#else text = llformat(" | %d/%d", total_added, concurrent_connections); +#endif LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); start = mHTTPView->updateColumn(bw_col, start);