Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / nlfaultinjection / repo / src / nlfaultinjection.cpp
1 /*
2  *
3  *    Copyright 2016-2018 The nlfaultinjection Authors.
4  *
5  *    Licensed under the Apache License, Version 2.0 (the "License");
6  *    you may not use this file except in compliance with the License.
7  *    You may obtain a copy of the License at
8  *
9  *    http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *    Unless required by applicable law or agreed to in writing, software
12  *    distributed under the License is distributed on an "AS IS" BASIS,
13  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *    See the License for the specific language governing permissions and
15  *    limitations under the License.
16  *
17  */
18
19 /**
20  *    @file
21  *      Implementation of the fault-injection utilities.
22  */
23
24 #ifndef __STDC_LIMIT_MACROS
25 #define __STDC_LIMIT_MACROS
26 #endif
27 #include <stdint.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include <nlassert.h>
32
33 #include <nlfaultinjection.hpp>
34
35 namespace nl {
36
37 namespace FaultInjection {
38
39 static void Die(void) __attribute__((noreturn));
40
41 static GlobalContext *sGlobalContext = NULL;
42
43 /**
44  * The callback function that implements the deterministic
45  * injection feature (see FailAtFault).
46  */
47 static bool DeterministicCbFn(Identifier aId,
48                               Record *aRecord,
49                               void *aContext)
50 {
51     bool retval = false;
52
53     (void)aId;
54     (void)aContext;
55
56     if (aRecord->mNumCallsToSkip)
57     {
58         aRecord->mNumCallsToSkip--;
59     }
60     else if (aRecord->mNumCallsToFail)
61     {
62         aRecord->mNumCallsToFail--;
63         retval = true;
64     }
65
66     return retval;
67 }
68
69 /**
70  * Callback list node for DeterministicCbFn.
71  * This node terminates all callback lists.
72  */
73 static Callback sDeterministicCb = { DeterministicCbFn, NULL, NULL };
74
75 /**
76  * The callback function that implements the random
77  * injection feature (see FailRandomlyAtFault).
78  */
79 static bool RandomCbFn(Identifier aId,
80                        Record *aRecord,
81                        void *aContext)
82 {
83     bool retval = false;
84
85     (void)aId;
86     (void)aContext;
87
88     if (aRecord->mPercentage > 0)
89     {
90         int randValue = (rand() % 100) + 1;
91         if (randValue <= aRecord->mPercentage)
92         {
93             retval = true;
94         }
95     }
96
97     return retval;
98 }
99
100 /**
101  * Callback list node for RandomCbFn.
102  * Note that this is initialized to point to sDeterministicCb.
103  * All Record instances are initialized to point to
104  * this callback node.
105  */
106 static Callback sRandomCb = { RandomCbFn, NULL, &sDeterministicCb };
107
108 /**
109  * Alias for the address of the first default callback.
110  */
111 static const Callback *sEndOfCustomCallbacks = &sRandomCb;
112
113 /**
114  * Initialize the Manager instance.
115  *
116  * @param[in]   inNumFaults     The size of inFaultArray, equal to the number of fault IDs.
117  * @param[in]   inFaultArray    A pointer to an array of Record, in which this object
118  *                              will store the configuration of each fault.
119  * @param[in]   inManagerName   A pointer to a C string containing the name of the Manager.
120  * @param[in]   inFaultNames    A pointer to an array of inNumFaults C strings that describe
121  *                              each fault ID.
122  *
123  * @return      -EINVAL if the inputs are not valid.
124  *              0 otherwise.
125  */
126 int32_t Manager::Init(size_t inNumFaults,
127                       Record *inFaultArray,
128                       Name inManagerName,
129                       const Name *inFaultNames)
130 {
131     int32_t err = 0;
132     Identifier i;
133
134     nlEXPECT_ACTION((inNumFaults > 0 && inFaultArray && inManagerName && inFaultNames), exit, err = -EINVAL);
135
136     mName = inManagerName;
137     mNumFaults = inNumFaults;
138     mFaultRecords = inFaultArray;
139     mFaultNames = inFaultNames;
140     mLock = NULL;
141     mUnlock = NULL;
142     mLockContext = NULL;
143
144     // Link all callback lists to the two default callbacks.
145     for (i = 0; i < mNumFaults; i++)
146     {
147         mFaultRecords[i].mCallbackList = &sRandomCb;
148     }
149
150 exit:
151     return err;
152 }
153
154 /**
155  * Configure a fault to be triggered randomly, with a given probability defined as a percentage
156  * This is meant to be used on live systems to generate a build that will encounter random failures.
157  *
158  * @param[in]   inId            The fault ID
159  * @param[in]   inPercentage    An integer between 0 and 100. 100 means "always". 0 means "never".
160  *
161  * @return      -EINVAL if the inputs are not valid.
162  *              0 otherwise.
163  */
164 int32_t Manager::FailRandomlyAtFault(Identifier inId,
165                                      uint8_t inPercentage)
166 {
167     int32_t err = 0;
168
169     nlEXPECT_ACTION((inId < mNumFaults && inPercentage <= 100),
170                     exit,
171                     err = -EINVAL);
172
173     Lock();
174
175     mFaultRecords[inId].mNumCallsToSkip = 0;
176     mFaultRecords[inId].mNumCallsToFail = 0;
177     mFaultRecords[inId].mPercentage = inPercentage;
178
179     Unlock();
180
181 exit:
182     return err;
183 }
184
185 /**
186  * Configure a fault to be triggered deterministically.
187  *
188  * @param[in]   inId                The fault ID
189  * @param[in]   inNumCallsToSkip    The number of times this fault is to be skipped before it
190  *                                  starts to fail.
191  * @param[in]   inNumCallsToFail    The number of times the fault should be triggered.
192  * @param[in]   inTakeMutex         By default this method takes the Manager's mutex.
193  *                                  If inTakeMutex is set to kMutexDoNotTake, the mutex is not taken.
194  *
195  * @return      -EINVAL if the inputs are not valid.
196  *              0 otherwise.
197  */
198 int32_t Manager::FailAtFault(Identifier inId,
199                              uint32_t inNumCallsToSkip,
200                              uint32_t inNumCallsToFail,
201                              bool inTakeMutex)
202 {
203     int32_t err = 0;
204
205     nlEXPECT_ACTION(inId < mNumFaults && inNumCallsToSkip <= UINT16_MAX && inNumCallsToFail <= UINT16_MAX, exit, err = -EINVAL);
206
207     if (inTakeMutex)
208     {
209         Lock();
210     }
211
212     mFaultRecords[inId].mNumCallsToSkip = static_cast<uint16_t>(inNumCallsToSkip);
213     mFaultRecords[inId].mNumCallsToFail = static_cast<uint16_t>(inNumCallsToFail);
214     mFaultRecords[inId].mPercentage = 0;
215
216     if (inTakeMutex)
217     {
218         Unlock();
219     }
220
221 exit:
222     return err;
223 }
224
225 /**
226  * @overload int32_t FailAtFault(Identifier inId, uint32_t inNumCallsToSkip, uint32_t inNumCallsToFail, bool inTakeMutex)
227  */
228 int32_t Manager::FailAtFault(Identifier inId,
229                              uint32_t inNumCallsToSkip,
230                              uint32_t inNumCallsToFail)
231 {
232     return FailAtFault(inId, inNumCallsToSkip, inNumCallsToFail, kMutexTake);
233 }
234
235 /**
236  * Configure a fault to reboot the system when triggered.
237  * If the application has installed a RebootCallbackFn, it will
238  * be invoked when fault inId is triggered.
239  * If the application has not installed the callback, the system
240  * will crash.
241  *
242  * @param[in]   inId                The fault ID
243  *
244  * @return      -EINVAL if the inputs are not valid.
245  *              0 otherwise.
246  */
247 int32_t Manager::RebootAtFault(Identifier inId)
248 {
249     int32_t err = 0;
250
251     nlEXPECT_ACTION(inId < mNumFaults, exit, err = -EINVAL);
252
253     Lock();
254
255     mFaultRecords[inId].mReboot = true;
256
257     Unlock();
258
259 exit:
260     return err;
261 }
262
263 /**
264  * Store a set of arguments for a given fault ID.
265  * The array of arguments is made available to the code injected with
266  * the nlFAULT_INJECT macro.
267  * For this to work for a given fault ID, the Manager must allocate memory to
268  * store the arguments and configure the Record's mLengthOfArguments and
269  * mArguments members accordingly.
270  *
271  * @param[in]   inId                The fault ID
272  * @param[in]   inNumArgs           The number of arguments in the array pointed to by inArgs.
273  * @param[in]   inArgs              The pointer to the array of integers to be stored in the fault
274  *
275  * @return      -EINVAL if the inputs are not valid.
276  *              0 otherwise.
277  */
278 int32_t Manager::StoreArgsAtFault(Identifier inId, uint16_t inNumArgs, int32_t *inArgs)
279 {
280     int32_t err = 0;
281     size_t i;
282
283     nlEXPECT_ACTION(inId < mNumFaults &&
284                     mFaultRecords[inId].mArguments != NULL &&
285                     mFaultRecords[inId].mLengthOfArguments >= inNumArgs &&
286                     inNumArgs <= UINT8_MAX,
287                     exit,
288                     err = -EINVAL);
289
290     Lock();
291
292     for (i = 0; i < inNumArgs; i++)
293     {
294         mFaultRecords[inId].mArguments[i] = inArgs[i];
295     }
296
297     mFaultRecords[inId].mNumArguments = static_cast<uint8_t>(inNumArgs);
298
299     Unlock();
300
301 exit:
302     return err;
303 }
304
305 /**
306  * Attach a callback to a fault ID.
307  * Calling this twice does not attach the callback twice.
308  *
309  * @param[in]   inId        The fault ID
310  * @param[in]   inCallback  The callback node to be attached to the fault
311  *
312  *
313  * @return      -EINVAL if the inputs are not valid.
314  *              0 otherwise.
315  */
316 int32_t Manager::InsertCallbackAtFault(Identifier inId,
317                                        Callback *inCallBack)
318 {
319     int32_t err = 0;
320
321     // Make sure it's not already there
322     err = RemoveCallbackAtFault(inId, inCallBack);
323
324     nlEXPECT_SUCCESS(err, exit);
325
326     Lock();
327
328     // Insert the callback at the beginning of the list.
329     // Remember that all lists end into the two default (deterministic
330     // and random) callbacks!
331     inCallBack->mNext = mFaultRecords[inId].mCallbackList;
332     mFaultRecords[inId].mCallbackList = inCallBack;
333
334     Unlock();
335
336 exit:
337     return err;
338 }
339
340 /**
341  * Detaches a callback from a fault.
342  *
343  * @param[in]   inId        The fault
344  * @param[in]   inCallback  The callback node to be removed.
345  * @param[in]   inTakeMutex         By default this method takes the Manager's mutex.
346  *                                  If inTakeMutex is set to kMutexDoNotTake, the mutex is not taken.
347  *
348  * @return      -EINVAL if the inputs are not valid.
349  *              0 otherwise.
350  */
351 int32_t Manager::RemoveCallbackAtFault(Identifier inId,
352                                        Callback *inCallBack,
353                                        bool inTakeMutex)
354 {
355     int32_t err = 0;
356     Callback **cb = NULL;
357
358     nlEXPECT_ACTION((inId < mNumFaults) && (inCallBack != NULL), exit, err = -EINVAL);
359
360     if (inTakeMutex)
361     {
362         Lock();
363     }
364
365     cb = &mFaultRecords[inId].mCallbackList;
366
367     while (*cb != NULL)
368     {
369         if (*cb == inCallBack)
370         {
371             *cb = (*cb)->mNext;
372             break;
373         }
374         cb = &((*cb)->mNext);
375     }
376
377     if (inTakeMutex)
378     {
379         Unlock();
380     }
381
382 exit:
383     return err;
384 }
385
386 /**
387  * @overload int32_t Manager::RemoveCallbackAtFault(Identifier inId, Callback *inCallBack, bool inTakeMutex)
388  */
389 int32_t Manager::RemoveCallbackAtFault(Identifier inId,
390                                        Callback *inCallBack)
391 {
392     return RemoveCallbackAtFault(inId, inCallBack, kMutexTake);
393 }
394
395 /**
396  * When the program traverses the location at which a fault should be injected, this method is invoked
397  * on the manager to query the configuration of the fault ID.
398  *
399  * A fault can be triggered randomly, deterministically or on a call-by-call basis by a callback.
400  * All three types of trigger can be installed at the same time, and they all get a chance of
401  * injecting the fault.
402  *
403  * @param[in] inId                The fault ID
404  * @param[in] inTakeMutex         By default this method takes the Manager's mutex.
405  *                                If inTakeMutex is set to kMutexDoNotTake, the mutex is not taken.
406  *
407  * @return    true if the fault should be injected; false otherwise.
408  */
409 bool Manager::CheckFault(Identifier inId, bool inTakeMutex)
410 {
411     bool retval = false;
412     Callback *cb = NULL;
413     Callback *next = NULL;
414     bool reboot = false;
415
416     nlEXPECT(inId < mNumFaults, exit);
417
418     if (inTakeMutex)
419     {
420         Lock();
421     }
422
423     cb = mFaultRecords[inId].mCallbackList;
424
425     while (cb != NULL)
426     {
427         // Save mNext now, in case the callback removes itself
428         // calling RemoveCallbackAtFault
429         next = cb->mNext;
430         if (cb->mCallBackFn(inId, &mFaultRecords[inId], cb->mContext))
431         {
432             retval = true;
433         }
434         cb = next;
435     }
436
437     reboot = mFaultRecords[inId].mReboot;
438
439     if (retval && sGlobalContext && sGlobalContext->mCbTable.mPostInjectionCb)
440     {
441         sGlobalContext->mCbTable.mPostInjectionCb(this, inId, &mFaultRecords[inId]);
442     }
443
444     if (retval && reboot)
445     {
446         // If the application has not setup a context and/or reboot callback, the system will crash
447         if (sGlobalContext && sGlobalContext->mCbTable.mRebootCb)
448         {
449             sGlobalContext->mCbTable.mRebootCb();
450         }
451         else
452         {
453             Die();
454         }
455     }
456
457     mFaultRecords[inId].mNumTimesChecked++;
458
459     if (inTakeMutex)
460     {
461         Unlock();
462     }
463
464 exit:
465     return retval;
466 }
467
468 /**
469  * @overload bool CheckFault(Identifier inId, bool inTakeMutex)
470  */
471 bool Manager::CheckFault(Identifier inId)
472 {
473     return CheckFault(inId, kMutexTake);
474 }
475
476 /**
477  * When the program traverses the location at which a fault should be injected, this method is invoked
478  * on the manager to query the configuration of the fault ID.
479  *
480  * This version of the method retrieves the arguments stored in the Record.
481  *
482  * A fault can be triggered randomly, deterministically or on a call-by-call basis by a callback.
483  * All three types of trigger can be installed at the same time, and they all get a chance of
484  * injecting the fault.
485  *
486  * @param[in] inId            The fault ID
487  * @param[in] outNumArgs      The length of the array pointed to by outArgs
488  * @param[in] outArgs         The array of arguments configured for the faultId
489  * @param[in] inTakeMutex     By default this method takes the Manager's mutex.
490  *                            If inTakeMutex is set to kMutexDoNotTake, the mutex is not taken.
491  *
492  * @return    true if the fault should be injected; false otherwise.
493  */
494 bool Manager::CheckFault(Identifier inId, uint16_t &outNumArgs, int32_t *&outArgs, bool inTakeMutex)
495 {
496     bool retval = false;
497
498     if (inTakeMutex)
499     {
500         Lock();
501     }
502
503     retval = CheckFault(inId, kMutexDoNotTake);
504     if (retval)
505     {
506         outNumArgs = mFaultRecords[inId].mNumArguments;
507         outArgs = mFaultRecords[inId].mArguments;
508     }
509
510     if (inTakeMutex)
511     {
512         Unlock();
513     }
514
515     return retval;
516 }
517
518 /**
519  * @overload bool CheckFault(Identifier inId, uint16_t &outNumArgs, int32_t *&outArgs, bool inTakeMutex)
520  */
521 bool Manager::CheckFault(Identifier inId, uint16_t &outNumArgs, int32_t *&outArgs)
522 {
523     return CheckFault(inId, outNumArgs, outArgs, kMutexTake);
524 }
525
526 /**
527  * Reset the counters in the fault Records
528  * Note that calling this method does not impact the current configuration
529  * in any way (including the number of times a fault is to be skipped
530  * before it should fail).
531  */
532 void Manager::ResetFaultCounters(void)
533 {
534     Identifier id = 0;
535
536     Lock();
537
538     for (id = 0; id < mNumFaults; id++)
539     {
540         mFaultRecords[id].mNumTimesChecked = 0;
541     }
542
543     Unlock();
544 }
545
546 /**
547  * Reset the configuration of a fault Record
548  *
549  * @param[in] inId        The fault ID
550  *
551  * @return      -EINVAL if the inputs are not valid.
552  *              0 otherwise.
553  */
554 int32_t Manager::ResetFaultConfigurations(Identifier inId)
555 {
556     Callback *cb;
557     int32_t err = 0;
558
559     nlEXPECT_ACTION((inId < mNumFaults),
560                     exit,
561                     err = -EINVAL);
562
563     Lock();
564
565     mFaultRecords[inId].mNumCallsToSkip = 0;
566     mFaultRecords[inId].mNumCallsToFail = 0;
567     mFaultRecords[inId].mPercentage = 0;
568     mFaultRecords[inId].mReboot = 0;
569     mFaultRecords[inId].mNumArguments = 0;
570
571     cb = mFaultRecords[inId].mCallbackList;
572     // All callback handling code in this module is based on the assumption
573     // that custom callbacks are inserted at the beginning of the list
574     while (cb != sEndOfCustomCallbacks && cb != NULL)
575     {
576         (void)RemoveCallbackAtFault(inId, cb, kMutexDoNotTake);
577         cb = mFaultRecords[inId].mCallbackList;
578     }
579
580     Unlock();
581
582 exit:
583     return err;
584 }
585
586 /**
587  * Reset the configuration of all fault Records
588  *
589  * @return      -EINVAL if the inputs are not valid.
590  *              0 otherwise.
591  */
592 int32_t Manager::ResetFaultConfigurations(void)
593 {
594     int32_t err = 0;
595     Identifier id = 0;
596
597     for (id = 0; id < mNumFaults; id++)
598     {
599         err = ResetFaultConfigurations(id);
600         nlEXPECT(err == 0, exit);
601     }
602
603 exit:
604     return err;
605 }
606
607 /**
608  * Take the Manager's mutex.
609  */
610 void Manager::Lock(void)
611 {
612     if (mLock)
613     {
614         mLock(mLockContext);
615     }
616 }
617
618 /**
619  * Release the Manager's mutex.
620  */
621 void Manager::Unlock(void)
622 {
623     if (mUnlock)
624     {
625         mUnlock(mLockContext);
626     }
627 }
628
629 /**
630  * Configure the instance of GlobalContext to use.
631  * On systems in which faults are configured and injected from different threads,
632  * this function should be called before threads are started.
633  *
634  * @param[in] inGlobalContext   Pointer to the GlobalContext provided by the application
635  */
636 void SetGlobalContext(GlobalContext *inGlobalContext)
637 {
638     sGlobalContext = inGlobalContext;
639 }
640
641 /**
642  * Parse an integer
643  *
644  * This implementation does not check for ERANGE, as it assumes a very simple
645  * underlying implementation of strtol.
646  *
647  * @param[in]  str      Pointer to a string representing an integer
648  *
649  * @param[out] num      Pointer to the integer result
650  *
651  * @return              true in case of success; false if the string does not
652  *                      contain an integer.
653  */
654 static bool ParseInt(const char *str, int32_t *num)
655 {
656     char *endptr = NULL;
657     long tmp;
658     bool retval = true;
659
660     tmp = strtol(str, &endptr, 10);
661     if (!endptr || *endptr != '\0')
662     {
663         retval = false;
664     }
665     else
666     {
667         *num = static_cast<int32_t>(tmp);
668     }
669
670     return retval;
671 }
672
673 /**
674  * Parse an unsigned integer
675  *
676  * @param[in]  str      Pointer to a string representing an insigned int
677  *
678  * @param[out] num      Pointer to the unsigned integer result
679  *
680  * @return              true in case of success; false if the string does not
681  *                      contain an unsigned integer.
682  */
683 static bool ParseUInt(const char *str, uint32_t *num)
684 {
685     bool retval = true;
686     int32_t tmpint = 0;
687
688     retval = ParseInt(str, &tmpint);
689     if (retval)
690     {
691         if (tmpint < 0)
692         {
693             retval = false;
694         }
695         else
696         {
697             *num = static_cast<uint32_t>(tmpint);
698         }
699     }
700
701     return retval;
702 }
703
704 /**
705  * Parse a fault-injection configuration string and apply the configuration.
706  *
707  * @param[in]   aFaultInjectionStr  The configuration string. An example of a valid string that
708  *                                  enables two faults is "system_buffer_f5_s1:inet_send_p33"
709  *                                  An example of a configuration string that
710  *                                  also passes three integer arguments to the fault point is
711  *                                  "system_buffer_f5_s1_a10_a7_a-4"
712  *                                  The format is
713  *                                  "<module>_<fault>_{f<numTimesToFail>[_s<numTimesToSkip>],p<randomFailurePercentage>}[_a<integer>]..."
714  *
715  * @param[in]   inArray             An array of GetManagerFn callbacks
716  *                                  to be used to parse the string.
717  *
718  * @param[in]   inArraySize         Num of elements in inArray
719  *
720  * @return      true  if the string can be parsed completely; false otherwise
721  */
722 bool ParseFaultInjectionStr(char *aFaultInjectionStr, const GetManagerFn *inArray, size_t inArraySize)
723 {
724     ManagerTable table = { inArray, inArraySize };
725     size_t numTables = 1;
726
727     return ParseFaultInjectionStr(aFaultInjectionStr, &table, numTables);
728 }
729
730 /**
731  * Parse a fault-injection configuration string and apply the configuration.
732  *
733  * @param[in]   aFaultInjectionStr  The configuration string. An example of a valid string that
734  *                                  enables two faults is "system_buffer_f5_s1:inet_send_p33"
735  *                                  An example of a configuration string that
736  *                                  also passes three integer arguments to the fault point is
737  *                                  "system_buffer_f5_s1_a10_a7_a-4"
738  *                                  The format is
739  *                                  "<module>_<fault>_{f<numTimesToFail>[_s<numTimesToSkip>],p<randomFailurePercentage>}[_a<integer>]..."
740  *
741  * @param[in]   inTables            An array of ManagerTable structures
742  *                                  to be used to parse the string.
743  *
744  * @param[in]   inNumTables         Size of inTables
745  *
746  * @return      true  if the string can be parsed completely; false otherwise
747  */
748 bool ParseFaultInjectionStr(char *aFaultInjectionStr, const ManagerTable *inTables, size_t inNumTables)
749 {
750     char *tok1 = NULL;
751     char *savePtr1 = NULL;
752     char *tok2 = NULL;
753     char *savePtr2 = NULL;
754     char *outerString = aFaultInjectionStr;
755     size_t i = 0;
756     nl::FaultInjection::Identifier j = 0;
757     int err = 0;
758     bool retval = false;
759     int32_t args[kMaxFaultArgs];
760     uint16_t numArgs = 0;
761
762     nl::FaultInjection::Manager *mgr = NULL;
763     nl::FaultInjection::Identifier faultId = 0;
764
765     memset(args, 0, sizeof(args));
766
767     while ((tok1 = strtok_r(outerString, ":", &savePtr1)))
768     {
769         uint32_t numTimesToFail = 0;
770         uint32_t numTimesToSkip = 0;
771         uint32_t percentage = 0;
772         bool gotPercentage = false;
773         bool gotReboot = false;
774         bool gotArguments = false;
775         const Name *faultNames = NULL;
776
777         outerString = NULL;
778
779         tok2 = strtok_r(tok1, "_", &savePtr2);
780         nlEXPECT(tok2 != NULL, exit);
781
782                 // this is the module
783                 for (i = 0; i < inNumTables; i++)
784         {
785             for (j = 0; j < inTables[i].mNumItems; j++)
786             {
787                 nl::FaultInjection::Manager &tmpMgr = inTables[i].mArray[j]();
788                 if (!strcmp(tok2, tmpMgr.GetName()))
789                 {
790                     mgr = &tmpMgr;
791                     break;
792                 }
793             }
794         }
795         nlEXPECT(mgr != NULL, exit);
796
797         tok2 = strtok_r(NULL, "_", &savePtr2);
798         nlEXPECT(tok2 != NULL, exit);
799
800         // this is the fault name
801         faultNames = mgr->GetFaultNames();
802                 for (j = 0; j < mgr->GetNumFaults(); j++)
803                 {
804                         if (!strcmp(tok2, faultNames[j]))
805                         {
806                                 faultId = j;
807                                 break;
808                         }
809                 }
810
811         nlEXPECT(j != mgr->GetNumFaults(), exit);
812
813         while ((tok2 = strtok_r(NULL, "_", &savePtr2)))
814         {
815             switch (tok2[0])
816             {
817                 case 'a':
818                     {
819                         int32_t tmp = 0;
820                         nlEXPECT(numArgs < kMaxFaultArgs, exit);
821
822                         gotArguments = true;
823
824                         nlEXPECT(ParseInt(&(tok2[1]), &tmp), exit);
825                         args[numArgs++] = tmp;
826                     }
827                     break;
828                 case 'f':
829                     nlEXPECT(ParseUInt(&(tok2[1]), &numTimesToFail), exit);
830                     break;
831                 case 's':
832                     nlEXPECT(ParseUInt(&(tok2[1]), &numTimesToSkip), exit);
833                     break;
834                 case 'p':
835                     gotPercentage = true;
836                     nlEXPECT(ParseUInt(&(tok2[1]), &percentage), exit);
837                     nlEXPECT(percentage <= 100, exit);
838                     break;
839                 case 'r':
840                     gotReboot = true;
841                     break;
842                 default:
843                     goto exit;
844                     break;
845             }
846         }
847
848         if (gotArguments)
849         {
850             err = mgr->StoreArgsAtFault(faultId, numArgs, args);
851             nlEXPECT_SUCCESS(err, exit);
852         }
853
854         if (gotPercentage)
855         {
856             err = mgr->FailRandomlyAtFault(faultId, static_cast<uint8_t>(percentage));
857             nlEXPECT_SUCCESS(err, exit);
858         }
859         else
860         {
861             err = mgr->FailAtFault(faultId, numTimesToSkip, numTimesToFail);
862             nlEXPECT_SUCCESS(err, exit);
863         }
864         if (gotReboot)
865         {
866             err = mgr->RebootAtFault(faultId);
867             nlEXPECT_SUCCESS(err, exit);
868         }
869     }
870
871     retval = true;
872
873 exit:
874     return retval;
875 }
876
877 /**
878  * Internal function to kill the process if a
879  * fault is supposed to reboot the process but the application
880  * has not installed a callback
881  */
882 static void Die(void)
883 {
884     while (true)
885         *((volatile long *)1) = 0;
886 }
887
888 } // namespace FaultInjection
889
890 } // namespace nl