Skip to content

Improvements to RocprofLogger#1421

Open
jrmadsen wants to merge 1 commit into
pytorch:mainfrom
jrmadsen:jrmadsen/RocprofLogger-improvements
Open

Improvements to RocprofLogger#1421
jrmadsen wants to merge 1 commit into
pytorch:mainfrom
jrmadsen:jrmadsen/RocprofLogger-improvements

Conversation

@jrmadsen

@jrmadsen jrmadsen commented Jun 2, 2026

Copy link
Copy Markdown
  • Check return codes of rocprofiler-sdk API calls and log errors
  • Avoid using .handle for opaque types directly
  • Improve finalization variable lifetime and ordering
    • use placement new into buffer in .bss section to prevent destruction of GlobalContext singleton
    • Implement RocprofLogger::endTracing() to force invocation of finalization function
  • Remove superfluous logging_ and registered_ variables
    • can be handled by queries to rocprofiler-sdk API

- Check return codes of rocprofiler-sdk API calls and log errors
- Avoid using .handle for opaque types directly
- Improve finalization variable lifetime and ordering
  - use placement new into buffer in .bss section to prevent destruction of "GlobalContext" singleton
  - Implement RocprofLogger::endTracing() to force invocation of finalization function
- Remove superfluous logging_ and registered_ variables
  - can be handled by queries to rocprofiler-sdk API
@meta-cla

meta-cla Bot commented Jun 2, 2026

Copy link
Copy Markdown

Hi @jrmadsen!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@jrmadsen

Copy link
Copy Markdown
Author

I have signed the CLA.

@scotts

scotts commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@jrmadsen, thank you! The bot isn't picking up that you signed the CLA. We're also getting a failure on the ROCm PyTorch tests, but it may be an infra issue, so I've kicked it off again. Can you also rebase from main? We've made some testing improvements in the past week that may also help with that.

@meta-cla meta-cla Bot added the cla signed label Jun 17, 2026
@meta-cla

meta-cla Bot commented Jun 17, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-codesync

meta-codesync Bot commented Jun 17, 2026

Copy link
Copy Markdown

@scotts has imported this pull request. If you are a Meta employee, you can view this in D108910661.

return instance;
// placement new into .bss section to prevent destruction issues on shutdown.
static auto *instance = new (global_context_buffer.data()) GlobalContext{};
return *instance;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a legit, common trick (leaky singleton / immortal singleton).

Reserve a chunk of raw bytes big enough to hold a GlobalContext - the compiler sees plain bytes, not an object, so there's no destructor attached to it. Then construct a GlobalContext in this exact memory we reserved - instead of allocating fresh. Thus we get a real, live object, but since nothing owns it as a typed object with a destructor, it's never destroyed.

The problem:
std::array<std::byte, ...> only guarantees 1-byte alignment, because a single byte can sit anywhere. So the reserved buffer can start at any address, say one ending in ...3. Then placement-new drops a GlobalContext (and its std::mutex) onto that odd address. Now the mutex is misaligned: it's sitting somewhere the standard says it's never allowed to. That's undefined behavior. In practice it ranges from "works by luck" to torn reads, failed atomics, or crashes, and it's the kind of thing that's fine on one toolchain/arch and explodes on another.

Suggested patch:

alignas(GlobalContext) auto global_context_buffer =
    std::array<std::byte, sizeof(GlobalContext)>{};

alignas(GlobalContext) forces the buffer's starting address to satisfy whatever GlobalContext's strictest member needs (the mutex, etc). Now placement-new lands the object on a legal address and the undefined behavior is gone.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sanrise, I think we should be even simpler:

  GlobalContext& getGlobalContext() {
    static GlobalContext* instance = new GlobalContext{};  // intentionally leaked
    return *instance;
  }

I'd rather code be obviously correct, and the alignment operator with placement new is not obvious. We also intentionally leak global singletons in other places. If intentionally leaking the global context becomes a problem, we can deal with it then - but I doubt it will.

@mwootton

Copy link
Copy Markdown
Contributor

@scotts Are there any other changes @jrmadsen or I need to work on here? Thanks.

CHECKSTATUS), \
rocprofiler_get_status_string(CHECKSTATUS)); \
} \
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's safer to wrap this in the conventional do { ... } while (0).

}
} else if (status == 1) {
singleton().registered_ = true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now an empty branch - we should delete it.

auto& globalContext = getGlobalContext();
globalContext.name_info = rocprofiler::sdk::get_callback_tracing_names();
globalContext.buff_name_info = rocprofiler::sdk::get_buffer_tracing_names();
globalContext.finalizer = finialize_func;

@scotts scotts Jul 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two error paths, but the finalizer is still set. I think that's invalid - we should probably only set the finalizer after we're certain init will succeed.

Also, might as well fix the typo: finialize_func -> finalize_func

apiList.add("hipSetDevice");
apiList.add("hipGetLastError");
apiList.add("__hipPushCallConfiguration");
apiList.add("__hipPopCallConfiguration");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is removing these purposeful?

@scotts

scotts commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@mwootton, I added some other minor comments.

@mwootton

mwootton commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

I pinged @jrmadsen. Removing the items from the filter list looks like a mistake, those are pure noise AFAIK. They wrap each kernel launch api.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants