Skip to content

Commit 06992a9

Browse files
committed
Fix #8639 - Concurrency problem in remote profiler communication mechanism.
1 parent 14b6ee0 commit 06992a9

File tree

1 file changed

+122
-67
lines changed

1 file changed

+122
-67
lines changed

src/jrd/ProfilerManager.cpp

Lines changed: 122 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
#include "../jrd/met_proto.h"
3434
#include "../jrd/pag_proto.h"
3535
#include "../jrd/tra_proto.h"
36+
#include "../common/classes/Spinlock.h"
3637

3738
#include <atomic>
39+
#include <mutex>
3840

3941
#ifdef WIN_NT
4042
#include <process.h>
@@ -100,11 +102,13 @@ namespace
100102
event_t clientEvent;
101103
USHORT bufferSize;
102104
std::atomic<Tag> tag;
105+
std::atomic_uint seq;
106+
SpinLock bufferMutex;
103107
char userName[USERNAME_LENGTH + 1]; // \0 if has PROFILE_ANY_ATTACHMENT
104108
alignas(FB_ALIGNMENT) UCHAR buffer[4096];
105109
};
106110

107-
static const USHORT VERSION = 2;
111+
static const USHORT VERSION = 3;
108112

109113
public:
110114
ProfilerIpc(thread_db* tdbb, MemoryPool& pool, AttNumber aAttachmentId, bool server = false);
@@ -179,7 +183,7 @@ class Jrd::ProfilerListener final
179183
listener->watcherThread();
180184
}
181185

182-
void processCommand(thread_db* tdbb);
186+
void processCommand(thread_db* tdbb, ProfilerIpc::Tag tag, UCharBuffer& buffer);
183187

184188
private:
185189
Attachment* const attachment;
@@ -736,6 +740,8 @@ ProfilerIpc::ProfilerIpc(thread_db* tdbb, MemoryPool& pool, AttNumber aAttachmen
736740
{
737741
Guard guard(this);
738742

743+
header->seq = 0;
744+
739745
if (sharedMemory->eventInit(&header->serverEvent) != FB_SUCCESS)
740746
(Arg::Gds(isc_random) << "ProfilerIpc eventInit(serverEvent) failed").raise();
741747
}
@@ -817,18 +823,17 @@ void ProfilerIpc::internalSendAndReceive(thread_db* tdbb, Tag tag,
817823
}
818824
});
819825

820-
const SLONG value = sharedMemory->eventClear(&header->clientEvent);
826+
const SLONG clientEventCounter = sharedMemory->eventClear(&header->clientEvent);
827+
828+
std::unique_lock bufferMutexLock(header->bufferMutex);
821829

822-
const Tag oldTag = header->tag.exchange(tag);
823-
switch (oldTag)
830+
switch (header->tag)
824831
{
825832
case Tag::NOP:
826-
header->tag = oldTag;
827833
(Arg::Gds(isc_random) << "Remote attachment failed to start listener thread").raise();
828834
break;
829835

830836
case Tag::SERVER_EXITED:
831-
header->tag = oldTag;
832837
(Arg::Gds(isc_random) << "Cannot start remote profile session - attachment exited").raise();
833838
break;
834839

@@ -846,41 +851,49 @@ void ProfilerIpc::internalSendAndReceive(thread_db* tdbb, Tag tag,
846851
fb_assert(inSize <= sizeof(header->buffer));
847852
memcpy(header->buffer, in, inSize);
848853

854+
header->tag = tag;
855+
const auto seq = ++header->seq;
856+
857+
bufferMutexLock.unlock();
858+
849859
if (sharedMemory->eventPost(&header->serverEvent) != FB_SUCCESS)
850860
(Arg::Gds(isc_random) << "Cannot start remote profile session - attachment exited").raise();
851861

862+
const SLONG TIMEOUT = 500 * 1000; // 0.5 sec
863+
const int serverPID = header->serverEvent.event_pid;
864+
865+
while (true)
852866
{
853-
const SLONG TIMEOUT = 500 * 1000; // 0.5 sec
867+
{ // scope
868+
EngineCheckout cout(tdbb, FB_FUNCTION);
854869

855-
const int serverPID = header->serverEvent.event_pid;
856-
while (true)
857-
{
870+
if (sharedMemory->eventWait(&header->clientEvent, clientEventCounter, TIMEOUT) == FB_SUCCESS)
871+
break;
872+
873+
if (serverPID != getpid() && !ISC_check_process_existence(serverPID))
858874
{
859-
EngineCheckout cout(tdbb, FB_FUNCTION);
860-
if (sharedMemory->eventWait(&header->clientEvent, value, TIMEOUT) == FB_SUCCESS)
861-
break;
875+
// Server process was died or exited
876+
fb_assert((header->tag == tag) || header->tag == Tag::SERVER_EXITED);
862877

863-
if (serverPID != getpid() && !ISC_check_process_existence(serverPID))
878+
if (header->tag == tag)
864879
{
865-
// Server process was died or exited
866-
fb_assert((header->tag == tag) || header->tag == Tag::SERVER_EXITED);
867-
868-
if (header->tag == tag)
880+
header->tag = Tag::SERVER_EXITED;
881+
if (header->serverEvent.event_pid)
869882
{
870-
header->tag = Tag::SERVER_EXITED;
871-
if (header->serverEvent.event_pid)
872-
{
873-
sharedMemory->eventFini(&header->serverEvent);
874-
header->serverEvent.event_pid = 0;
875-
}
883+
sharedMemory->eventFini(&header->serverEvent);
884+
header->serverEvent.event_pid = 0;
876885
}
877-
break;
878886
}
887+
888+
break;
879889
}
880-
JRD_reschedule(tdbb, true);
881890
}
891+
892+
JRD_reschedule(tdbb, true);
882893
}
883894

895+
bufferMutexLock.lock();
896+
884897
switch (header->tag)
885898
{
886899
case Tag::SERVER_EXITED:
@@ -977,7 +990,7 @@ void ProfilerListener::watcherThread()
977990
{
978991
while (!exiting)
979992
{
980-
const SLONG value = sharedMemory->eventClear(&header->serverEvent);
993+
const SLONG serverEventCounter = sharedMemory->eventClear(&header->serverEvent);
981994

982995
if (startup)
983996
{
@@ -986,18 +999,38 @@ void ProfilerListener::watcherThread()
986999
}
9871000
else
9881001
{
1002+
ProfilerIpc::Tag tag;
1003+
unsigned seq;
1004+
UCharBuffer buffer;
1005+
9891006
fb_assert(header->tag >= ProfilerIpc::Tag::FIRST_CLIENT_OP);
9901007

9911008
try
9921009
{
9931010
FbLocalStatus statusVector;
9941011
EngineContextHolder tdbb(&statusVector, attachment->getInterface(), FB_FUNCTION);
9951012

996-
processCommand(tdbb);
997-
header->tag = ProfilerIpc::Tag::RESPONSE;
1013+
{ // scope
1014+
std::unique_lock bufferMutexLock(header->bufferMutex);
1015+
1016+
if (header->userName[0] && attachment->getUserName() != header->userName)
1017+
status_exception::raise(Arg::Gds(isc_miss_prvlg) << "PROFILE_ANY_ATTACHMENT");
1018+
1019+
fb_assert(header->tag >= ProfilerIpc::Tag::FIRST_CLIENT_OP);
1020+
1021+
tag = header->tag;
1022+
seq = header->seq;
1023+
memcpy(buffer.getBuffer(header->bufferSize, false), header->buffer, header->bufferSize);
1024+
}
1025+
1026+
processCommand(tdbb, tag, buffer);
1027+
1028+
tag = ProfilerIpc::Tag::RESPONSE;
9981029
}
9991030
catch (const status_exception& e)
10001031
{
1032+
tag = ProfilerIpc::Tag::EXCEPTION;
1033+
10011034
//// TODO: Serialize status vector instead of formated message.
10021035

10031036
const ISC_STATUS* status = e.value();
@@ -1012,32 +1045,51 @@ void ProfilerListener::watcherThread()
10121045
errorMsg += temp;
10131046
}
10141047

1015-
header->bufferSize = MIN(errorMsg.length(), sizeof(header->buffer) - 1);
1016-
strncpy((char*) header->buffer, errorMsg.c_str(), sizeof(header->buffer));
1017-
header->buffer[header->bufferSize] = '\0';
1018-
1019-
header->tag = ProfilerIpc::Tag::EXCEPTION;
1048+
header->bufferSize = MIN(errorMsg.length(), sizeof(header->buffer));
1049+
memcpy(header->buffer, errorMsg.c_str(), header->bufferSize);
10201050
}
10211051

1022-
sharedMemory->eventPost(&header->clientEvent);
1052+
fb_assert(buffer.getCount() <= sizeof(header->buffer));
1053+
1054+
{ // scope
1055+
std::unique_lock bufferMutexLock(header->bufferMutex, std::try_to_lock);
1056+
1057+
// Otherwise a client lost interest in the response.
1058+
if (bufferMutexLock.owns_lock() && header->seq == seq)
1059+
{
1060+
if (header->seq == seq)
1061+
{
1062+
header->tag = tag;
1063+
header->bufferSize = buffer.getCount();
1064+
memcpy(header->buffer, buffer.begin(), buffer.getCount());
1065+
1066+
sharedMemory->eventPost(&header->clientEvent);
1067+
}
1068+
}
1069+
}
10231070
}
10241071

10251072
if (exiting)
10261073
break;
10271074

1028-
sharedMemory->eventWait(&header->serverEvent, value, 0);
1075+
sharedMemory->eventWait(&header->serverEvent, serverEventCounter, 0);
10291076
}
10301077
}
10311078
catch (const Exception& ex)
10321079
{
10331080
iscLogException("Error in profiler watcher thread\n", ex);
10341081
}
10351082

1036-
const ProfilerIpc::Tag oldTag = header->tag.exchange(ProfilerIpc::Tag::SERVER_EXITED);
1037-
if (oldTag >= ProfilerIpc::Tag::FIRST_CLIENT_OP)
1038-
{
1039-
fb_assert(header->clientEvent.event_pid);
1040-
sharedMemory->eventPost(&header->clientEvent);
1083+
{ // scope
1084+
std::unique_lock bufferMutexLock(header->bufferMutex);
1085+
1086+
if (header->tag >= ProfilerIpc::Tag::FIRST_CLIENT_OP)
1087+
{
1088+
fb_assert(header->clientEvent.event_pid);
1089+
sharedMemory->eventPost(&header->clientEvent);
1090+
}
1091+
1092+
header->tag = ProfilerIpc::Tag::SERVER_EXITED;
10411093
}
10421094

10431095
try
@@ -1051,70 +1103,75 @@ void ProfilerListener::watcherThread()
10511103
}
10521104
}
10531105

1054-
void ProfilerListener::processCommand(thread_db* tdbb)
1106+
void ProfilerListener::processCommand(thread_db* tdbb, ProfilerIpc::Tag tag, UCharBuffer& buffer)
10551107
{
1056-
const auto header = ipc->sharedMemory->getHeader();
10571108
const auto profilerManager = attachment->getProfilerManager(tdbb);
10581109

1059-
if (header->userName[0] && attachment->getUserName() != header->userName)
1060-
status_exception::raise(Arg::Gds(isc_miss_prvlg) << "PROFILE_ANY_ATTACHMENT");
1061-
10621110
using Tag = ProfilerIpc::Tag;
10631111

1064-
switch (header->tag)
1112+
switch (tag)
10651113
{
10661114
case Tag::CANCEL_SESSION:
1115+
fb_assert(buffer.isEmpty());
10671116
profilerManager->cancelSession();
1068-
header->bufferSize = 0;
1117+
buffer.resize(0);
10691118
break;
10701119

10711120
case Tag::DISCARD:
1121+
fb_assert(buffer.isEmpty());
10721122
profilerManager->discard();
1073-
header->bufferSize = 0;
1123+
buffer.resize(0);
10741124
break;
10751125

10761126
case Tag::FINISH_SESSION:
10771127
{
1078-
const auto in = reinterpret_cast<const ProfilerPackage::FinishSessionInput::Type*>(header->buffer);
1079-
fb_assert(sizeof(*in) == header->bufferSize);
1128+
const auto in = reinterpret_cast<const ProfilerPackage::FinishSessionInput::Type*>(buffer.begin());
1129+
fb_assert(sizeof(*in) == buffer.getCount());
1130+
10801131
profilerManager->finishSession(tdbb, in->flush);
1081-
header->bufferSize = 0;
1132+
1133+
buffer.resize(0);
10821134
break;
10831135
}
10841136

10851137
case Tag::FLUSH:
1138+
fb_assert(buffer.isEmpty());
10861139
profilerManager->flush();
1087-
header->bufferSize = 0;
1140+
buffer.resize(0);
10881141
break;
10891142

10901143
case Tag::PAUSE_SESSION:
10911144
{
1092-
const auto in = reinterpret_cast<const ProfilerPackage::PauseSessionInput::Type*>(header->buffer);
1093-
fb_assert(sizeof(*in) == header->bufferSize);
1145+
const auto in = reinterpret_cast<const ProfilerPackage::PauseSessionInput::Type*>(buffer.begin());
1146+
fb_assert(sizeof(*in) == buffer.getCount());
1147+
10941148
profilerManager->pauseSession(in->flush);
1095-
header->bufferSize = 0;
1149+
1150+
buffer.resize(0);
10961151
break;
10971152
}
10981153

10991154
case Tag::RESUME_SESSION:
1155+
fb_assert(buffer.isEmpty());
11001156
profilerManager->resumeSession();
1101-
header->bufferSize = 0;
1157+
buffer.resize(0);
11021158
break;
11031159

11041160
case Tag::SET_FLUSH_INTERVAL:
11051161
{
1106-
const auto in = reinterpret_cast<const ProfilerPackage::SetFlushIntervalInput::Type*>(header->buffer);
1107-
fb_assert(sizeof(*in) == header->bufferSize);
1162+
const auto in = reinterpret_cast<const ProfilerPackage::SetFlushIntervalInput::Type*>(buffer.begin());
1163+
fb_assert(sizeof(*in) == buffer.getCount());
11081164

11091165
profilerManager->setFlushInterval(in->flushInterval);
1110-
header->bufferSize = 0;
1166+
1167+
buffer.resize(0);
11111168
break;
11121169
}
11131170

11141171
case Tag::START_SESSION:
11151172
{
1116-
const auto in = reinterpret_cast<const ProfilerPackage::StartSessionInput::Type*>(header->buffer);
1117-
fb_assert(sizeof(*in) == header->bufferSize);
1173+
const auto in = reinterpret_cast<const ProfilerPackage::StartSessionInput::Type*>(buffer.begin());
1174+
fb_assert(sizeof(*in) == buffer.getCount());
11181175

11191176
const string description(in->description.str,
11201177
in->descriptionNull ? 0 : in->description.length);
@@ -1125,14 +1182,12 @@ void ProfilerListener::processCommand(thread_db* tdbb)
11251182
const string pluginOptions(in->pluginOptions.str,
11261183
in->pluginOptionsNull ? 0 : in->pluginOptions.length);
11271184

1128-
const auto out = reinterpret_cast<ProfilerPackage::StartSessionOutput::Type*>(header->buffer);
1129-
static_assert(sizeof(*out) <= sizeof(header->buffer), "Buffer size too small");
1130-
header->bufferSize = sizeof(*out);
1185+
const auto out = reinterpret_cast<ProfilerPackage::StartSessionOutput::Type*>(buffer.begin());
1186+
buffer.resize(sizeof(*out));
11311187

11321188
out->sessionIdNull = FB_FALSE;
11331189
out->sessionId = profilerManager->startSession(tdbb, flushInterval,
11341190
pluginName, description, pluginOptions);
1135-
11361191
break;
11371192
}
11381193

0 commit comments

Comments
 (0)