Compare commits

..

8 Commits

Author SHA1 Message Date
Clément Grégoire
1c26daa3ac Detect unreliable TSC on Linux via perf timestamp probe.
Linux exposes time_zero/time_mult/time_shift on the perf_event mmap
page for converting sample timestamps back to TSC cycles, gated by
cap_user_time_zero. That flag only states the conversion math is
valid; on Ryzen the kernel can leave it set while TSC silently drifts
between cores, before the clocksource watchdog catches it. We've had
user reports of this surfacing as garbage timestamps in traces.

CheckLinuxPerfTscConsistency() first looks at the current clocksource
in sysfs, then opens a dummy PERF_COUNT_SW_DUMMY event and verifies
that a PR_SET_NAME-triggered PERF_RECORD_COMM timestamp inverts back
into the rdtsc-sandwiched window with ~1 ms of slack. Modeled on
tools/perf/tests/perf-time-to-tsc.c.

Wired into CheckHardwareSupportsInvariantTSC() so a failing probe
calls InitFailure in non-fallback builds and routes GetTime() to
clock_gettime in TRACY_TIMER_FALLBACK builds. Inconclusive cases
(perf blocked, no COMM record, etc) return true to avoid penalising
sandboxed environments. Gated on TRACY_HAS_SYSTEM_TRACING so we only
probe when Tracy would actually open perf events.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 16:11:39 +02:00
Bartosz Taudul
6957f968a4 Include trace time span in llm system message. 2026-05-22 02:06:58 +02:00
Bartosz Taudul
896cdc9462 Fade-out outdated assistant replies.
The "outdated" concept is strictly for chain of assistant replies with
nothing in between, i.e.:

"I will check this..."      <- outdated
<tool call>                 <- not displayed
"Now I will do that..."     <- outdated
<tool call>                 <- not displayed
"Let me consider..."        <- outdated
<reasoning>                 <- not displayed
"Now I have the answer..."

The first three messages are at this point considered outdated, as the
model provided a more recent message.

Note that in chain such as below there are NO outdated messages:

"How can I help..."
<user input>
"Ah, I see..."
<user input>
"You may try to..."

Similarly, if the tool calls or reasoning sections are explicitly enabled
in the chat UI, the messages are also not considered outdated.
2026-05-21 21:23:14 +02:00
Bartosz Taudul
efc33a09d9 Fix potential issue with last chat turn detection. 2026-05-21 20:51:58 +02:00
Bartosz Taudul
61875e1bd0 Add LLM tool for listing sampling statistics. 2026-05-21 20:27:54 +02:00
Bartosz Taudul
e003885946 Merge pull request #1369 from siliceum/feature/no-sys-param-for-bsd
Drop sys/param.h dependency for BSD detection
2026-05-21 17:57:16 +02:00
Bartosz Taudul
d40806caff Merge pull request #1368 from siliceum/fix/timeline-depth
Prevent unlimited recursion leading to stack overflow
2026-05-21 17:50:58 +02:00
Clément Grégoire
e7c71c991c Drop sys/param.h dependency for BSD detection
Replace `#ifdef BSD` (which requires including `<sys/param.h>` first) with explicit checks for `__FreeBSD__`, `__NetBSD__`, `__OpenBSD__` and `__DragonFly__`, matching how these BSDs are already enumerated elsewhere in the codebase (OS name strings, thread id helpers, etc.).

This also avoids leaking the `sys/param.h` requirement through public headers (`TracySysTime.hpp`, `TracyCallstack.h`), where consumers would otherwise need it to correctly see `TRACY_HAS_SYSTIME` / `TRACY_HAS_CALLSTACK`.
`libbacktrace/config.h` is left as-is — it's third-party and only included from .c files where the `BSD` macro can still be picked up locally.

Note: for `setsockopt( m_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&val, sizeof( val ) );` I added `__APPLE__` too since this was the only place where it was not checked explicitely.
2026-05-21 10:18:30 +02:00
13 changed files with 329 additions and 30 deletions

View File

@@ -33,7 +33,7 @@ If the user thanks you for your help, ask them to consider making a donation at
# User's program
The program being profiled is named %PROGRAMNAME%. It was built at %PROGRAMTIME%. The profiling session was performed at %PROFILETIME%.%PROFILEDESCRIPTION%
The program being profiled is named %PROGRAMNAME%. It was built at %PROGRAMTIME%. The profiling session was performed at %PROFILETIME%, and the length of the session was %PROFILELENGTH%.%PROFILEDESCRIPTION%
Here are instructions you must follow when you are asked to work with program the user is profiling.

View File

@@ -221,5 +221,26 @@
"required": ["address"]
}
}
},
{
"type": "function",
"function": {
"name": "sampling_stats",
"description": "Get sampling statistics for functions, sorted by time spent in each.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "ECMAScript regex to search for."
},
"limit": {
"type": "integer",
"description": "Maximum number of entries to return. Default: 30."
}
},
"required": []
}
}
}
]

View File

@@ -614,7 +614,26 @@ void TracyLlm::Draw()
{
think = TracyLlmChat::Think::ToolCall;
}
if( !m_chatUi->Turn( role, it, m_chat.end(), think, turnIdx == m_chat.size() - 1 ) )
const auto isLast = it + 1 == m_chat.end();
bool fadeout = false;
if( role == TracyLlmChat::TurnRole::Assistant && !m_allThinkingRegions && !isLast )
{
auto nit = it + 1;
while( nit != m_chat.end() )
{
const auto& nline = *nit;
if( !nline.contains( "role" ) ) { nit++; continue; }
const auto& nroleStr = nline["role"].get_ref<const std::string&>();
if( nroleStr == "assistant" && nline.contains( "content" ) )
{
fadeout = true;
break;
}
if( nroleStr == "user" ) break;
nit++;
}
}
if( !m_chatUi->Turn( role, it, m_chat.end(), think, isLast, fadeout ) )
{
if( role == TracyLlmChat::TurnRole::Assistant )
{
@@ -905,6 +924,7 @@ void TracyLlm::UpdateSystemPrompt()
static constexpr std::string_view ProgramNameToken = "%PROGRAMNAME%";
static constexpr std::string_view ProgramTimeToken = "%PROGRAMTIME%";
static constexpr std::string_view ProfileTimeToken = "%PROFILETIME%";
static constexpr std::string_view ProfileLengthToken = "%PROFILELENGTH%";
static constexpr std::string_view ProfileDescriptionToken = "%PROFILEDESCRIPTION%";
static constexpr std::string_view SkillsToken = "%SKILLS%";
@@ -913,6 +933,8 @@ void TracyLlm::UpdateSystemPrompt()
const auto exectime = m_worker.GetExecutableTime();
const auto capturetime = m_worker.GetCaptureTime();
const auto firstTime = m_worker.GetFirstTime();
const auto lastTime = m_worker.GetLastTime();
char etime[64], ctime[64];
@@ -942,6 +964,7 @@ void TracyLlm::UpdateSystemPrompt()
Replace( systemPrompt, ProgramNameToken, m_worker.GetCaptureProgram() );
Replace( systemPrompt, ProgramTimeToken, etime );
Replace( systemPrompt, ProfileTimeToken, ctime );
Replace( systemPrompt, ProfileLengthToken, TimeToString( lastTime - firstTime ) );
Replace( systemPrompt, ProfileDescriptionToken, descStr );
Replace( systemPrompt, SkillsToken, skills );

View File

@@ -159,6 +159,13 @@ std::string TracyLlmChat::ToolCallDescription( const nlohmann::json& json ) cons
if( args.contains( "limit" ) ) limit = ", limit: " + std::to_string( args["limit"].get<uint32_t>() );
return "Symbol parents: " + std::string( m_worker.GetString( sym->name ) ) + limit;
}
else if( name == "sampling_stats" )
{
std::string query, limit;
if( args.contains( "query" ) ) query = ", query: " + args["query"].get_ref<const std::string&>();
if( args.contains( "limit" ) ) limit = ", limit: " + std::to_string( args["limit"].get<uint32_t>() );
return "Sampling stats" + query + limit;
}
return "";
}
@@ -220,7 +227,7 @@ void TracyLlmChat::End()
}
}
bool TracyLlmChat::Turn( TurnRole role, std::vector<nlohmann::json>::iterator it, const std::vector<nlohmann::json>::iterator& end, Think think, bool last )
bool TracyLlmChat::Turn( TurnRole role, std::vector<nlohmann::json>::iterator it, const std::vector<nlohmann::json>::iterator& end, Think think, bool last, bool fadeout )
{
auto& json = *it;
if( json.contains( "role" ) && json["role"].get_ref<const std::string&>() == "tool" ) return true;
@@ -384,7 +391,9 @@ bool TracyLlmChat::Turn( TurnRole role, std::vector<nlohmann::json>::iterator it
if( ptr != end )
{
NormalScope();
if( fadeout ) ImGui::PushStyleColor( ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled] );
m_markdown.Print( content.c_str(), content.size() );
if( fadeout ) ImGui::PopStyleColor();
if( roleStr == "assistant" ) ImGui::Spacing();
}
}

View File

@@ -43,7 +43,7 @@ public:
void Begin();
void End();
bool Turn( TurnRole role, std::vector<nlohmann::json>::iterator it, const std::vector<nlohmann::json>::iterator& end, Think think, bool last );
bool Turn( TurnRole role, std::vector<nlohmann::json>::iterator it, const std::vector<nlohmann::json>::iterator& end, Think think, bool last, bool fadeout );
void SetModelTimeLabel( const char* model, uint64_t duration_ns );
private:

View File

@@ -1,5 +1,6 @@
#include <algorithm>
#include <curl/curl.h>
#include <inttypes.h>
#include <nlohmann/json.hpp>
#include <libbase64.h>
#include <pugixml.hpp>
@@ -203,6 +204,11 @@ std::string TracyLlmTools::HandleToolCalls( const std::string& tool, const nlohm
{
return SymbolParents( Param( "address" ), ParamOptU32( "limit", 10 ) );
}
else if( tool == "sampling_stats" )
{
std::string empty;
return SamplingStats( ParamOptString( "query", empty ), ParamOptU32( "limit", 30 ) );
}
#endif
return "Unknown tool call: " + tool;
}
@@ -1106,6 +1112,112 @@ std::string TracyLlmTools::SymbolParents( const std::string& address, uint32_t l
}
return result.dump( -1, ' ', false, nlohmann::json::error_handler_t::replace );
}
std::string TracyLlmTools::SamplingStats( const std::string& query, uint32_t limit ) const
{
if( !m_worker.AreSymbolSamplesReady() ) return "Sampling data is not ready yet. Wait for background processing to complete.";
if( m_worker.GetCallstackSampleCount() == 0 ) return "No call stack samples in this trace.";
std::regex rx;
if( !query.empty() )
{
try
{
rx = std::regex( query );
}
catch( const std::regex_error& e )
{
return "Error: Invalid query regex: " + std::string( e.what() );
}
}
const auto& symMap = m_worker.GetSymbolMap();
const auto& symStat = m_worker.GetSymbolStats();
struct SymEntry
{
uint64_t symAddr;
uint32_t excl;
};
std::vector<SymEntry> data;
data.reserve( symStat.size() );
for( auto& v : symStat )
{
auto sit = symMap.find( v.first );
if( sit == symMap.end() ) continue;
data.emplace_back( v.first, v.second.excl );
}
if( data.empty() ) return "No symbol statistics available.";
unordered_flat_map<uint64_t, SymEntry> baseMap;
for( auto& v : data )
{
auto sym = m_worker.GetSymbolData( v.symAddr );
const auto symAddr = ( sym && sym->isInline ) ? m_worker.GetSymbolForAddress( v.symAddr ) : v.symAddr;
auto it = baseMap.find( symAddr );
if( it == baseMap.end() )
{
baseMap.emplace( symAddr, SymEntry { symAddr, v.excl } );
}
else
{
assert( symAddr == it->second.symAddr );
it->second.excl += v.excl;
}
}
data.clear();
for( auto& v : baseMap )
{
auto sit = symMap.find( v.second.symAddr );
if( sit == symMap.end() ) continue;
if( !query.empty() )
{
const auto name = m_worker.GetString( sit->second.name );
if( !std::regex_search( name, rx ) ) continue;
}
data.emplace_back( v.second );
}
if( data.empty() ) return "No symbols match the query.";
pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l.excl > r.excl; } );
if( data.size() > limit ) data.resize( limit );
const auto period = m_worker.GetSamplingPeriod();
const auto totalSamples = m_worker.GetCallstackSampleCount();
nlohmann::json result = {
{ "total_time", TimeToString( totalSamples * period ) },
{ "entries", nlohmann::json::array() },
{ "hint", "Entries are sorted by exclusive time (child time not included), highest first." }
};
auto& entries = result["entries"];
for( auto& v : data )
{
auto sit = symMap.find( v.symAddr );
assert( sit != symMap.end() );
char addr[32];
snprintf( addr, sizeof( addr ), "0x%" PRIx64, v.symAddr );
const auto file = m_worker.GetString( sit->second.file );
char loc[1024];
snprintf( loc, sizeof( loc ), "%s:%u", file, sit->second.line );
entries.push_back( {
{ "name", m_worker.GetString( sit->second.name ) },
{ "address", addr },
{ "location", loc },
{ "image", m_worker.GetString( sit->second.imageName ) },
{ "time", TimeToString( v.excl * period ) },
{ "code_size", MemSizeToString( sit->second.size.Val() ) }
} );
}
return result.dump( -1, ' ', false, nlohmann::json::error_handler_t::replace );
}
#endif
}

View File

@@ -68,6 +68,7 @@ private:
std::string SymbolDisasm( const std::string& address ) const;
#ifndef TRACY_NO_STATISTICS
std::string SymbolParents( const std::string& address, uint32_t limit ) const;
std::string SamplingStats( const std::string& query, uint32_t limit ) const;
#endif
void ManualEmbeddingsWorker( TracyLlmApi& api );

View File

@@ -3,10 +3,6 @@
#ifndef TRACY_NO_CALLSTACK
# if !defined _WIN32
# include <sys/param.h>
# endif
# if defined _WIN32
# include "../common/TracyWinFamily.hpp"
# if !defined TRACY_WIN32_NO_DESKTOP
@@ -26,7 +22,7 @@
# endif
# elif defined __APPLE__
# define TRACY_HAS_CALLSTACK 4
# elif defined BSD
# elif defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
# define TRACY_HAS_CALLSTACK 6
# endif

View File

@@ -15,7 +15,6 @@
# endif
#else
# include <sys/time.h>
# include <sys/param.h>
#endif
#ifdef _GNU_SOURCE
@@ -29,7 +28,7 @@
# include <sys/syscall.h>
#endif
#if defined __APPLE__ || defined BSD
#if defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
# include <sys/types.h>
# include <sys/sysctl.h>
#endif
@@ -373,6 +372,137 @@ static void InitFailure( const char* msg )
exit( 1 );
}
#if defined __linux__ && defined TRACY_HAS_RDTSC && defined TRACY_HAS_SYSTEM_TRACING
#include <linux/perf_event.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
// Linux exposes time_zero/time_mult/time_shift on the perf_event mmap page for
// converting sample timestamps back to TSC cycles. cap_user_time_zero only
// states the conversion math is valid; the underlying counter is the kernel's
// sched_clock, which on x86 is TSC iff sched_clock_stable(). On Ryzen the
// kernel can set cap_user_time_zero while TSC silently drifts between cores
// before the clocksource watchdog catches it. This probe verifies the
// conversion lands inside an rdtsc-sandwiched window for a synthesized
// PERF_RECORD_COMM, mirroring tools/perf/tests/perf-time-to-tsc.c.
static bool CheckLinuxPerfTscConsistency()
{
int csfd = open( "/sys/devices/system/clocksource/clocksource0/current_clocksource", O_RDONLY );
if( csfd >= 0 )
{
char buf[32] = {};
auto n = read( csfd, buf, sizeof( buf ) - 1 );
close( csfd );
while( n > 0 && ( buf[n - 1] == '\n' || buf[n - 1] == '\r' ) ) buf[--n] = 0;
if( n >= 3 && memcmp( buf, "tsc", 3 ) != 0 )
{
TracyDebug( "TSC check: clocksource is '%s', not 'tsc'; TSC considered unreliable", buf );
return false;
}
}
perf_event_attr attr = {};
attr.type = PERF_TYPE_SOFTWARE;
attr.config = PERF_COUNT_SW_DUMMY;
attr.size = sizeof( attr );
attr.sample_type = PERF_SAMPLE_TIME;
attr.sample_id_all = 1;
attr.comm = 1;
attr.disabled = 1;
attr.exclude_kernel = 1;
const int pfd = (int)syscall( SYS_perf_event_open, &attr, 0, -1, -1, 0 );
if( pfd < 0 )
{
TracyDebug( "TSC check: perf_event_open failed (%s); skipping probe", strerror( errno ) );
return true;
}
const auto pageSize = (size_t)sysconf( _SC_PAGESIZE );
const auto mapSize = pageSize * 2;
auto map = mmap( nullptr, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, pfd, 0 );
if( map == MAP_FAILED ) { close( pfd ); return true; }
auto* meta = (perf_event_mmap_page*)map;
if( !meta->cap_user_time_zero )
{
munmap( map, mapSize );
close( pfd );
TracyDebug( "TSC check: cap_user_time_zero not set, kernel says no" );
return false;
}
ioctl( pfd, PERF_EVENT_IOC_ENABLE, 0 );
char origName[16] = {};
prctl( PR_GET_NAME, origName, 0, 0, 0 );
const uint64_t tsc0 = __builtin_ia32_rdtsc();
prctl( PR_SET_NAME, "tracy-tsc-prb", 0, 0, 0 );
const uint64_t tsc1 = __builtin_ia32_rdtsc();
ioctl( pfd, PERF_EVENT_IOC_DISABLE, 0 );
prctl( PR_SET_NAME, origName, 0, 0, 0 );
const auto head = std::atomic_load_explicit(
(std::atomic<uint64_t>*)&meta->data_head, std::memory_order_acquire );
const auto tail = meta->data_tail;
const auto dataMask = pageSize - 1;
char* data = (char*)map + pageSize;
bool tscOk = false;
bool sawComm = false;
uint64_t pos = tail;
while( pos < head )
{
perf_event_header hdr;
memcpy( &hdr, data + ( pos & dataMask ), sizeof( hdr ) );
if( hdr.type == PERF_RECORD_COMM )
{
sawComm = true;
uint64_t sampleTime;
const auto timeOff = pos + hdr.size - sizeof( uint64_t );
memcpy( &sampleTime, data + ( timeOff & dataMask ), sizeof( sampleTime ) );
const auto t = sampleTime - meta->time_zero;
const auto quot = t / meta->time_mult;
const auto rem = t % meta->time_mult;
const uint64_t cyc = ( quot << meta->time_shift )
+ ( rem << meta->time_shift ) / meta->time_mult;
// ~1 ms of slack at 3 GHz; covers prctl syscall + ring write latency.
const uint64_t slack = 3000000;
if( cyc + slack >= tsc0 && cyc <= tsc1 + slack )
{
tscOk = true;
}
else
{
TracyDebug( "TSC check: conversion outside rdtsc window "
"(tsc0=%llu cyc=%llu tsc1=%llu)",
(unsigned long long)tsc0,
(unsigned long long)cyc,
(unsigned long long)tsc1 );
}
break;
}
pos += hdr.size;
}
munmap( map, mapSize );
close( pfd );
if( !sawComm )
{
TracyDebug( "TSC check: no PERF_RECORD_COMM in probe ring; treating as inconclusive" );
return true;
}
return tscOk;
}
#endif
static bool CheckHardwareSupportsInvariantTSC()
{
const char* noCheck = GetEnvVar( "TRACY_NO_INVARIANT_CHECK" );
@@ -389,9 +519,23 @@ static bool CheckHardwareSupportsInvariantTSC()
#endif
}
CpuId( regs, 0x80000007 );
if( regs[3] & ( 1 << 8 ) ) return true;
if( !( regs[3] & ( 1 << 8 ) ) ) return false;
return false;
#if defined __linux__ && defined TRACY_HAS_RDTSC && defined TRACY_HAS_SYSTEM_TRACING
if( !CheckLinuxPerfTscConsistency() )
{
#if !defined TRACY_TIMER_QPC && !defined TRACY_TIMER_FALLBACK
InitFailure( "TSC reported as invariant by CPUID but failed runtime consistency check "
"(clocksource demoted or perf-to-TSC conversion mismatch).\n"
"Define TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\n"
"Alternatively rebuild with TRACY_TIMER_FALLBACK to enable the clock_gettime fallback." );
#else
return false;
#endif
}
#endif
return true;
}
#if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER
@@ -447,7 +591,7 @@ static const char* GetProcessName()
# endif
#elif defined __linux__ && defined _GNU_SOURCE
if( program_invocation_short_name ) processName = program_invocation_short_name;
#elif defined __APPLE__ || defined BSD
#elif defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
auto buf = getprogname();
if( buf ) processName = buf;
#elif defined __QNX__
@@ -725,7 +869,7 @@ static const char* GetHostInfo()
size_t sz = sizeof( memSize );
sysctlbyname( "hw.memsize", &memSize, &sz, nullptr, 0 );
ptr += sprintf( ptr, "RAM: %zu MB\n", memSize / 1024 / 1024 );
#elif defined BSD
#elif defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
size_t memSize;
size_t sz = sizeof( memSize );
sysctlbyname( "hw.physmem", &memSize, &sz, nullptr, 0 );
@@ -1534,7 +1678,7 @@ Profiler::Profiler()
#ifndef _WIN32
pipe(m_pipe);
# if defined __APPLE__ || defined BSD
# if defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
// FreeBSD/XNU don't have F_SETPIPE_SZ, so use the default
m_pipeBufSize = 16384;
# else

View File

@@ -11,7 +11,7 @@
# elif defined __APPLE__
# include <mach/mach_host.h>
# include <mach/host_info.h>
# elif defined BSD
# elif defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
# include <sys/types.h>
# include <sys/sysctl.h>
# endif
@@ -79,7 +79,7 @@ void SysTime::ReadTimes()
idle = info.cpu_ticks[CPU_STATE_IDLE];
}
# elif defined BSD
# elif defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
void SysTime::ReadTimes()
{
@@ -109,7 +109,7 @@ float SysTime::Get()
#if defined _WIN32
return diffUsed == 0 ? -1 : ( diffUsed - diffIdle ) * 100.f / diffUsed;
#elif defined __linux__ || defined __APPLE__ || defined BSD
#elif defined __linux__ || defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
const auto total = diffUsed + diffIdle;
return total == 0 ? -1 : diffUsed * 100.f / total;
#endif

View File

@@ -1,13 +1,7 @@
#ifndef __TRACYSYSTIME_HPP__
#define __TRACYSYSTIME_HPP__
#if defined _WIN32 || defined __linux__ || defined __APPLE__
# define TRACY_HAS_SYSTIME
#else
# include <sys/param.h>
#endif
#ifdef BSD
#if defined _WIN32 || defined __linux__ || defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
# define TRACY_HAS_SYSTIME
#endif

View File

@@ -27,7 +27,6 @@
#else
# include <arpa/inet.h>
# include <sys/socket.h>
# include <sys/param.h>
# include <errno.h>
# include <fcntl.h>
# include <netinet/in.h>
@@ -509,7 +508,7 @@ bool ListenSocket::Listen( uint16_t port, int backlog )
#if defined _WIN32
unsigned long val = 0;
setsockopt( m_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&val, sizeof( val ) );
#elif defined BSD
#elif defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
int val = 0;
setsockopt( m_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&val, sizeof( val ) );
val = 1;

View File

@@ -6,7 +6,7 @@
# include <windows.h>
#elif defined __linux__
# include <sys/sysinfo.h>
#elif defined __APPLE__ || defined BSD
#elif defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
# include <sys/types.h>
# include <sys/sysctl.h>
#endif
@@ -30,7 +30,7 @@ size_t GetPhysicalMemorySize()
size_t sz = sizeof( memSize );
sysctlbyname( "hw.memsize", &memSize, &sz, nullptr, 0 );
return memSize;
#elif defined BSD
#elif defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__
size_t memSize;
size_t sz = sizeof( memSize );
sysctlbyname( "hw.physmem", &memSize, &sz, nullptr, 0 );