@@ -2932,14 +2932,48 @@ bool installPortInParent(mach_port_t parentTaskPort, mach_port_t* outServicePort
29322932 return true ;
29332933}
29342934
2935+ // A launcher can start several independently wrapped processes with the same
2936+ // profiler environment. Expand against the process whose task port the
2937+ // sidecar received, not the sidecar's own pid, so they cannot truncate one
2938+ // another's output.
2939+ static bool expandTargetPid (std::string& path, pid_t targetPid, const char * variable) {
2940+ if (path.find (" %p" ) == std::string::npos) {
2941+ return true ;
2942+ }
2943+ if (targetPid <= 0 ) {
2944+ fprintf (stdout,
2945+ " [rosettax87] %s: cannot expand %%p because the target pid is unavailable; "
2946+ " profiling disabled\n " ,
2947+ variable);
2948+ return false ;
2949+ }
2950+
2951+ const std::string replacement = std::to_string (targetPid);
2952+ size_t offset = 0 ;
2953+ while ((offset = path.find (" %p" , offset)) != std::string::npos) {
2954+ path.replace (offset, 2 , replacement);
2955+ offset += replacement.size ();
2956+ }
2957+ return true ;
2958+ }
2959+
29352960bool spawnReceiveThread (mach_port_t servicePort, mach_port_t parentTaskPort) {
29362961 if (g_rosetta_config != nullptr && !g_rosetta_config->profile_path .empty ()) {
2937- const char * path = g_rosetta_config->profile_path .c_str ();
2938- g_profile.file = std::fopen (path, " wb" );
2962+ std::string path = g_rosetta_config->profile_path ;
2963+ pid_t targetPid = 0 ;
2964+ pid_for_task (parentTaskPort, &targetPid);
2965+ if (!expandTargetPid (path, targetPid, " X87_PROFILE" )) {
2966+ path.clear ();
2967+ }
2968+ g_profile.file = path.empty () ? nullptr : std::fopen (path.c_str (), " wb" );
29392969 if (g_profile.file == nullptr ) {
2940- fprintf (stdout, " [rosettax87] X87_PROFILE: failed to open '%s' for writing\n " , path);
2970+ if (!path.empty ()) {
2971+ fprintf (stdout, " [rosettax87] X87_PROFILE: failed to open '%s' for writing\n " ,
2972+ path.c_str ());
2973+ }
29412974 } else {
2942- fprintf (stdout, " [rosettax87] X87_PROFILE: dumping IR streams to '%s'\n " , path);
2975+ fprintf (stdout, " [rosettax87] X87_PROFILE: dumping IR streams to '%s'\n " ,
2976+ path.c_str ());
29432977 }
29442978 fflush (stdout);
29452979 }
@@ -3051,19 +3085,6 @@ void unlinkWindowSeries(const std::string& path) {
30513085 closedir (d);
30523086}
30533087
3054- // Several cooperative sidecars can inherit one X87_SAMPLE value from the same
3055- // launcher. Expand against the process whose task port we received, not this
3056- // sidecar's pid, so each profile names the process it actually sampled.
3057- static std::string expandSamplePid (std::string path, pid_t targetPid) {
3058- const std::string replacement = std::to_string (targetPid);
3059- size_t offset = 0 ;
3060- while ((offset = path.find (" %p" , offset)) != std::string::npos) {
3061- path.replace (offset, 2 , replacement);
3062- offset += replacement.size ();
3063- }
3064- return path;
3065- }
3066-
30673088void startSampler (mach_port_t parentTaskPort, uint64_t runtimeBase, const SamplerConfig& in) {
30683089 if (in.path .empty ()) {
30693090 return ;
@@ -3072,23 +3093,12 @@ void startSampler(mach_port_t parentTaskPort, uint64_t runtimeBase, const Sample
30723093 // Sweeping every thread faster than the sample rate is never what was
30733094 // meant: the sweep is the expensive mode and the rate is the cheap one.
30743095 cfg.sweep_interval_us = std::max (cfg.sweep_interval_us , cfg.interval_us );
3075- if (cfg.guest_range_pinned ) {
3076- g_counters.guest_lo .store (cfg.guest_lo , std::memory_order_relaxed);
3077- g_counters.guest_hi .store (cfg.guest_hi , std::memory_order_relaxed);
3078- }
3079- // Resolve the target before touching the output path. Wine starts one
3080- // sidecar per i386 process, and all of them inherit the same environment.
3081- // Expanding first keeps the injector from truncating the game's profile.
3096+ // The target's pid is needed by both threads and pid_for_task only answers
3097+ // while it is alive, so read it before touching the output path.
30823098 pid_t targetPid = 0 ;
3083- if (pid_for_task (parentTaskPort, &targetPid) != KERN_SUCCESS || targetPid <= 0 ) {
3084- if (cfg.path .find (" %p" ) != std::string::npos) {
3085- fprintf (stdout,
3086- " [rosettax87] X87_SAMPLE: cannot expand %%p because the target pid is "
3087- " unavailable; sampling disabled\n " );
3088- return ;
3089- }
3090- } else {
3091- cfg.path = expandSamplePid (cfg.path , targetPid);
3099+ pid_for_task (parentTaskPort, &targetPid);
3100+ if (!expandTargetPid (cfg.path , targetPid, " X87_SAMPLE" )) {
3101+ return ;
30923102 }
30933103 // Nothing from a previous run may survive into this one: a run that never
30943104 // latches writes no profile, and the absence has to be the answer rather
@@ -3101,7 +3111,6 @@ void startSampler(mach_port_t parentTaskPort, uint64_t runtimeBase, const Sample
31013111 unlink (cfg.path .c_str ());
31023112 unlink ((cfg.path + " .windows" ).c_str ());
31033113 unlinkWindowSeries (cfg.path );
3104-
31053114 pthread_attr_t attr;
31063115 pthread_attr_init (&attr);
31073116 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED );
0 commit comments