mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 15:05:08 +00:00
Kernel: Utilize AK::SourceLocation for LOCK_DEBUG instrumentation.
The previous `LOCKER(..)` instrumentation only covered some of the cases where a lock is actually acquired. By utilizing the new `AK::SourceLocation` functionality we can now reliably instrument all calls to lock automatically. Other changes: - Tweak the message in `Thread::finalize()` which dumps leaked lock so it's more readable and includes the function information that is now available. - Make the `LOCKER(..)` define a no-op, it will be cleaned up in a follow up change.
This commit is contained in:
parent
87724b3d09
commit
04156d53ca
4 changed files with 50 additions and 44 deletions
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/SourceLocation.h>
|
||||||
#include <AK/TemporaryChange.h>
|
#include <AK/TemporaryChange.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/KSyms.h>
|
#include <Kernel/KSyms.h>
|
||||||
|
@ -13,12 +14,7 @@
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
void Lock::lock(Mode mode)
|
void Lock::lock(Mode mode, const SourceLocation& location)
|
||||||
{
|
|
||||||
lock("unknown", 0, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::lock(const char* file, int line, Mode mode)
|
|
||||||
#else
|
#else
|
||||||
void Lock::lock(Mode mode)
|
void Lock::lock(Mode mode)
|
||||||
#endif
|
#endif
|
||||||
|
@ -52,9 +48,10 @@ void Lock::lock(Mode mode)
|
||||||
}
|
}
|
||||||
VERIFY(m_times_locked == 0);
|
VERIFY(m_times_locked == 0);
|
||||||
m_times_locked++;
|
m_times_locked++;
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
if (current_thread) {
|
if (current_thread) {
|
||||||
current_thread->holding_lock(*this, 1, file, line);
|
current_thread->holding_lock(*this, 1, location);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
m_queue.should_block(true);
|
m_queue.should_block(true);
|
||||||
|
@ -77,8 +74,9 @@ void Lock::lock(Mode mode)
|
||||||
VERIFY(mode == Mode::Exclusive || mode == Mode::Shared);
|
VERIFY(mode == Mode::Exclusive || mode == Mode::Shared);
|
||||||
VERIFY(m_times_locked > 0);
|
VERIFY(m_times_locked > 0);
|
||||||
m_times_locked++;
|
m_times_locked++;
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
current_thread->holding_lock(*this, 1, file, line);
|
current_thread->holding_lock(*this, 1, location);
|
||||||
#endif
|
#endif
|
||||||
m_lock.store(false, AK::memory_order_release);
|
m_lock.store(false, AK::memory_order_release);
|
||||||
return;
|
return;
|
||||||
|
@ -98,8 +96,9 @@ void Lock::lock(Mode mode)
|
||||||
it->value++;
|
it->value++;
|
||||||
else
|
else
|
||||||
m_shared_holders.set(current_thread, 1);
|
m_shared_holders.set(current_thread, 1);
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
current_thread->holding_lock(*this, 1, file, line);
|
current_thread->holding_lock(*this, 1, location);
|
||||||
#endif
|
#endif
|
||||||
m_lock.store(false, AK::memory_order_release);
|
m_lock.store(false, AK::memory_order_release);
|
||||||
return;
|
return;
|
||||||
|
@ -168,10 +167,9 @@ void Lock::unlock()
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
if (current_thread) {
|
if (current_thread) {
|
||||||
current_thread->holding_lock(*this, -1);
|
current_thread->holding_lock(*this, -1, {});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_lock.store(false, AK::memory_order_release);
|
m_lock.store(false, AK::memory_order_release);
|
||||||
if (unlocked_last) {
|
if (unlocked_last) {
|
||||||
u32 did_wake = m_queue.wake_one();
|
u32 did_wake = m_queue.wake_one();
|
||||||
|
@ -205,7 +203,7 @@ auto Lock::force_unlock_if_locked(u32& lock_count_to_restore) -> Mode
|
||||||
|
|
||||||
dbgln_if(LOCK_RESTORE_DEBUG, "Lock::force_unlock_if_locked @ {}: unlocking exclusive with lock count: {}", this, m_times_locked);
|
dbgln_if(LOCK_RESTORE_DEBUG, "Lock::force_unlock_if_locked @ {}: unlocking exclusive with lock count: {}", this, m_times_locked);
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
m_holder->holding_lock(*this, -(int)m_times_locked);
|
m_holder->holding_lock(*this, -(int)m_times_locked, {});
|
||||||
#endif
|
#endif
|
||||||
m_holder = nullptr;
|
m_holder = nullptr;
|
||||||
VERIFY(m_times_locked > 0);
|
VERIFY(m_times_locked > 0);
|
||||||
|
@ -233,7 +231,7 @@ auto Lock::force_unlock_if_locked(u32& lock_count_to_restore) -> Mode
|
||||||
lock_count_to_restore = it->value;
|
lock_count_to_restore = it->value;
|
||||||
VERIFY(lock_count_to_restore > 0);
|
VERIFY(lock_count_to_restore > 0);
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
m_holder->holding_lock(*this, -(int)lock_count_to_restore);
|
m_holder->holding_lock(*this, -(int)lock_count_to_restore, {});
|
||||||
#endif
|
#endif
|
||||||
m_shared_holders.remove(it);
|
m_shared_holders.remove(it);
|
||||||
VERIFY(m_times_locked >= lock_count_to_restore);
|
VERIFY(m_times_locked >= lock_count_to_restore);
|
||||||
|
@ -264,12 +262,7 @@ auto Lock::force_unlock_if_locked(u32& lock_count_to_restore) -> Mode
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
void Lock::restore_lock(Mode mode, u32 lock_count)
|
void Lock::restore_lock(Mode mode, u32 lock_count, const SourceLocation& location)
|
||||||
{
|
|
||||||
return restore_lock("unknown", 0, mode, lock_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::restore_lock(const char* file, int line, Mode mode, u32 lock_count)
|
|
||||||
#else
|
#else
|
||||||
void Lock::restore_lock(Mode mode, u32 lock_count)
|
void Lock::restore_lock(Mode mode, u32 lock_count)
|
||||||
#endif
|
#endif
|
||||||
|
@ -296,8 +289,9 @@ void Lock::restore_lock(Mode mode, u32 lock_count)
|
||||||
m_holder = current_thread;
|
m_holder = current_thread;
|
||||||
m_queue.should_block(true);
|
m_queue.should_block(true);
|
||||||
m_lock.store(false, AK::memory_order_release);
|
m_lock.store(false, AK::memory_order_release);
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
m_holder->holding_lock(*this, (int)lock_count, file, line);
|
m_holder->holding_lock(*this, (int)lock_count, location);
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -318,8 +312,9 @@ void Lock::restore_lock(Mode mode, u32 lock_count)
|
||||||
VERIFY(set_result == AK::HashSetResult::InsertedNewEntry);
|
VERIFY(set_result == AK::HashSetResult::InsertedNewEntry);
|
||||||
m_queue.should_block(true);
|
m_queue.should_block(true);
|
||||||
m_lock.store(false, AK::memory_order_release);
|
m_lock.store(false, AK::memory_order_release);
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
m_holder->holding_lock(*this, (int)lock_count, file, line);
|
m_holder->holding_lock(*this, (int)lock_count, location);
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,16 @@ public:
|
||||||
}
|
}
|
||||||
~Lock() = default;
|
~Lock() = default;
|
||||||
|
|
||||||
void lock(Mode = Mode::Exclusive);
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
void lock(const char* file, int line, Mode mode = Mode::Exclusive);
|
void lock(Mode mode = Mode::Exclusive, const SourceLocation& location = SourceLocation::current());
|
||||||
void restore_lock(const char* file, int line, Mode, u32);
|
void restore_lock(Mode, u32, const SourceLocation& location = SourceLocation::current());
|
||||||
|
#else
|
||||||
|
void lock(Mode = Mode::Exclusive);
|
||||||
|
void restore_lock(Mode, u32);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void unlock();
|
void unlock();
|
||||||
[[nodiscard]] Mode force_unlock_if_locked(u32&);
|
[[nodiscard]] Mode force_unlock_if_locked(u32&);
|
||||||
void restore_lock(Mode, u32);
|
|
||||||
[[nodiscard]] bool is_locked() const { return m_mode != Mode::Unlocked; }
|
[[nodiscard]] bool is_locked() const { return m_mode != Mode::Unlocked; }
|
||||||
void clear_waiters();
|
void clear_waiters();
|
||||||
|
|
||||||
|
@ -79,17 +81,19 @@ private:
|
||||||
class Locker {
|
class Locker {
|
||||||
public:
|
public:
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
ALWAYS_INLINE explicit Locker(const char* file, int line, Lock& l, Lock::Mode mode = Lock::Mode::Exclusive)
|
ALWAYS_INLINE explicit Locker(Lock& l, Lock::Mode mode = Lock::Mode::Exclusive, const SourceLocation& location = SourceLocation::current())
|
||||||
: m_lock(l)
|
#else
|
||||||
{
|
|
||||||
m_lock.lock(file, line, mode);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
ALWAYS_INLINE explicit Locker(Lock& l, Lock::Mode mode = Lock::Mode::Exclusive)
|
ALWAYS_INLINE explicit Locker(Lock& l, Lock::Mode mode = Lock::Mode::Exclusive)
|
||||||
|
#endif
|
||||||
: m_lock(l)
|
: m_lock(l)
|
||||||
{
|
{
|
||||||
|
#if LOCK_DEBUG
|
||||||
|
m_lock.lock(mode, location);
|
||||||
|
#else
|
||||||
m_lock.lock(mode);
|
m_lock.lock(mode);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE ~Locker()
|
ALWAYS_INLINE ~Locker()
|
||||||
{
|
{
|
||||||
if (m_locked)
|
if (m_locked)
|
||||||
|
@ -101,11 +105,21 @@ public:
|
||||||
m_locked = false;
|
m_locked = false;
|
||||||
m_lock.unlock();
|
m_lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LOCK_DEBUG
|
||||||
|
ALWAYS_INLINE void lock(Lock::Mode mode = Lock::Mode::Exclusive, const SourceLocation& location = SourceLocation::current())
|
||||||
|
#else
|
||||||
ALWAYS_INLINE void lock(Lock::Mode mode = Lock::Mode::Exclusive)
|
ALWAYS_INLINE void lock(Lock::Mode mode = Lock::Mode::Exclusive)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
VERIFY(!m_locked);
|
VERIFY(!m_locked);
|
||||||
m_locked = true;
|
m_locked = true;
|
||||||
|
|
||||||
|
#if LOCK_DEBUG
|
||||||
|
m_lock.lock(mode, location);
|
||||||
|
#else
|
||||||
m_lock.lock(mode);
|
m_lock.lock(mode);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -113,13 +127,8 @@ private:
|
||||||
bool m_locked { true };
|
bool m_locked { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#define LOCKER(...) Locker locker(__VA_ARGS__)
|
||||||
# define LOCKER(...) Locker locker(__FILE__, __LINE__, __VA_ARGS__)
|
#define RESTORE_LOCK(lock, ...) (lock).restore_lock(__VA_ARGS__)
|
||||||
# define RESTORE_LOCK(lock, ...) (lock).restore_lock(__FILE__, __LINE__, __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
# define LOCKER(...) Locker locker(__VA_ARGS__)
|
|
||||||
# define RESTORE_LOCK(lock, ...) (lock).restore_lock(__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Lockable {
|
class Lockable {
|
||||||
|
|
|
@ -360,8 +360,10 @@ void Thread::finalize()
|
||||||
if (lock_count() > 0) {
|
if (lock_count() > 0) {
|
||||||
dbgln("Thread {} leaking {} Locks!", *this, lock_count());
|
dbgln("Thread {} leaking {} Locks!", *this, lock_count());
|
||||||
ScopedSpinLock list_lock(m_holding_locks_lock);
|
ScopedSpinLock list_lock(m_holding_locks_lock);
|
||||||
for (auto& info : m_holding_locks_list)
|
for (auto& info : m_holding_locks_list) {
|
||||||
dbgln(" - {} @ {} locked at {}:{} count: {}", info.lock->name(), info.lock, info.file, info.line, info.count);
|
const auto& location = info.source_location;
|
||||||
|
dbgln(" - Lock: \"{}\" @ {} locked in function \"{}\" at \"{}:{}\" with a count of: {}", info.lock->name(), info.lock, location.function_name(), location.file_name(), location.line_number(), info.count);
|
||||||
|
}
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <AK/IntrusiveList.h>
|
#include <AK/IntrusiveList.h>
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
|
#include <AK/SourceLocation.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Time.h>
|
#include <AK/Time.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
@ -1066,7 +1067,7 @@ public:
|
||||||
RecursiveSpinLock& get_lock() const { return m_lock; }
|
RecursiveSpinLock& get_lock() const { return m_lock; }
|
||||||
|
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
void holding_lock(Lock& lock, int refs_delta, const char* file = nullptr, int line = 0)
|
void holding_lock(Lock& lock, int refs_delta, const SourceLocation& location)
|
||||||
{
|
{
|
||||||
VERIFY(refs_delta != 0);
|
VERIFY(refs_delta != 0);
|
||||||
m_holding_locks.fetch_add(refs_delta, AK::MemoryOrder::memory_order_relaxed);
|
m_holding_locks.fetch_add(refs_delta, AK::MemoryOrder::memory_order_relaxed);
|
||||||
|
@ -1082,7 +1083,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!have_existing)
|
if (!have_existing)
|
||||||
m_holding_locks_list.append({ &lock, file ? file : "unknown", line, 1 });
|
m_holding_locks_list.append({ &lock, location, 1 });
|
||||||
} else {
|
} else {
|
||||||
VERIFY(refs_delta < 0);
|
VERIFY(refs_delta < 0);
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -1208,8 +1209,7 @@ private:
|
||||||
#if LOCK_DEBUG
|
#if LOCK_DEBUG
|
||||||
struct HoldingLockInfo {
|
struct HoldingLockInfo {
|
||||||
Lock* lock;
|
Lock* lock;
|
||||||
const char* file;
|
SourceLocation source_location;
|
||||||
int line;
|
|
||||||
unsigned count;
|
unsigned count;
|
||||||
};
|
};
|
||||||
Atomic<u32> m_holding_locks { 0 };
|
Atomic<u32> m_holding_locks { 0 };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue