@@ -169,7 +169,10 @@ class DiagsConfigState
169169 * @param[in] dtt DiagsTagType_Debug or DiagsTagType_Action.
170170 * @param[in] new_value 0, 1, or 2 (see class contract).
171171 * @pre new_value is in [0, 2].
172- * @post enabled(dtt) returns new_value on all subsequent calls.
172+ * @post enabled(dtt) returns new_value on all subsequent calls. As a side
173+ * effect, when dtt == DiagsTagType_Debug, DbgCtl::_config_mode is also
174+ * updated via a relaxed atomic store so that DbgCtl-based checks remain
175+ * in sync without acquiring a lock.
173176 * @errors None.
174177 * @thread-safety Must be called with external serialization against
175178 * concurrent enabled(dtt) reads.
@@ -195,19 +198,21 @@ class DiagsConfigState
195198 * via DiagsPtr.
196199 *
197200 * @thread-safety After construction, the instance is safe to use concurrently
198- * from any number of threads for emission (print, log, error). Reconfiguration
199- * methods (activate_taglist, deactivate_all, setup_diagslog, config_roll_*,
200- * reseat_diagslog, should_roll_*, set_std_output) acquire an internal mutex
201- * and are mutually exclusive with each other and with tag-table reads. See
202- * per-method @thread-safety blocks for details.
201+ * from any number of threads for emission (print, log, error). Tag-table
202+ * mutation methods (activate_taglist, deactivate_all) and log-pointer
203+ * swap methods (setup_diagslog, reseat_diagslog, should_roll_*,
204+ * set_std_output) acquire tag_table_lock internally. Rolling-policy
205+ * configuration methods (config_roll_diagslog, config_roll_outputlog) and
206+ * dump() do NOT acquire any lock; see per-method @thread-safety for details.
203207 */
204208class Diags : public DebugInterface
205209{
206210public:
207211 /* *
208212 * @brief Construct a Diags instance with the given initial configuration.
209213 *
210- * @param[in] prefix_string Tag prefix prepended to all debug output.
214+ * @param[in] prefix_string Tag prefix prepended to all debug output. Must
215+ * be non-empty.
211216 * @param[in] base_debug_tags Initial debug tag regex, or nullptr for none.
212217 * @param[in] base_action_tags Initial action tag regex, or nullptr for none.
213218 * @param[in] _diags_log BaseLogFile for diags.log output, or nullptr to
@@ -216,10 +221,11 @@ class Diags : public DebugInterface
216221 * the system default (LOGFILE_DEFAULT_PERMS).
217222 * @param[in] output_log_perm File permission bits for stdout/stderr log
218223 * files, or -1 for the system default.
219- * @pre None; safe to construct before any thread reads via diags().
224+ * @pre prefix_string is non-empty; safe to construct before any thread
225+ * reads via diags().
220226 * @post magic == DIAGS_MAGIC; tag tables are initialized from the given
221227 * regexes; diags_log is open if _diags_log is non-null and openable.
222- * @errors Allocation failures abort via ink_fatal . Log-open failures are
228+ * @errors Allocation failures abort via ink_abort . Log-open failures are
223229 * reported via log_log_error and result in a null m_fp for the log.
224230 * @thread-safety Single-threaded construction expected. After construction
225231 * the instance may be installed via DiagsPtr::set and used concurrently.
@@ -290,13 +296,16 @@ class Diags : public DebugInterface
290296 * @brief Test whether the given IP endpoint matches the configured debug
291297 * client IP.
292298 *
293- * @param[in] test_ip Endpoint to compare against debug_client_ip.
294- * @return True if test_ip equals debug_client_ip.
299+ * @param[in] test_ip Endpoint whose IP address is compared against the
300+ * configured debug client IP. The port is ignored.
301+ * @return True if the IP address of @a test_ip equals the configured
302+ * debug client IP; false otherwise or if no debug client IP is set.
295303 * @pre None.
296304 * @post No state change.
297305 * @errors None.
298- * @thread-safety Safe to call concurrently; debug_client_ip is written
299- * only during single-threaded reconfiguration.
306+ * @thread-safety DEFECT: reads debug_client_ip, a plain (non-atomic)
307+ * struct, while the live config-update callback may write it on another
308+ * thread. Intended semantics: relaxed atomic read.
300309 */
301310 bool
302311 test_override_ip (IpEndpoint const &test_ip)
@@ -393,15 +402,15 @@ class Diags : public DebugInterface
393402 /* *
394403 * @brief Emit a message unconditionally, regardless of tag state.
395404 *
396- * @param[in] tag Non-null C string label; included in output but not checked
397- * against the tag regex.
398- * @param[in] level DiagsLevel for routing and terminal-level handling .
405+ * @param[in] tag C string label included in output, or nullptr to omit the
406+ * tag prefix. Not checked against the tag regex.
407+ * @param[in] level DiagsLevel for sink routing .
399408 * @param[in] loc Source location of the call site, or nullptr.
400409 * @param[in] fmt Non-null printf-format string.
401410 * @pre fmt is non-null and arguments match its conversions.
402411 * @post Message emitted to all sinks enabled for level in config.outputs.
403- * For terminal levels (DiagsLevel_IsTerminal), the process exits — this
404- * call does not return .
412+ * Does not handle terminal levels; the process does not exit.
413+ * Use error_va() if terminal-level exit behavior is required .
405414 * @errors I/O errors during emission are absorbed; output may be lost.
406415 * @thread-safety Safe to call concurrently from any thread.
407416 */
@@ -417,7 +426,7 @@ class Diags : public DebugInterface
417426 /* *
418427 * @brief va_list form of print().
419428 *
420- * @param[in] tag Non-null tag label .
429+ * @param[in] tag Tag label, or nullptr to omit the tag prefix .
421430 * @param[in] level DiagsLevel for routing.
422431 * @param[in] loc Source location, or nullptr.
423432 * @param[in] fmt Non-null printf-format string.
@@ -433,11 +442,12 @@ class Diags : public DebugInterface
433442 /* *
434443 * @brief Emit a message only if the tag is active in DiagsTagType_Debug.
435444 *
436- * @param[in] tag Non-null C string naming the debug tag.
445+ * @param[in] tag C string naming the debug tag, or nullptr (null matches
446+ * unconditionally; see tag_activated).
437447 * @param[in] level DiagsLevel for routing.
438448 * @param[in] loc Source location, or nullptr.
439449 * @param[in] fmt Non-null printf-format string.
440- * @pre tag and fmt are non-null; arguments match fmt's conversions.
450+ * @pre fmt is non-null; arguments match fmt's conversions.
441451 * @post If on(tag) is true, message is emitted identically to print().
442452 * If on(tag) is false, no output is produced.
443453 * @errors Same as print().
@@ -458,16 +468,17 @@ class Diags : public DebugInterface
458468 /* *
459469 * @brief va_list form of log().
460470 *
461- * @param[in] tag Non- null tag name .
471+ * @param[in] tag Tag name, or nullptr ( null matches unconditionally) .
462472 * @param[in] level DiagsLevel for routing.
463473 * @param[in] loc Source location, or nullptr.
464474 * @param[in] fmt Non-null printf-format string.
465475 * @param[in] ap Initialized va_list. Consumed; caller MUST NOT reuse
466476 * without va_end + va_start.
467- * @pre tag and fmt are non-null; ap is initialized.
477+ * @pre fmt is non-null; ap is initialized.
468478 * @post Same as log().
469479 * @errors Same as print().
470- * @thread-safety Same as log().
480+ * @thread-safety Same as log(). The on() call reads _enabled without a
481+ * lock; see DiagsConfigState::enabled().
471482 */
472483 void
473484 log_va (const char *tag, DiagsLevel level, const SourceLocation *loc, const char *fmt, va_list ap)
@@ -521,8 +532,10 @@ class Diags : public DebugInterface
521532 * @pre fp is a valid open stream.
522533 * @post Configuration summary written to fp.
523534 * @errors I/O errors on fp are not signaled.
524- * @thread-safety Safe to call concurrently; acquires tag_table_lock
525- * internally for the tag-table portion of output.
535+ * @thread-safety Not thread-safe. Reads config fields (_enabled,
536+ * outputs, base_debug_tags, base_action_tags) without holding
537+ * tag_table_lock. Concurrent reconfiguration may produce
538+ * interleaved or inconsistent output.
526539 */
527540 void dump (FILE *fp = stdout) const ;
528541
@@ -578,8 +591,10 @@ class Diags : public DebugInterface
578591 * @errors Open failures cause a false return and an internal log_log_error
579592 * diagnostic at LL_Error.
580593 * @thread-safety Caller MUST serialize with all other reconfiguration
581- * methods. reseat_diagslog() acquires tag_table_lock around its call;
582- * direct callers must provide equivalent serialization.
594+ * methods. reseat_diagslog() acquires tag_table_lock only for the
595+ * pointer swap that follows this call; setup_diagslog() itself is not
596+ * called under the lock. Direct callers must provide equivalent
597+ * serialization for the pointer swap.
583598 */
584599 bool setup_diagslog (BaseLogFile *blf);
585600
@@ -591,10 +606,10 @@ class Diags : public DebugInterface
591606 * @param[in] rs Rolling size threshold in bytes (used by size-based
592607 * policies).
593608 * @pre None.
594- * @post Rolling policy for diags.log is updated; effective on the next
595- * should_roll_diagslog() call.
609+ * @post Rolling policy fields are updated in the calling thread.
596610 * @errors None.
597- * @thread-safety Acquires tag_table_lock.
611+ * @thread-safety No lock is acquired. The caller must ensure no concurrent
612+ * calls to should_roll_diagslog() occur during reconfiguration.
598613 */
599614 void config_roll_diagslog (RollingEnabledValues re, int ri, int rs);
600615
@@ -605,9 +620,10 @@ class Diags : public DebugInterface
605620 * @param[in] ri Rolling interval in seconds.
606621 * @param[in] rs Rolling size threshold in bytes.
607622 * @pre None.
608- * @post Rolling policy for the output log is updated .
623+ * @post Rolling policy fields are updated in the calling thread .
609624 * @errors None.
610- * @thread-safety Acquires tag_table_lock.
625+ * @thread-safety No lock is acquired. The caller must ensure no concurrent
626+ * calls to should_roll_outputlog() occur during reconfiguration.
611627 */
612628 void config_roll_outputlog (RollingEnabledValues re, int ri, int rs);
613629
@@ -625,11 +641,10 @@ class Diags : public DebugInterface
625641 * FILE *).
626642 * 5. On failure: leaves the previous file in place.
627643 *
628- * @return True if diags_log was successfully reseated; false if the
629- * existing log was not initialized (the pre-init early-return path).
630- * Note: the current implementation returns true even when
631- * setup_diagslog() fails; actual failure is observable only via the
632- * internal log_log_error message written to BaseLogFile's error channel.
644+ * @return False if diags_log is null or not yet initialized (safe no-op).
645+ * True in all other cases, including when the internal reopen fails —
646+ * reopen failures are not reflected in the return value and are
647+ * observable only via the internal diagnostic trace log.
633648 * @pre A Diags instance is active and diags_log is initialized
634649 * (is_init() == true). If this precondition is not met, returns false
635650 * without performing a reopen — this is the safe no-op path for calls
@@ -638,9 +653,9 @@ class Diags : public DebugInterface
638653 * path; the previous BaseLogFile (and its FILE *) is destroyed; all
639654 * subsequent emission goes to the new file.
640655 * On failure: diags_log is unchanged; the newly allocated BaseLogFile
641- * is deleted by setup_diagslog() before it returns.
656+ * is deleted before it returns.
642657 * @errors Open failures are not directly signaled via the return value;
643- * failure is observable only via an internal log_log_error message .
658+ * failure is reported to the internal diagnostic trace log only .
644659 * @thread-safety The swap in step 4 is performed under tag_table_lock.
645660 * Concurrent emission either observes the pre-swap or post-swap log;
646661 * no message is lost or written to a destroyed FILE *.
@@ -652,42 +667,55 @@ class Diags : public DebugInterface
652667 bool reseat_diagslog ();
653668
654669 /* *
655- * @brief Test whether diags.log is due for a roll under its current policy .
670+ * @brief Roll diags.log if the current rolling policy condition is met .
656671 *
657- * @return True if the configured rolling condition is met.
672+ * @return True if the underlying file was renamed (rolled); false if no
673+ * roll condition was triggered, or if fstat failed. Note: true is
674+ * returned even when the subsequent reopen of the new log file fails.
658675 * @pre None.
659- * @post No state change; rolling is not performed.
660- * @errors None.
661- * @thread-safety Acquires tag_table_lock.
676+ * @post If the rolling condition is met: the current diags.log is flushed,
677+ * rolled (renamed), and replaced by a new BaseLogFile at the same path.
678+ * If not: no state change. fstat failure causes an early false return
679+ * without rolling.
680+ * @errors None signaled. fstat and reopen failures silently suppress the
681+ * replacement; the rolled state of the original file is not reversed.
682+ * @thread-safety tag_table_lock is acquired only during the BaseLogFile
683+ * pointer swap. The rolling-condition checks and file operations execute
684+ * without a lock; the caller must ensure no concurrent reconfiguration
685+ * (see config_roll_diagslog()).
662686 */
663687 bool should_roll_diagslog ();
664688
665689 /* *
666- * @brief Test whether the output log is due for a roll under its current
667- * policy .
690+ * @brief Roll stdout_log and stderr_log if the current rolling policy
691+ * condition is met .
668692 *
669- * @return True if the configured rolling condition is met.
670- * @pre None.
671- * @post No state change; rolling is not performed.
672- * @errors None.
673- * @thread-safety Acquires tag_table_lock.
693+ * @return True if any output log was rolled; false otherwise.
694+ * @pre stdout_log and stderr_log are non-null.
695+ * @post If the rolling condition is met: affected logs are flushed, rolled,
696+ * and replaced by new BaseLogFile instances at the same paths.
697+ * If not: no state change. fstat failure causes an early false return.
698+ * @errors None signaled. fstat failures silently suppress the roll.
699+ * @thread-safety Same as should_roll_diagslog(); see config_roll_outputlog().
674700 */
675701 bool should_roll_outputlog ();
676702
677703 /* *
678704 * @brief Reseat the named standard stream to a file at the given path.
679705 *
680706 * @param[in] stream STDOUT or STDERR (see StdStream).
681- * @param[in] file Non-null, non-empty path string. Passed verbatim to the
682- * OS open call; symlinks are re-resolved at each call. An empty string
683- * causes an immediate false return without modifying state.
684- * @return True on success; false if the file could not be opened or the
685- * resulting FILE * is null.
686- * @pre A Diags instance is active; file is non-null and non-empty.
707+ * @param[in] file Non-null path string. Passed verbatim to the OS open
708+ * call; symlinks are re-resolved at each call. An empty string causes an
709+ * immediate false return without modifying any state.
710+ * @return True on success; false if file is empty, the file could not be
711+ * opened, or the resulting FILE * is null.
712+ * @pre A Diags instance is active; file is non-null (passing null is UB —
713+ * it is passed directly to strcmp without a null guard).
687714 * @post On success: the new file is open and bound as the named stream;
688715 * the previous BaseLogFile is deleted.
689- * On failure: the previous BaseLogFile is deleted and the stream pointer
690- * is set to nullptr — the previous binding is NOT preserved.
716+ * On failure: the stream pointer is set to nullptr — the previous
717+ * binding is NOT preserved. The previous BaseLogFile is NOT freed
718+ * (leaked on the failure path).
691719 * @errors File-open failures cause a false return and internal
692720 * log_log_error messages; the named stream is left unbound (nullptr).
693721 * @thread-safety Acquires tag_table_lock internally on both success and
0 commit comments