Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tracing thread creation points for debugging

Tags:

c++

I've come to love lambdas, and a while ago I wrote a simple wrapper which takes a lambda and starts it on a new thread.

//
// Starts a task on a separate thread, when passed a lambda expression
//
template<typename T>
smart_ptrs::w32handle StartTask(T f)
{
    // Make a copy of the task on the heap
    T* pTask = new T(f);

    // Create a new thread to service the task
    smart_ptrs::w32handle hThread(::CreateThread(
        NULL, 
        0, 
        (LPTHREAD_START_ROUTINE)& detail::start_task_proc<T>, 
        (LPVOID) pTask, 
        NULL, 
        NULL)); 

    // If the caller ignores this rc, the thread handle will simply close and the
    // thread will continue in fire-and-forget fashion.
    return hThread;
}

NOTE: Yes I know this doesn't really need a template and could happily use std::function. I've posted it this way because it matches more complicated (async queue) versions I have which do have to be templates.

The end result is a function which is very easy to use in parallel algorithms etc. However a problem arises if you start to use such a function extensively. Because the threads created all appear to come from the same rather generic function it can become difficult to tell where in the code they were started. It's usually possible to work out from the context of what they're doing but it's not as easy as it used to be when using an explicit thread function. Does anyone have a good method of tagging such threads so that they're easier to debug?

like image 651
Benj Avatar asked Jun 03 '13 09:06

Benj


People also ask

How do I create a debug point?

To set a breakpoint in source code: Click in the far left margin next to a line of code. You can also select the line and press F9, select Debug > Toggle Breakpoint, or right-click and select Breakpoint > Insert breakpoint.

What is thread debugging?

A thread is a sequence of instructions to which the operating system grants processor time. Every process that is running in the operating system consists of at least one thread.


1 Answers

As far as I can tell from the code, you'll have threads that have a start_task_proc somewhere in their callstack that call their function object. You could modify that function to take a pointer to a "task info" structure, rather than the bare function object. You could stuff whatever information you like into that information object, e.g. line numbers and filenames of where you created the task:

template <class T>
struct TaksInfo {
  T func;
  unsigned line;
  char const* file;

  TaskInfo(T&& t, unsigned l, char const* f)
    : func(std::move(t), line(l), file(f) {}
};


template<typename T>
smart_ptrs::w32handle StartTask(T f, unsigned line, char const* file)
{
    // Make a copy of the task on the heap
    auto pTask = new TaskInfo<T>(f, line, file);

    // Create a new thread to service the task
    smart_ptrs::w32handle hThread(::CreateThread(
        NULL, 
        0, 
        (LPTHREAD_START_ROUTINE)& detail::start_task_proc<T>, 
        (LPVOID) pTask, 
        NULL, 
        NULL)); 

    // If the caller ignores this rc, the thread handle will simply close and the
    // thread will continue in fire-and-forget fashion.
    return hThread;
}

#define START_TASK(f) StartTask(f, __LINE__, __FILE__)

template <class T>
DWORD start_task_proc(LPVOID lpParameter) {
  auto pTask = (TaskInfo<T>*) lpParameter;
  return pTask->func();
}


//use:
int main() {
  auto threadHandle = START_TASK(([]() -> DWORD { std::cout << "foo\n"; return 42;} ));
}

If you now inspect pTask in start_task_proc you can see file and line wich can tell you where the task was started.
Of course you could avoid the TaskInfo structure and make the information just a template parameter of start_task_proc:

template <class T, unsigned Line, char const* File>
DWORD start_task_proc(LPVOID lpParameter) { /* as you have it */)

template<unsigned Line, char const* File, typename T> //use T last fur type deduction
smart_ptrs::w32handle StartTask(T f)
{
    // Make a copy of the task on the heap
    auto pTask = new T(std::move(f));

    // Create a new thread to service the task
    smart_ptrs::w32handle hThread(::CreateThread(
        NULL, 
        0, 
        (LPTHREAD_START_ROUTINE)& detail::start_task_proc<T, Line, File>,   //!!!
        (LPVOID) pTask, 
        NULL, 
        NULL)); 

    // If the caller ignores this rc, the thread handle will simply close and the
    // thread will continue in fire-and-forget fashion.
    return hThread;
}

#define START_TASK(f) StartTask<__LINE__, __FILE__>(f)
like image 132
Arne Mertz Avatar answered Nov 15 '22 06:11

Arne Mertz