@@ -32,23 +32,38 @@ using sofa::helper::AdvancedTimer;
3232namespace sofapython3
3333{
3434
35+ /* *
36+ * @brief Converts AdvancedTimer records to a Python dictionary structure
37+ *
38+ * This function processes the timer records and builds a hierarchical Python dictionary
39+ * that represents the timer data in a format that's easy to use in Python.
40+ *
41+ * @param id The timer ID to get records for
42+ * @return A Python dictionary representing the timer records
43+ */
3544py::dict getRecords (const std::string & id) {
3645 using sofa::helper::Record;
3746 using sofa::helper::system::thread::ctime_t ;
38- using sofa::helper::system::thread::CTime;
3947
40- static auto timer_freq = CTime::getTicksPerSec ();
41- auto getTime = [](ctime_t t)
48+ auto getTime = [](ctime_t t, ctime_t referenceTime)
4249 {
43- return 1000 . * t / timer_freq;
50+ constexpr double nbMillisecPerSec = 1000 .;
51+ return nbMillisecPerSec * sofa::helper::system::thread::CTime::toSecond (t - referenceTime);
4452 };
4553
4654 const auto records = AdvancedTimer::getRecords (id);
4755
48- std::stack<py::dict> tokens;
49- py::dict token, token_temp;
50- tokens.push (token);
51- ctime_t t0;
56+ // Stack of dictionaries that represents the hierarchical structure of timer records
57+ // Each element in the stack corresponds to a different level in the timer hierarchy.
58+ std::stack<py::dict> hierarchyStack;
59+
60+ // Current dictionary being processed at the top of the stack
61+ // Represents the most recently created level in the timer hierarchy.
62+ py::dict currentLevel;
63+
64+ py::dict token_temp;
65+ hierarchyStack.push (currentLevel);
66+ std::optional<ctime_t > referenceTime;
5267
5368 for (const auto & r : records)
5469 {
@@ -57,126 +72,96 @@ py::dict getRecords(const std::string & id) {
5772 case Record::RNONE:
5873 break ;
5974 case Record::RBEGIN: // Timer begins
60- token = tokens.top ();
61- if (token.contains (r.label .c_str ()))
75+ case Record::RSTEP_BEGIN: // Step begins
76+ currentLevel = hierarchyStack.top ();
77+ if (currentLevel.contains (r.label .c_str ()))
6278 {
63- if (py::list::check_ (token [r.label .c_str ()]))
79+ if (py::list::check_ (currentLevel [r.label .c_str ()]))
6480 {
6581 token_temp = py::dict ();
66- py::list (token [r.label .c_str ()]).append (token_temp);
67- token = token_temp;
82+ py::list (currentLevel [r.label .c_str ()]).append (token_temp);
83+ currentLevel = token_temp;
6884 }
69- else if (py::dict::check_ (token [r.label .c_str ()]))
85+ else if (py::dict::check_ (currentLevel [r.label .c_str ()]))
7086 {
71- token_temp = token [r.label .c_str ()];
72- token [r.label .c_str ()] = py::list ();
73- py::list (token [r.label .c_str ()]).append (token_temp);
87+ token_temp = currentLevel [r.label .c_str ()];
88+ currentLevel [r.label .c_str ()] = py::list ();
89+ py::list (currentLevel [r.label .c_str ()]).append (token_temp);
7490 token_temp = py::dict ();
75- py::list (token [r.label .c_str ()]).append (token_temp);
76- token = token_temp;
91+ py::list (currentLevel [r.label .c_str ()]).append (token_temp);
92+ currentLevel = token_temp;
7793 }
7894 else
7995 {
80- msg_error (" Timer::getRecords" ) << " Got an unexpected token of type '" << std::string (py::str (token .get_type ())) << " '." ;
96+ msg_error (" Timer::getRecords" ) << " Got an unexpected token of type '" << std::string (py::str (currentLevel .get_type ())) << " '." ;
8197 break ;
8298 }
8399 }
84100 else
85101 {
86- token[r.label .c_str ()] = py::dict ();
87- token = token[r.label .c_str ()];
102+ // Creating a new level in the hierarchy for the current timer label
103+ currentLevel[r.label .c_str ()] = py::dict ();
104+ // Update the current level to the one just added
105+ currentLevel = currentLevel[r.label .c_str ()];
88106 }
89- t0 = r.time ;
90- token[" start_time" ] = getTime (r.time - t0);
91- tokens.push (token);
92- break ;
93- case Record::REND: // Timer ends
94- token = tokens.top ();
95- token[" end_time" ] = getTime (r.time - t0);
96- token[" total_time" ] = getTime (r.time - t0) - py::cast<float >(token[" start_time" ]);
97- tokens.pop ();
98- break ;
99- case Record::RSTEP_BEGIN: // Step begins
100- token = tokens.top ();
101- if (token.contains (r.label .c_str ()))
107+ if (r.type == Record::RBEGIN)
102108 {
103- if (py::list::check_ (token[r.label .c_str ()]))
104- {
105- token_temp = py::dict ();
106- py::list (token[r.label .c_str ()]).append (token_temp);
107- token = token_temp;
108- }
109- else if (py::dict::check_ (token[r.label .c_str ()]))
110- {
111- token_temp = token[r.label .c_str ()];
112- token[r.label .c_str ()] = py::list ();
113- py::list (token[r.label .c_str ()]).append (token_temp);
114- token_temp = py::dict ();
115- py::list (token[r.label .c_str ()]).append (token_temp);
116- token = token_temp;
117- }
118- else
119- {
120- msg_error (" Timer::getRecords" ) << " Got an unexpected token of type '" << std::string (py::str (token.get_type ())) << " '." ;
121- break ;
122- }
109+ referenceTime = r.time ;
123110 }
124- else
111+ if (!referenceTime. has_value ())
125112 {
126- token[r. label . c_str ()] = py::dict () ;
127- token = token[r. label . c_str ()] ;
113+ msg_error ( " Timer::getRecords " ) << " Reference time not set. " ;
114+ break ;
128115 }
129- token [" start_time" ] = getTime (r.time - t0 );
130- tokens .push (token );
116+ currentLevel [" start_time" ] = getTime (r.time , *referenceTime );
117+ hierarchyStack .push (currentLevel );
131118 break ;
119+ case Record::REND: // Timer ends
132120 case Record::RSTEP_END: // Step ends
133- token = tokens .top ();
134- token [" end_time" ] = getTime (r.time - t0 );
135- token [" total_time" ] = getTime (r.time - t0 ) - py::cast<float >(token [" start_time" ]);
136- tokens .pop ();
121+ currentLevel = hierarchyStack .top ();
122+ currentLevel [" end_time" ] = getTime (r.time , *referenceTime );
123+ currentLevel [" total_time" ] = getTime (r.time , *referenceTime ) - py::cast<float >(currentLevel [" start_time" ]);
124+ hierarchyStack .pop ();
137125 break ;
138126 case Record::RVAL_SET: // Sets a value
139- token = tokens.top ();
140- token[r.label .c_str ()] = r.val ;
141- break ;
142127 case Record::RVAL_ADD: // Sets a value
143- token = tokens .top ();
144- token [r.label .c_str ()] = r.val ;
128+ currentLevel = hierarchyStack .top ();
129+ currentLevel [r.label .c_str ()] = r.val ;
145130 break ;
146131 default :
147- token = tokens .top ();
148- token [r.label .c_str ()] = py::list ();
149- token = token [r.label .c_str ()];
150- token [" start_time" ] = r.time ;
132+ currentLevel = hierarchyStack .top ();
133+ currentLevel [r.label .c_str ()] = py::list ();
134+ currentLevel = currentLevel [r.label .c_str ()];
135+ currentLevel [" start_time" ] = r.time ;
151136 break ;
152137 }
153138 }
154139
155140 // There should be two remaining records: Top level "record" + "timer starts". The "timer starts" record remains in
156141 // the stack since we normally get the records before the timer ends (ending the timer in Sofa destroys the
157142 // records...)
158- if (tokens .size () == 2 )
143+ if (hierarchyStack .size () == 2 )
159144 {
160- token = tokens .top ();
161- tokens .pop ();
145+ currentLevel = hierarchyStack .top ();
146+ hierarchyStack .pop ();
162147 }
163- else if (tokens .size () == 1 )
148+ else if (hierarchyStack .size () == 1 )
164149 {
165150 // This should not happen unless we successfully got the timer records AFTER the timer has ends, which would mean
166151 // that Sofa's advanced timer has improved, let not warn the user for that.
167- token = tokens .top ();
152+ currentLevel = hierarchyStack .top ();
168153 }
169154
170155 // Pop the last token ("records")
171- tokens .pop ();
156+ hierarchyStack .pop ();
172157
173158 // The stack should be empty by now
174- if (!tokens .empty ())
159+ if (!hierarchyStack .empty ())
175160 {
176- msg_error (" Timer::getRecords" ) << " Records stack leaked." ;
161+ msg_error (" Timer::getRecords" ) << " Records stack leaked ( " << hierarchyStack. size () << " elements) ." ;
177162 }
178163
179- return token ;
164+ return currentLevel ;
180165}
181166
182167py::module addSubmoduleTimer (py::module &m)
0 commit comments