Time Travel Debugging team uses Copilot Chat for C++

Ken Sykes and Juan Carlos Arevalo Baeza (JCAB) are both Principal Software Engineers who work on the Time Travel Debugging team at Microsoft, which is the team that maintains and develops the Windows Debugger (WinDbg) and related technologies. Their codebase is developed with C++ and CMake, and they primarily use Visual Studio Code for developing their code.
They have been integrating GitHub Copilot and GitHub Copilot Chat into their C++ development in VS Code and have found many useful workflows for the AI pair programmer.
This blog post series has been written in partnership with Ken and JCAB to highlight their successful workflows with GitHub Copilot and GitHub Copilot Chat respectively and inspire other C++ developers on potential use cases to integrate with their coding workflows.
This blog post series has been written in partnership with Ken and JCAB to highlight their workflows with Copilot and Copilot Chat respectively and inspire other C++ developers on potential use cases to integrate with their code.
To view the first blog post on Copilot, please visit this link.
How they use Copilot Chat
They have integrated Copilot Chat as a search tool and pair programmer when coding, as it has provided applicable and relevant answers to some of their programming questions for their codebase.
For example, some sample scenarios that JCAB uses Copilot Chat to help with are:

Learning how to find, install, and use various tools
Asking programming documentation questions
Asking coding questions

Example questions and actual responses provided by Copilot Chat are enumerated below by JCAB throughout his workflows.
Learn how to find, install, and use various tools
Installing Tools
For Windows users, it is not natural to know how to install various Linux tools, but it is necessary for Linux development on their WSL Ubuntu machines. Luckily, Copilot Chat was able to get us close to the right set of directions to do so by iterating over the prompt. For example, I was curious about learning more about acquiring clang via package manager.
Please note, it is always a good idea to validate Copilot responses to ensure they are correct.

jcab_microsoft: How do I list “apt” packages that match a portion of their names?
GitHub Copilot: You can use the `apt-cache search` command to list all packages that match a portion of their names. Here’s how you can do it:
apt-cache search <package-name-portion>

Replace `<package-name-portion>` with the portion of the package name you’re interested in. This command will return a list of all packages that contain the specified string in their name or description.
For example, to search for all packages that contain the word “python” in their name or description, you would use:
apt-cache search python

This will return a list of all packages related to Python.

Ubuntu’s “apt” packages tend to be quite old, so it defaults to clang 14 and offers “clang-15” as an opt-in. But apparently, there’s a way to get packages directly from the makers.
I didn’t know about this but asked the basic question anyway and Copilot delivered wonderfully. In the end, this got me brand-spanking-new clang 19 without breaking a sweat.

jcab_microsoft: How can I get a current version of clang in my Ubuntu 22?
GitHub Copilot: To get the current version of Clang on Ubuntu 22.04, you can use the LLVM project’s own repositories. Here are the steps:

First, add the LLVM repository to your system:

wget -O – https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -

echo “deb https://apt.llvm.org/focal/ llvm-toolchain-focal main” | sudo tee /etc/apt/sources.list.d/llvm.list

Update the package list:

sudo apt-get update

Install Clang:

sudo apt-get install clang
This will install the latest stable version of Clang available in the LLVM repository.
To check the installed version of Clang, you can use the following command:
clang –version
Please note that the LLVM repository might not always have the absolute latest version of Clang, but it generally has more recent versions than the official Ubuntu repositories.

In general, I like to use iterative questions with Copilot Chat to understand how to install tools, especially in unfamiliar territories and can probe for more information when prompted.
As I mentioned before, it’s a good idea to check Copilot responses for issues. In this case, it suggested to use a command “apt-key” which is deprecated. Luckily in this case, that command provided good instructions on how to do it the non-deprecated way.
Ask about programming documentation
Despite being an experienced C++ and Windows developer, I don’t know everything! But I can ask C++ and Windows-specific questions for my programming and see that Copilot Chat is very helpful.

jcab_microsoft: What was the NTSTATUS generated by a fastfail in Windows?
GitHub Copilot: The `NTSTATUS` code generated by a fastfail in Windows is `0xC0000409`, which corresponds to `STATUS_STACK_BUFFER_OVERRUN`. This status code indicates that a stack buffer overrun has occurred in the application, compromising the integrity of the process.
When a fastfail occurs, the process is terminated immediately without running any exception handling or cleanup code. This is designed to prevent potential security vulnerabilities from being exploited.

Note that Copilot isn’t perfect, and its information can sometimes be out of date, so sometimes I use it as a nudge in the right direction (see this blog from 2023 clarifying this information, although this is what was meant at first).
And sometimes, documentation questions turn into code questions.

jcab_microsoft: In C++, is there a way to wait for multiple synchronization objects?
GitHub Copilot: In C++, there isn’t a direct way to wait for multiple synchronization objects like you can in some other languages or platforms (for example, `WaitForMultipleObjects` in Windows API). However, you can achieve similar functionality using condition variables along with a status check for the objects you’re interested in.
Here’s a simplified example:
#include <mutex>

#include <condition_variable>

#include <vector>

std::mutex mtx;

std::condition_variable cv;

std::vector<bool> objects_ready; // replace with actual status of your objects

void wait_for_objects() {

    std::unique_lock<std::mutex> lock(mtx);

    cv.wait(lock, []{ 

        // This is a predicate function that checks if all objects are ready

        for (bool is_ready : objects_ready) {

            if (!is_ready) return false;

        }

        return true;

    });

}

void set_object_ready(int index) {

    std::lock_guard<std::mutex> lock(mtx);

    objects_ready[index] = true;

    cv.notify_all(); // notify all waiting threads

}
In this example, `wait_for_objects` waits until all objects are ready. `set_object_ready` sets an object as ready and notifies all waiting threads.
Please note that this is a simplified example and may not be suitable for all use cases. Depending on your needs, you may need to use a different synchronization mechanism or a third-party library.

Ask coding questions
In my current line of work, I often need to find how to do a given Windows thing in Standard C++ and/or Linux. Below are some questions I’ve asked Copilot Chat that have been highly performant and helpful.

jcab_microsoft: what is the standard C++ way to implement _BitScanForward?
Github Copilot: `_BitScanForward` is a Microsoft-specific intrinsic function that is not part of the C++ standard library. However, you can use the `__builtin_ctz` function in GCC and Clang to achieve the same functionality. Here’s an example implementation:
#include <cstdint>

#ifdef _MSC_VER

#include <intrin.h>

#endif

uint32_t BitScanForward(uint32_t mask)

{

#ifdef _MSC_VER

    unsigned long index;

    _BitScanForward(&index, mask);

    return index;

#else

    return __builtin_ctz(mask);

#endif

}
This implementation checks if the code is being compiled with Microsoft Visual C++ (indicated by the `_MSC_VER` macro) and uses `_BitScanForward` if it is. Otherwise, it uses the `__builtin_ctz` function to achieve the same functionality. Note that `__builtin_ctz` returns the number of trailing zeros in the input, so you may need to subtract the result from the bit width of the input type to get the index of the first set bit.

Notice that Copilot Chat didn’t necessarily fully answer the question using C++20 standards, but still provided some level of guidance for me. Again, it can be important to iterate with Copilot if this is a priority for you.
And some more for porting over code…

jcab_microsoft: How can I get the _byteswap_ulong intrinsic in clang?
GitHub Copilot: The `_byteswap_ulong` intrinsic is specific to Microsoft’s compiler. In Clang, you can use the `__builtin_bswap32` function to achieve the same functionality. Here’s an example:
#include <cstdint>

uint32_t byteswap_ulong(uint32_t value)

{

    return __builtin_bswap32(value);

}
In this function, `__builtin_bswap32` reverses the byte order of a 32-bit value, which is equivalent to what `_byteswap_ulong` does in Microsoft’s compiler.

jcab_microsoft: What about a clang intrinsic for _mul128?
GitHub Copilot: Clang does not have a direct intrinsic for `_mul128`. However, you can use the built-in type `__int128` to perform 128-bit multiplication. Here’s an example:
#include <cstdint>

void mul128(int64_t a, int64_t b, int64_t* high, int64_t* low) {

    __int128 result = static_cast<__int128>(a) * static_cast<__int128>(b);

    *high = result >> 64;

    *low = result;

}
In this function, `a` and `b` are the 64-bit integers to multiply, and `high` and `low` are pointers to 64-bit integers where the high and low 64 bits of the result will be stored, respectively. The function first casts `a` and `b` to `__int128` and performs the multiplication, then it stores the high and low 64 bits of the result in `*high` and `*low`, respectively.

jcab_microsoft: What’s the best equivalent on Linux to fastfail?
GitHub Copilot: On Linux, the closest equivalent to Windows’ `fastfail` would be to use the `abort()` function from the C Standard Library. This function causes the program to terminate abnormally and generate a core dump file for post-mortem analysis.
Here’s how you can modify the `DBG_FAIL` macro in your code to use `abort()`:
#define DBG_FAIL(…)          abort()

This will cause the program to terminate immediately when `DBG_FAIL` is called, similar to how `fastfail` works on Windows. However, please note that unlike `fastfail`, `abort()` does not allow you to specify an exit code. The program will always return with a status indicating abnormal termination.

And always, if in doubt, never hesitate to ask for clarification and pointers to the relevant documentation.

jcab_microsoft: Do you have any pointers to references expanding on the use of abort() in place of __fastfail()?
GitHub Copilot: The use of `abort()` in place of `__fastfail()` is a common practice in Unix-like environments, including Linux, where `__fastfail()` is not available. The `abort()` function raises the `SIGABRT` signal, causing the process to terminate unless the signal is caught by a signal handler. This is similar to how `__fastfail()` works on Windows.
Here are some references that discuss the use of `abort()`:
– [abort – C++ Reference](http://www.cplusplus.com/reference/cstdlib/abort/)
– [abort – Linux man page](https://man7.org/linux/man-pages/man3/abort.3.html)
These references provide detailed information about how `abort()` works and how it can be used to terminate a process in response to a fatal error or security violation.
Please note that while `abort()` is similar to `__fastfail()`, there are some differences. For example, `abort()` does not allow you to specify an exit code, and it does not guarantee that the process will terminate immediately in all circumstances. If you need more control over the termination process, you may need to use a different function or mechanism.

Wrap Up
A big thanks to Ken and JCAB from the Time Travel Debugging Team for collaborating with us on this blog post!
Copilot Chat can provide you with AI-powered assistance at many points in your C++ development, such as installing new tools and answering various programming questions. Download Copilot Chat (automatically available with Visual Studio as of 17.10 Preview 3) and try out utilizing it when you are learning new processes or generating code! Please note that responses are generated by AI, so we recommend reviewing any responses provided and augmenting and iterating with your prompts when necessary.
Our team is working hard on improving C++ integrations, so please let us know any improvements you’d like to see to your C++ workflows. Additionally, let us know what other types of content you’d like to see regarding use cases or additional information about Copilot Chat.
We welcome all types of feedback on your experience with the product. The comments below are open, or you can find us via email at visualcpp@microsoft.com or via Twitter at @VisualC.