Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added sample interator api #191

Merged
merged 3 commits into from
Apr 7, 2022
Merged

Added sample interator api #191

merged 3 commits into from
Apr 7, 2022

Conversation

JCash
Copy link
Contributor

@JCash JCash commented Apr 7, 2022

Fixes #190

@@ -4309,22 +4309,12 @@ static void Server_Update(Server* server)

#define SAMPLE_NAME_LEN 128

typedef enum SampleType
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to the header, and renamed to rmtSampleType

lib/Remotery.c Outdated
Comment on lines 6108 to 6111
if (g_Settings.sampletree_handler != NULL)
{
g_Settings.sampletree_handler(rmt, g_Settings.sampletree_context, sample_tree);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had to put it before the FreeSamples() as the sample pointers would be invalid after that call.

lib/Remotery.c Outdated
Comment on lines 8821 to 8843
RMT_API rmtSampleIterator _rmt_IterateChildren(rmtSample* sample)
{
rmtSampleIterator iterator;
iterator.sample = 0;
iterator.initial = sample != NULL ? sample->first_child : 0;
return iterator;
}

RMT_API rmtBool _rmt_IterateNext(rmtSampleIterator* iter)
{
if (iter->initial != NULL)
{
iter->sample = iter->initial;
iter->initial = 0;
}
else
{
if (iter->sample != NULL)
iter->sample = iter->sample->next_sibling;
}

return iter->sample != NULL ? RMT_TRUE : RMT_FALSE;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

lib/Remotery.c Outdated
Comment on lines 8846 to 8854
RMT_API const char* _rmt_SampleTreeGetThreadName(rmtMsgSampleTree* sample_tree)
{
return sample_tree->threadName;
}

RMT_API rmtSample* _rmt_SampleTreeGetRootSample(rmtMsgSampleTree* sample_tree)
{
return sample_tree->rootSample;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

At first, I passed the threadName explicitly to the callback function, but later thought that passing the opaque message pointer fits the api a bit better:

  • Easier to add more accessor functions later
  • Matches the Sample accessor api used in the callback api

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm... yes... that's a good counter argument. I'll edit my comment.

lib/Remotery.c Outdated
Comment on lines 8857 to 8865
RMT_API const char* _rmt_SampleGetName(Remotery* rmt, rmtSample* sample)
{
const char* name = StringTable_Find(rmt->string_table, sample->name_hash);
if (name == NULL)
{
return "null";
}
return name;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Needs the Remotery pointer, so I explicitly pass that in the callback.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You should use g_Remotery in the implementation instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking that too, but I was perhaps trying to remove the need to use a global variable.

The current setup in the PR was to address the initialization problem:

The problem with the user context and the remotery pointer is a catch 22:

// Setting up settings before creating the context
Remotery *rmt;
rmtSettings* settings = rmt_Settings();
settings->sampletree_handler = dumpTree;
settings->sampletree_context = rmt; // Not valid yet
rmt_CreateGlobalInstance(&rmt);

An alternative, albeit not very clean code flow either:

// Setup some setting variables before creating the Remotery instance...
Remotery *rmt;
rmtSettings* settings = rmt_Settings();
settings->port = port;
rmt_CreateGlobalInstance(&rmt);

// ... and set some settings after
settings->sampletree_handler = dumpTree;
settings->sampletree_context = rmt; // Not valid yet

But, I have no examples of other users use case, and they can for sure create their own custom contexts etc.

So I'll go ahead with removing the Remotery pointer from the callback function.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Most of the internal code uses the global variable g_Remotery. The API explicitly avoids requesting it from the user. It even has a DLL API to cater for this where the use calls rmt_SetGlobalInstance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, now I finally see what you mean. Fixing...

RMT_SampleType_OpenGL,
RMT_SampleType_Metal,
RMT_SampleType_Count,
} rmtSampleType;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given the other public interfaces in the header, I prefixed it with RMT_.

Might want to be upper case, to match style of rmtError enum?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is fine. I wanted to remove all error-messages-as-enums, anyway and replace them with 4 or 5 broader error enums that give you optional message retrieval.

lib/Remotery.h Outdated
@@ -363,6 +388,7 @@ typedef void* (*rmtMallocPtr)(void* mm_context, rmtU32 size);
typedef void* (*rmtReallocPtr)(void* mm_context, void* ptr, rmtU32 size);
typedef void (*rmtFreePtr)(void* mm_context, void* ptr);
typedef void (*rmtInputHandlerPtr)(const char* text, void* context);
typedef void (*rmtSampleTreeHandlerPtr)(Remotery* rmt, void* cbk_context, rmtMsgSampleTree* sample_tree);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The callback type and its arguments.

RMT_API rmtU64 _rmt_SampleGetTime(rmtSample* sample);
RMT_API rmtU64 _rmt_SampleGetSelfTime(rmtSample* sample);
RMT_API void _rmt_SampleGetColour(rmtSample* sample, rmtU8* r, rmtU8* g, rmtU8* b);
RMT_API rmtSampleType _rmt_SampleGetType(rmtSample* sample);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently, they all are "private", with the "_" prefix.

I guess they should also be wrapped with the "RMT_OPTIONAL" define.
Should it also be contained withing a new tag, e.g. RMT_ENABLED_ITERATORS ? Might be a bit overkill?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't worry about adding a RMT_ENABLED_ITERATORS - just RMT_ENABLED will do.

sample/dump.c Outdated
Comment on lines 67 to 75
void dumpTree(Remotery* rmt, void* ctx, rmtMsgSampleTree* sample_tree)
{
rmtSample* root = _rmt_SampleTreeGetRootSample(sample_tree);
const char* thread_name = _rmt_SampleTreeGetThreadName(sample_tree);

printf("// ******************** DUMP TREE: %s ************************\n", thread_name);

printTree(rmt, root, 0);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Example callback

sample/dump.c Outdated
Comment on lines 57 to 65
void printTree(Remotery* rmt, rmtSample* sample, int indent)
{
printSample(rmt, sample, indent);

rmtSampleIterator iter = _rmt_IterateChildren(sample);
while (_rmt_IterateNext(&iter)) {
printTree(rmt, iter.sample, indent+1);
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Example of iterator

@dwilliamson
Copy link
Collaborator

Really great stuff.

I started reviewing this before you left your comments so I'll address them now.

lib/Remotery.c Outdated
@@ -6115,6 +6105,11 @@ static rmtError Remotery_SendSampleTreeMessage(Remotery* rmt, Message* message)
error = bin_SampleTree(bin_buf, sample_tree);
rmt_EndCPUSample();

if (g_Settings.sampletree_handler != NULL)
{
g_Settings.sampletree_handler(rmt, g_Settings.sampletree_context, sample_tree);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rather than passing sample_tree and having to create the opaque type rmtMsgSampleTree, can you pass sample_tree->rootSample instead? Then you only need to expose the rmtSample type to the public API.

There is also then no need for _rmt_SampleTreeGetRootSample and _rmt_SampleTreeGetThreadName can be replaced with passing the thread name to your sampletree_handler.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Leave as is but could you rename to rmtSampleTree?

Copy link
Contributor Author

@JCash JCash Apr 7, 2022

Choose a reason for hiding this comment

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

Sure!

lib/Remotery.h Outdated
RMT_API rmtSample* _rmt_SampleTreeGetRootSample(rmtMsgSampleTree* sample_tree);

// Sample accessors
RMT_API const char* _rmt_SampleGetName(Remotery* rmt, rmtSample* sample);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Need a comment that accessing all this data should only be done from the callback and not another thread elsewhere. This is because functions like _rmt_SampleGetName use StringTable_Find which is currently only safe to access from the main Remotery thread.

@@ -661,6 +691,23 @@ RMT_API void _rmt_BeginMetalSample(rmtPStr name, rmtU32* hash_cache);
RMT_API void _rmt_EndMetalSample(void);
#endif

// Iterator
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this API compile and link when RMT_ENABLED=0? Prefix-underscores shouldn't be used here.

As an example:

RMT_API void _rmt_LogText(rmtPStr text);

This is the function signature that is part of the Private interface and this is the macro which gets compiled-out when RMT_ENABLED=0:

#define rmt_LogText(text)                                                           \
    RMT_OPTIONAL(RMT_ENABLED, _rmt_LogText(text))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have now added this, but I'm wondering if the use case is a bit different.
The client code will fail to compile on code like this if we simply omit the info:

error: expected expression
    const char* name = rmt_SampleGetName(ctx->rmt, sample);

I see two options:

  1. Each such interface needs to produce "valid" values, which is counter intuitive when using the RMT_OPTIONAL + RMT_ENABLED
  2. Letting the client control their code using #ifdef RMT_ENABLED explicitly around this particular piece of code.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, there's going to be a lot of errors if you implement some of them in terms of RMT_OPTIONAL as they need to return a value even when RMT_ENABLED=0.

One option is to return nothing and specify the output as an address parameter to the function call but that would then prevent you from initialising at the declaration point in C when multiple calls are involved.

Have you looked at using RMT_OPTIONAL_RET?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the api now to use the RMT_OPTIONAL+RMT_OPTIONAL_RET and the dump.c example builds and runs in -DRMT_ENABLED=0 as well.

lib/Remotery.c Outdated
Comment on lines 8846 to 8854
RMT_API const char* _rmt_SampleTreeGetThreadName(rmtMsgSampleTree* sample_tree)
{
return sample_tree->threadName;
}

RMT_API rmtSample* _rmt_SampleTreeGetRootSample(rmtMsgSampleTree* sample_tree)
{
return sample_tree->rootSample;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm... yes... that's a good counter argument. I'll edit my comment.

Renamed to rmtSampleTree
Added RMT_OPTIONAL functions for the iterator/sample api
@JCash JCash requested a review from dwilliamson April 7, 2022 13:56
Tweaked api for the iterator to make it work better with c89 and the RMT_OPTIONAL
Comment on lines +386 to +387
#define rmt_IterateChildren(iter, sample) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_IterateChildren(iter, sample))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tweaked the api from

rmtSampleIterator iter = rmt_IterateChildren(sample);

to

rmtSampleIterator iter;
rmt_IterateChildren(&iter, sample);

in order to both make it more C89 compatible, and also make it work with RMT_OPTIONAL better.

@dwilliamson
Copy link
Collaborator

I love this... going to get some tea and make sure I understand the implications of adding this before merge.

Thanks so much.

@dwilliamson dwilliamson merged commit 5c44432 into Celtoys:main Apr 7, 2022
@JCash JCash deleted the iterator-api branch April 13, 2022 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Api for iterating the profiler info
2 participants