Skip to content

Consider using non-null-terminated strings for source location function/file strings. #1265

@bmqn

Description

@bmqn

I want to redefine TracyFunction to something like __PRETTY_FUNCTION__ so I can include the C++ namespace and class in the function name. For this to be useful, I'd also like to strip the return type and arguments from that string. This is possible using a constexpr function, but Tracy currently assumes null-terminated strings so we are restricted to stripping from the left only.

Example:

// Client code

constexpr const char* MyPrettyFunction(const char* pretty)
{
	std::string_view s(pretty);
	auto start = s.find(' ');
	++start;
	auto end = s.find('(', start);
	if (end == std::string_view::npos) end = s.size();
	std::string_view r = s.substr(start, end - start);
	return r.data();
}

#define TracyFunction MyPrettyFunction(__PRETTY_FUNCTION__)

A C++ method might appear as int foo::bar(int, int) when using __PRETTY_FUNCTION__, but we can only strip it to foo::bar(int, int) when using null-terminated strings. The name is quite verbose when viewed in the profiler and specifying an explicit name may be unfeasible for large codebases.

To address this, we could encode strings using a pointer + length. This would allow client code to define exactly what portion of the string should be displayed, without being constrained by null termination.

Example:

// TracyProfiler.hpp

struct StringData
{
    const char* ptr;
    size_t len;
};

struct SourceLocationData
{
    const char* name;
    StringData function;
    StringData file;
    uint32_t line;
    uint32_t color;
};

With new defaults:

// Tracy.hpp

#ifndef TracyFunction
#  define TracyFunction tracy::StringData{ __FUNCTION__, 0 }
#endif

#ifndef TracyFile
#  define TracyFile tracy::StringData{ __FILE__, 0 }
#endif

A length of 0 preserves the existing null-terminated behavior. In client code we are free to specify a length for both TracyFunction and TracyFile:

// Client code

#include <client/TracyProfiler.hpp>

constexpr tracy::StringData MyPrettyFunction(const char* pretty)
{
	std::string_view s(pretty);
	auto start = s.find(' ');
	++start;
	auto end = s.find('(', start);
	if (end == std::string_view::npos) end = s.size();
	std::string_view r = s.substr(start, end - start);
	tracy::StringData d;
	d.ptr = r.data();
	d.len = r.size();
	return d;
}

#define TracyFunction MyPrettyFunction(__PRETTY_FUNCTION__)

I have implemented this locally, but wanted to open an issue to first discuss feasibility for this kind of change. Changing the internal types used by both the server and client would break existing profile data. However, this approach can be done client-side only. The client maintains a cache that maps pointers to sizes. The server continues to request strings using the pointer as a key, and the client uses the locally known length to return only the desired sub-string, which avoids any server changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions