Ludovic,
there is an even better variant of your second approach, that's changing MetricCallbackEntry.m_caller to a std::string.
That's the same scheme EventCallbackEntry already uses, and m_caller is only used to identiy the registrant, so that's straight forward.
Regards,
Michael
Am 18.11.22 um 01:49 schrieb Ludovic LANGE:
Some news...
My issue was caused because of course I made a mistake with the way of registering my metrics Listener.
I used the following code (see my previous post) in the constructor:
+ MyMetrics.RegisterListener(IDTAG.c_str(), "*", std::bind(&canlog::MetricListener, this, _1));
and for the destructor:+ MyMetrics.DeregisterListener(IDTAG.c_str());
I had to use IDTAG because this class has multiple instances.
The issue is passing IDTAG.c_str() as a parameter ; because MyMetrics.RegisterListener stores the value of this const char* parameter and uses it 1) as an immutable parameter and 2) as a value.
Of course this does not work with my first way to call MyMetrics.RegisterListener :
- the memory buffer returned by IDTAG.c_str() has no more existence after exiting the constructor, so this lead to memory corruption.
- when calling IDTAG.c_str() again in the destructor, there is absolutely no chance that this c_str() call returns the same memory address as the call in the constructor, so the value comparison will never work.
I rewrote the code this way by adding a new member (m_idtag) to the class:
@ vehicle/OVMS.V3/components/can/src/canlog.cpp:352 @ canlog::canlog(const char* type, std::string format, canformat::canformat_serve_
m_dropcount = 0;
m_filtercount = 0;
+ m_idtag = strdup(IDTAG.c_str());
+
using std::placeholders::_1;
using std::placeholders::_2;
MyEvents.RegisterEvent(IDTAG, "*", std::bind(&canlog::EventListener, this, _1, _2));
+ if (NULL != m_idtag)
+ {
+ MyMetrics.RegisterListener(m_idtag, "*", std::bind(&canlog::MetricListener, this, _1));
+ }
+ else
+ {
+ ESP_LOGE(TAG,"Cannot allocate memory - not registering metrics listener");
+ }
int queuesize = MyConfig.GetParamValueInt("can", "log.queuesize",100);
m_queue = xQueueCreate(queuesize, sizeof(CAN_log_message_t));
@ vehicle/OVMS.V3/components/can/src/canlog.cpp:374 @ canlog::canlog(const char* type, std::string format, canformat::canformat_serve_
canlog::~canlog()
{
MyEvents.DeregisterEvent(IDTAG);
+ if (NULL != m_idtag)
+ {
+ MyMetrics.DeregisterListener(m_idtag);
+ free((void*)m_idtag);
+ }
if (m_task)
{
Another option is to change MyMetrics.RegisterListener so that it store *a copy* of the parameter, and does string comparison instead of a value comparison:
modified: vehicle/OVMS.V3/main/ovms_metrics.cpp
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
@ vehicle/OVMS.V3/main/ovms_metrics.cpp:1019 @ void OvmsMetrics::RegisterListener(const char* caller, const char* name, MetricC
return;
}
+ const char * caller_copy = (const char *)strdup(caller);
+
+ if (NULL == caller_copy)
+ {
+ ESP_LOGE(TAG, "Problem registering metric %s for caller %s - insufficient memory",name,caller);
+ return;
+ }
+
MetricCallbackList *ml = k->second;
- ml->push_back(new MetricCallbackEntry(caller,callback));
+ ml->push_back(new MetricCallbackEntry(caller_copy,callback));
}
void OvmsMetrics::DeregisterListener(const char* caller)
@ vehicle/OVMS.V3/main/ovms_metrics.cpp:1041 @ void OvmsMetrics::DeregisterListener(const char* caller)
while (itc!=ml->end())
{
MetricCallbackEntry* ec = *itc;
- if (ec->m_caller == caller)
+ if (strcmp(ec->m_caller, caller) == 0)
{
+ free((char *)ec->m_caller);
itc = ml->erase(itc);
delete ec;
}
I've tested both approaches and they both solve my issue : no more crashes and memory corruption.
The second one is more "risky" (as it potentially impacts everyone) but I think that it should not cause much regression.
Let me know what you think - should I make a PR for this second approach ?
Regards,