Skip to content

Commit 4136493

Browse files
andy31415pull[bot]
authored andcommitted
Add ability to assert that chip stack is locked by the current thread. (#6551)
* Initial implementation for tracking chip stack locks. marked as log only for darwin and linux targets * Fix logging for lock error locations * Ignore chip stack locking errors if main loop is not yet started * Restyle fixes * Remove unused type * Remove the Platform log module, use DeviceLayer instead * Add platform buildconfig dependency in inet, to allow for lock tracking management * Add platform dependency in unit tests: need lock implementation * Fix bool status lock/unlock ordering
1 parent fedf28b commit 4136493

11 files changed

+204
-2
lines changed

src/include/platform/LockTracker.h

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
*
3+
* Copyright (c) 2021 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
#include <platform/CHIPDeviceBuildConfig.h>
21+
22+
/// Defines support for asserting that the chip stack is locked by the current thread via
23+
/// the macro:
24+
///
25+
/// assertChipStackLockedByCurrentThread()
26+
///
27+
/// Makes use of the following preprocessor macros:
28+
///
29+
/// CHIP_STACK_LOCK_TRACKING_ENABLED - keeps track of who locks/unlocks the chip stack
30+
/// CHIP_STACK_LOCK_TRACKING_ERROR_FATAL - lock tracking errors will cause the chip stack to abort/die
31+
32+
namespace chip {
33+
namespace Platform {
34+
35+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
36+
37+
namespace Internal {
38+
39+
void AssertChipStackLockedByCurrentThread(const char * file, int line);
40+
41+
} // namespace Internal
42+
43+
#define assertChipStackLockedByCurrentThread() ::chip::Platform::Internal::AssertChipStackLockedByCurrentThread(__FILE__, __LINE__)
44+
45+
#else
46+
47+
#define assertChipStackLockedByCurrentThread() (void) 0
48+
49+
#endif
50+
51+
} // namespace Platform
52+
} // namespace chip

src/include/platform/PlatformManager.h

+12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#pragma once
2525

26+
#include <platform/CHIPDeviceBuildConfig.h>
2627
#include <platform/CHIPDeviceEvent.h>
2728

2829
namespace chip {
@@ -94,6 +95,10 @@ class PlatformManager
9495
void UnlockChipStack();
9596
CHIP_ERROR Shutdown();
9697

98+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
99+
bool IsChipStackLockedByCurrentThread() const;
100+
#endif
101+
97102
private:
98103
bool mInitialized = false;
99104
// ===== Members for internal use by the following friends.
@@ -180,6 +185,13 @@ extern PlatformManagerImpl & PlatformMgrImpl();
180185
namespace chip {
181186
namespace DeviceLayer {
182187

188+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
189+
inline bool PlatformManager::IsChipStackLockedByCurrentThread() const
190+
{
191+
return static_cast<const ImplClass *>(this)->_IsChipStackLockedByCurrentThread();
192+
}
193+
#endif
194+
183195
inline CHIP_ERROR PlatformManager::InitChipStack()
184196
{
185197
// NOTE: this is NOT thread safe and cannot be as the chip stack lock is prepared by

src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp

+27-2
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,46 @@ void GenericPlatformManagerImpl_POSIX<ImplClass>::_LockChipStack()
8282
{
8383
int err = pthread_mutex_lock(&mChipStackLock);
8484
assert(err == 0);
85+
86+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
87+
mChipStackIsLocked = true;
88+
mChipStackLockOwnerThread = pthread_self();
89+
#endif
8590
}
8691

8792
template <class ImplClass>
8893
bool GenericPlatformManagerImpl_POSIX<ImplClass>::_TryLockChipStack()
8994
{
90-
return pthread_mutex_trylock(&mChipStackLock) == 0;
95+
bool locked = (pthread_mutex_trylock(&mChipStackLock) == 0);
96+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
97+
if (locked)
98+
{
99+
mChipStackIsLocked = true;
100+
mChipStackLockOwnerThread = pthread_self();
101+
}
102+
#endif
103+
return locked;
91104
}
92105

93106
template <class ImplClass>
94107
void GenericPlatformManagerImpl_POSIX<ImplClass>::_UnlockChipStack()
95108
{
109+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
110+
mChipStackIsLocked = false;
111+
#endif
112+
96113
int err = pthread_mutex_unlock(&mChipStackLock);
97114
assert(err == 0);
98115
}
99116

117+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
118+
template <class ImplClass>
119+
bool GenericPlatformManagerImpl_POSIX<ImplClass>::_IsChipStackLockedByCurrentThread() const
120+
{
121+
return !mMainLoopStarted || (mChipStackIsLocked && (pthread_equal(pthread_self(), mChipStackLockOwnerThread)));
122+
}
123+
#endif
124+
100125
template <class ImplClass>
101126
CHIP_ERROR GenericPlatformManagerImpl_POSIX<ImplClass>::_StartChipTimer(int64_t aMilliseconds)
102127
{
@@ -210,7 +235,7 @@ template <class ImplClass>
210235
void * GenericPlatformManagerImpl_POSIX<ImplClass>::EventLoopTaskMain(void * arg)
211236
{
212237
ChipLogDetail(DeviceLayer, "CHIP task running");
213-
238+
static_cast<GenericPlatformManagerImpl_POSIX<ImplClass> *>(arg)->Impl()->mMainLoopStarted = true;
214239
static_cast<GenericPlatformManagerImpl_POSIX<ImplClass> *>(arg)->Impl()->RunEventLoop();
215240
return nullptr;
216241
}

src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h

+10
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ class GenericPlatformManagerImpl_POSIX : public GenericPlatformManagerImpl<ImplC
6767
pthread_attr_t mChipTaskAttr;
6868
struct sched_param mChipTaskSchedParam;
6969

70+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
71+
bool mMainLoopStarted = false;
72+
bool mChipStackIsLocked = false;
73+
pthread_t mChipStackLockOwnerThread;
74+
#endif
75+
7076
// ===== Methods that implement the PlatformManager abstract interface.
7177

7278
CHIP_ERROR
@@ -80,6 +86,10 @@ class GenericPlatformManagerImpl_POSIX : public GenericPlatformManagerImpl<ImplC
8086
CHIP_ERROR _StartChipTimer(int64_t durationMS);
8187
CHIP_ERROR _Shutdown();
8288

89+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
90+
bool _IsChipStackLockedByCurrentThread() const;
91+
#endif
92+
8393
// ===== Methods available to the implementation subclass.
8494

8595
private:

src/inet/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ static_library("inet") {
9999
public_deps = [
100100
":inet_config_header",
101101
"${chip_root}/src/lib/support",
102+
"${chip_root}/src/platform:platform_buildconfig",
102103
"${chip_root}/src/system",
103104
"${nlio_root}:nlio",
104105
]

src/inet/InetLayer.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848

4949
#include "InetFaultInjection.h"
5050

51+
#include <platform/LockTracker.h>
52+
5153
#include <system/SystemTimer.h>
5254

5355
#include <support/CodeUtils.h>
@@ -535,6 +537,8 @@ INET_ERROR InetLayer::GetLinkLocalAddr(InterfaceId link, IPAddress * llAddr)
535537
*/
536538
INET_ERROR InetLayer::NewRawEndPoint(IPVersion ipVer, IPProtocol ipProto, RawEndPoint ** retEndPoint)
537539
{
540+
assertChipStackLockedByCurrentThread();
541+
538542
*retEndPoint = nullptr;
539543

540544
VerifyOrReturnError(State == kState_Initialized, INET_ERROR_INCORRECT_STATE);
@@ -573,6 +577,8 @@ INET_ERROR InetLayer::NewRawEndPoint(IPVersion ipVer, IPProtocol ipProto, RawEnd
573577
*/
574578
INET_ERROR InetLayer::NewTCPEndPoint(TCPEndPoint ** retEndPoint)
575579
{
580+
assertChipStackLockedByCurrentThread();
581+
576582
*retEndPoint = nullptr;
577583

578584
VerifyOrReturnError(State == kState_Initialized, INET_ERROR_INCORRECT_STATE);
@@ -611,6 +617,8 @@ INET_ERROR InetLayer::NewTCPEndPoint(TCPEndPoint ** retEndPoint)
611617
*/
612618
INET_ERROR InetLayer::NewUDPEndPoint(UDPEndPoint ** retEndPoint)
613619
{
620+
assertChipStackLockedByCurrentThread();
621+
614622
*retEndPoint = nullptr;
615623

616624
VerifyOrReturnError(State == kState_Initialized, INET_ERROR_INCORRECT_STATE);
@@ -777,6 +785,8 @@ INET_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint16_t hostNam
777785
INET_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint16_t hostNameLen, uint8_t options, uint8_t maxAddrs,
778786
IPAddress * addrArray, DNSResolveCompleteFunct onComplete, void * appState)
779787
{
788+
assertChipStackLockedByCurrentThread();
789+
780790
INET_ERROR err = INET_NO_ERROR;
781791
DNSResolver * resolver = nullptr;
782792

@@ -867,6 +877,8 @@ INET_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint16_t hostNam
867877
*/
868878
void InetLayer::CancelResolveHostAddress(DNSResolveCompleteFunct onComplete, void * appState)
869879
{
880+
assertChipStackLockedByCurrentThread();
881+
870882
if (State != kState_Initialized)
871883
return;
872884

@@ -1016,6 +1028,8 @@ void InetLayer::HandleTCPInactivityTimer(chip::System::Layer * aSystemLayer, voi
10161028
chip::System::Error InetLayer::HandleInetLayerEvent(chip::System::Object & aTarget, chip::System::EventType aEventType,
10171029
uintptr_t aArgument)
10181030
{
1031+
assertChipStackLockedByCurrentThread();
1032+
10191033
VerifyOrReturnError(INET_IsInetEvent(aEventType), CHIP_SYSTEM_ERROR_UNEXPECTED_EVENT);
10201034

10211035
// Dispatch the event according to its type.
@@ -1100,6 +1114,8 @@ chip::System::Error InetLayer::HandleInetLayerEvent(chip::System::Object & aTarg
11001114
*/
11011115
void InetLayer::PrepareSelect(int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval & sleepTimeTV)
11021116
{
1117+
assertChipStackLockedByCurrentThread();
1118+
11031119
if (State != kState_Initialized)
11041120
return;
11051121

@@ -1160,6 +1176,8 @@ void InetLayer::PrepareSelect(int & nfds, fd_set * readfds, fd_set * writefds, f
11601176
*/
11611177
void InetLayer::HandleSelectResult(int selectRes, fd_set * readfds, fd_set * writefds, fd_set * exceptfds)
11621178
{
1179+
assertChipStackLockedByCurrentThread();
1180+
11631181
if (State != kState_Initialized)
11641182
return;
11651183

src/inet/tests/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ static_library("helpers") {
4747
public_deps = [
4848
"${chip_root}/src/inet",
4949
"${chip_root}/src/lib/core",
50+
"${chip_root}/src/platform",
5051
"${nlunit_test_root}:nlunit-test",
5152
]
5253
}

src/platform/BUILD.gn

+28
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ if (chip_device_platform != "none") {
5555

5656
# Enable adding rotating device id to the additional data.
5757
chip_enable_rotating_device_id = true
58+
59+
# lock tracking: none/log/fatal or auto for a platform-dependent choice
60+
chip_stack_lock_tracking = "auto"
5861
}
5962

6063
buildconfig_header("platform_buildconfig") {
@@ -75,6 +78,30 @@ if (chip_device_platform != "none") {
7578
defines += [ "CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE=${chip_enable_ble}" ]
7679
}
7780

81+
stack_log_tracking = chip_stack_lock_tracking
82+
if (stack_log_tracking == "auto") {
83+
if (chip_device_platform == "linux" || chip_device_platform == "darwin") {
84+
# TODO: should be fatal for development. Change once bugs are fixed
85+
stack_log_tracking = "log"
86+
} else {
87+
# TODO: may want to enable at least logging for embedded to find bugs
88+
# this needs tuning depending on how many resources various platforms have
89+
# available (mainly flash size)
90+
stack_log_tracking = "none"
91+
}
92+
}
93+
94+
if (stack_log_tracking == "fatal") {
95+
defines += [
96+
"CHIP_STACK_LOCK_TRACKING_ENABLED=1",
97+
"CHIP_STACK_LOCK_TRACKING_ERROR_FATAL=1",
98+
]
99+
} else if (stack_log_tracking == "log") {
100+
defines += [ "CHIP_STACK_LOCK_TRACKING_ENABLED=1" ]
101+
} else {
102+
assert(stack_log_tracking == "none")
103+
}
104+
78105
if (chip_enable_nfc) {
79106
defines += [ "CHIP_DEVICE_CONFIG_ENABLE_NFC=1" ]
80107
}
@@ -224,6 +251,7 @@ if (chip_device_platform != "none" && chip_device_platform != "external") {
224251
"DeviceControlServer.cpp",
225252
"GeneralUtils.cpp",
226253
"Globals.cpp",
254+
"LockTracker.cpp",
227255
"PersistedStorage.cpp",
228256
"SystemEventSupport.cpp",
229257
"SystemTimerSupport.cpp",

src/platform/LockTracker.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
*
3+
* Copyright (c) 2021 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include <platform/LockTracker.h>
19+
20+
#if defined(CHIP_STACK_LOCK_TRACKING_ENABLED)
21+
22+
#include <platform/CHIPDeviceLayer.h>
23+
#include <platform/PlatformManager.h>
24+
#include <support/CodeUtils.h>
25+
#include <support/logging/CHIPLogging.h>
26+
namespace chip {
27+
namespace Platform {
28+
namespace Internal {
29+
30+
void AssertChipStackLockedByCurrentThread(const char * file, int line)
31+
{
32+
if (!chip::DeviceLayer::PlatformMgr().IsChipStackLockedByCurrentThread())
33+
{
34+
ChipLogError(DeviceLayer, "Chip stack locking error at '%s:%d'. Code is unsafe/racy", file, line);
35+
#if defined(CHIP_STACK_LOCK_TRACKING_ERROR_FATAL)
36+
chipDie();
37+
#endif
38+
}
39+
}
40+
41+
} // namespace Internal
42+
} // namespace Platform
43+
} // namespace chip
44+
45+
#endif

0 commit comments

Comments
 (0)