Chapter 195. Debug and Test
Table of Contents
195.1. Debugging
195.1.1. Asserts
If the target platform resources allow, the first step in debugging
should be to enable ASSERT
s. The inclusion of
assert checking will increase the code footprint and lower the
performance, but does allow the code to catch internal errors from
unexpected data values. e.g. when the application/client is not able
to guarantee the validity of data passed into the mDNS Responder.
The mDNS Responder asserts are controlled via the standard eCos
Infrastructure CYGPKG_INFRA
package CYGDBG_USE_ASSERTS
option. If enabled, then
run-time assertion checks are performed by the mDNS Responder.
If assertions are enabled, and a debugger is being used it is normally
worthwhile setting a breakpoint on
the cyg_assert_fail
symbol so that the debugger
will stop prior to entering the default busy-loop processing.
195.1.2. Diagnostic Output
In conjunction with the CYGDBG_MDNS_DEBUG
CDL
configuration setting, the
header-file src/mdns_debug.h
implements the mDNS specific debug control.
When CYGDBG_MDNS_DEBUG
is enabled a set of
individually selectable sub-systems are available to control the
diagnostic output generated.
However, when developing or debugging the mDNS Responder
implementation, it may be simpler (with fewer build side-effects) to
control the debugging output via uncommenting the necessary manifests
at the head of
the src/mdns_debug.h
source file
than re-configuring the complete eCos configuration via the CDL. That
way only the mDNS package will be re-built.
Note | |
---|---|
Some diagnostic output, if enabled, may adversely affect the operation of the mDNS Responder as seen by 3rd-party code. For example, “slow” serial diagnostic output of the packet parsing and response generation could mean that a significant amount of time passes, such that the mDNS Responder no longer adheres to the timings as specified by the mDNS/DNS-SD standards. |
195.2. Testing
If the configuration option CYGPKG_NET_MDNS_TESTS
is enabled then a set of simple tests are built.
Note | |
---|---|
If the target platform has limited memory and is unable to execute the tests then the eCos synthetic Linux target can be used to execute tests and verify the behaviour of the mDNS implementation when debugging, assertions or large test executables are required. |
195.2.1. mdns_example
The mdns_example provides a very basic HTTP daemon, with enough functionality to pass the Bonjour Conformance Test (see Section 195.2.5, “Bonjour Conformance Test”).
On startup the application initialises a HTTP daemon listening on port
80, and registers and enables the mDNS announcing that
service. A HTTP GET
request for the default
root page will return an HTML page listing the sub-pages provided. The
application is manually terminated by requesting
the exit
page. The config
page
has specific support for the features needed for the BCT.
The mdns_example is, however, a simple example of announcing a DNS-SD service, as well as a simple lwIP HTTP daemon demonstration.
195.2.2. dnssd_example
The dnssd_example application demonstrates the use of the DNS-SD API to discover network services (see Section 195.2.6, “DNS-SD Example” for more detail). The application will enumerate local services being announced to the network, before performing specific queries for “_http” and “_ipp” services to ascertain server and TXT information.
195.2.3. mdns_testp
The mdns_testp uses dummy network
interfaces to monitor the responses generated by the mDNS Responder in
a known clean environment. The physical network connection is not used
after the startup initialisation. The test application controls the
packets injected into the networking stack and provides its own
artificial driver
layer for packet transmission. This
allows for specific mDNS Responder features to be tested without the
noise and interference of a real network setup, and without specifying
a stand-alone, closed, network like the 3rd-party Bonjour conformance
testing.
195.2.4. mdns_farm
The mdns_farm test is primarily for use in
the eCosCentric® automated
testfarm. It can however be manually executed by starting the
application, and then manually executing the
simple bash test
script misc/mdnstest1.sh
on a suitable host,
passing the network address of the target executing
the mdns_farm executable. For example:
$ ./misc/mdnstest1.sh 192.168.7.165
Note | |
---|---|
The host used to execute the script should have a suitable avahi configuration. |
195.2.5. Bonjour Conformance Test
If the configuration
option CYGPKG_NET_MDNS_TESTS_STANDALONE
is enabled
then the mdns_example is built. This implements a
simple mDNS application providing, by default, a limited functionality
HTTP daemon on port 80. This test program, when used with a suitable
eCos system configuration, can be used for execution against the
Bonjour Conformance Test (BCT).
Note | |
---|---|
For the full BCT test, which includes link-local address verification,
the eCos configuration should have the lwIP IPv4 AutoIP
( |
For the BCT to function, a very specific closed network setup is required. For the mDNS Responder package testing the following equipment was used:
Unit-Under-Test (UUT)
The target platform being tested, executing a suitable eCosPro configuration.
MacBookPro (OS: 10.8.2)
Executing the BonjourConformanceTest v1.2.8 in a Terminal shell window, and the Safari v6.0.1 web-browser.
Airport Extreme (HW: A1408 FW: 7.6.1)
10base-T (wired) Ethernet connections to the UUT and the MacBookPro.
The HTTP daemon provided by the mdns_example
application provides support for the necessary BCT interaction using
specific CGI parameters to the /config
page.
The BCT initially tests the IPv4 link-local address support before starting the mDNS Responder interaction.
The following example URLs are based on the BCT v1.2.8 interaction with a default mDNS configuration. If the application being tested uses a different starting hostname, or later BCT executables change the processing, then the target address will need to be modified to suit the state reached at the specific point in the BCT sequence.
For the MANUAL NAME CHANGE
component the
unit-under-test (UUT) needs to be supplied with a specific service
label. The config?service=
option allows a new
service-label to be specified for the registered HTTP daemon.
ecospro-21.local./config?service=New - Bonjour Service Name
Note | |
---|---|
The mdns_example provides the HTTP daemon
as the first registered service, and the
simple |
For the Mixed-Network Interoperability
Test
ROUTABLE TO LINK-LOCAL COMMUNICATION
component the UUT needs to be configured with a routable IP
address. The config?ip=
option allows an IPv4
address to be specified. This will re-configure the default network
interface to the specified IPv4 address with an explicit netmask
of 255.255.255.0
. For example:
ecospro-42.local./config?ip=17.1.1.1
For the final CHATTINESS
component of the BCT the
UUT needs to be re-configured with a link-local
address. The config?ll=
option allows an IPv4
link-local address to be supplied, explicitly setting the
netmask 255.255.0.0
. For example:
ecospro-42.local./config?ll=169.254.1.0
Note | |
---|---|
For the final |
When the BCT execution finishes it prompts the operator regarding the
generation of an execution report. When applying for Bonjour
Conformance the report should be created, and in conjunction with the
generated debug.log
file, sent to Apple for
validation as documented in their procedures.
The results for an actual BCT run using
the mdns_example
, executing on a STM32F207xx
(Cortex-M3) based platform, are available in
the Example 195.1, “
doc/bct_stm32f207_result.txt
”
and doc/bct_stm32f207_debug.log
files. These
files are original, as produced by the BCT application. For reference,
the doc/bct_stm32f207_terminal.txt
file contains the
execution output captured from the MacOS Terminal.
The following is a listing of
the doc/bct_result.txt
file as provided in the
package.
Note | |
---|---|
The BCT actually finished its testing at 18:05 on Tue Nov 20th, but
the |
Example 195.1.
doc/bct_stm32f207_result.txt
Bonjour Conformance Test Version 1.2.8 Started Tue Nov 20 12:57:22 2012 Completed Wed Nov 21 09:00:08 2012 Link-Local Address Allocation ----------------------------- PASSED: INITIAL PROBING PASSED: PROBING: RATE LIMITING PASSED: PROBING: CONFLICTING SIMULTANEOUS PROBES PASSED: PROBING: PROBE DENIALS PASSED: PROBING COMPLETION WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT WARNING: SUBSEQUENT CONFLICTS: RE-PROBE AFTER FIRST CONFLICT PASSED: SUBSEQUENT CONFLICTS PASSED: HOT-PLUG: USE OF PREVIOUS ADDRESS AS FIRST PROBE CANDIDATE PASSED: CABLE CHANGE HANDLING PASSED: PREMATURE MDNS PROBING PASSED with 9 warning(s). Multicast DNS ----------------------------- PASSED: INITIAL PROBING PASSED: PROBING: SIMULTANEOUS PROBE CONFLICT PASSED: PROBING: RATE LIMITING PASSED: PROBING: PROBE DENIALS PASSED: WINNING SIMULTANEOUS PROBES - ANNOUNCEMENTS PASSED: WINNING SIMULTANEOUS PROBES: WINNING SIMULTANEOUS PROBES PASSED: SRV PROBING/ANNOUNCEMENTS PASSED: SUBSEQUENT CONFLICT - ANNOUNCEMENTS PASSED: SUBSEQUENT CONFLICT - A PASSED: SUBSEQUENT CONFLICT - ANNOUNCEMENTS PASSED: SUBSEQUENT CONFLICT - SRV PASSED: SIMPLE REPLY VERIFICATION PASSED: SHARED REPLY TIMING - UNIFORM RANDOM REPLY TIME DISTRIBUTION PASSED: SHARED REPLY TIMING PASSED: MULTIPLE QUESTIONS - SHARED REPLY TIMING - UNIFORM RANDOM REPLY TIME DISTRIBUTION PASSED: MULTIPLE QUESTIONS - SHARED REPLY TIMING PASSED: REPLY AGGREGATION PASSED: MANUAL NAME CHANGE - ANNOUNCEMENTS PASSED: HOT-PLUGGING: INITIAL PROBING PASSED: HOT-PLUGGING: PROBING: SIMULTANEOUS PROBE CONFLICT PASSED: HOT-PLUGGING: PROBING: RATE LIMITING PASSED: HOT-PLUGGING: PROBING: PROBE DENIALS PASSED: HOT-PLUGGING: WINNING SIMULTANEOUS PROBES - ANNOUNCEMENTS PASSED: HOT-PLUGGING: WINNING SIMULTANEOUS PROBES: WINNING SIMULTANEOUS PROBES PASSED: HOT-PLUGGING: SRV PROBING/ANNOUNCEMENTS PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - ANNOUNCEMENTS PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - A PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - ANNOUNCEMENTS PASSED: HOT-PLUGGING: SUBSEQUENT CONFLICT - SRV PASSED: HOT-PLUGGING PASSED: NO DUPLICATE RECORDS IN PACKETS PASSED: REQUIRED ADDITIONAL RECORDS IN ANSWERS PASSED: LEGAL CHARACTERS IN ADDRESS RECORD NAMES PASSED: CACHE FLUSH BIT SET IN NON-SHARED RESPONSES PASSED: CACHE FLUSH BIT NOT SET IN PROPOSED ANSWER OF PROBES PASSED with 0 warning(s). Mixed-Network Interoperability ----------------------------- PASSED: LINK-LOCAL TO ROUTABLE COMMUNICATION PASSED: ROUTABLE TO LINK-LOCAL COMMUNICATION PASSED: CACHE FLUSH BIT NOT SET IN UNICAST RESPONSE PASSED: UNICAST INTEROPERABILITY PASSED: CHATTINESS PASSED: mDNS IP TTL CHECK PASSED: DUPLICATE RECORDS CHECK PASSED: ADDITIONAL RECORDS IN ANSWER CHECK PASSED with 0 warning(s). ****************************************************************************** CONGRATULATIONS: You successfully passed the Bonjour Conformance test ******************************************************************************
195.2.6. DNS-SD Example
If required the dnssd_example application can be used as a starting point for a real-world application. The test starts a thread which performs some DNS-SD operations, as likely to be needed by an application that needs to search for mDNS announced network services. The test is contained within the function:
void thread_dnssd(
cyg_addrword_t arg)
;
Since the callback function (as described in cyg_mdns_discovery_callback_register()) must NEVER block, this example uses the eCos mailbox and fixed-size-memory-allocator functionality as the mechanism to communicate between the callback (which was registered with the mDNS world with the aforementioned function) and the application code. It uses the non-blocking functionality of these core eCos features to ensure the callback will not block.
The core of the foreground functionality is performed in the function:
void example_browse(
const char *tag, const cyg_uint8 * const *services, cyg_uint8 cflags, cyg_uint32 waitsecs, dump_hostport_callback_fn cb_hostport)
;
PTR
service browse operation, and
then waits for callback events to the function registered against the
specific services
name. As documented, a query
can result in multiple registered callbacks being called since the
queries are common, and not specific to a
registered services
name. The waitsecs
just specifies how long (in
seconds) the example code will wait for responses before
returning. The code currently waits a relatively long time after
issuing the single query, whereas a real-world application would more
likely have its own control loop to re-issue periodic queries if
needed; and to maintain whatever data structures for discovered
services it needs to track. However, whilst the callback is active, any
matching mDNS response will be passed through the callback,
irrespective of whether that response is the result of a query
generated by this “host”, or another network
device. Another example in the function:
service_table_entry *example_query(
const cyg_uint8 * const *query, cyg_uint16 type, callback_context *ctx)
;
The example callback is provided by
the cb_service()
function, and is coded to
support both the basic service browsing, but also the individual
specific queries. Simpler applications could implement a simpler
function just responding to the information they need. Fundamentally
the callback implements two phases: Processing records, and marking
the end of a “set” of responses:
void cb_service(void *priv, cyg_uint32 state, const cyg_mdns_resource *phrr, struct pbuf *p, u16_t offname, u16_t offdata) { if (state & MDNS_DISCOVERY_CB_NOMORE) { // No record for this callback /* If required notify foreground. This could trigger a U/I update with recently acquired data, or just be the point at which captured data is passed through. */ } else { // Process record switch (phrr->type) { … } } return; }
A fundamental task of the callback is to extract the encoded
information from the referenced lwIP packet buffer. The following
example fragment shows how the referenced response name can be copied
into a local buffer. This example
uses raw
=true
so that the
result is an uncompressed version in the mDNS encoded string format
(length byte followed by the field data, with the set of fields
terminated by a 0x00
byte indicating a
zero-length field):
cyg_uint16 nlen = cyg_mdns_strlen(p,offname,true); cyg_uint8 name[nlen]; u16_t endindex; endindex = cyg_mdns_name_uncompress((char *)name,sizeof(name),p,offname,true); CYG_ASSERT((endindex <= offdata),"Name length mismatch");
Note | |
---|---|
The callback should NEVER modify the passed lwIP packet buffer. A mDNS response may contain multiple records that are relevant to more than one callback as well as to the internal mDNS Responder operation. Also mDNS responses may be compressed, where data is shared between records. This is why a DNS-SD query client that needs to preserve information needs to make its own uncompressed copies without changing the referenced mDNS response data. |
Back at the application foreground level, the first substantial
operation performed within thread_dnssd()
is to
use the specific DNS-SD “wildcard” query to ascertain what
service types are being announced to the network. This uses
the cyg_mdns_name_services_dnssd_udp_local
variable
(built-in to the mDNS package) to reference the standard
“_services._dns-sd._udp.local” name. The function outputs
each service type discovered, for example:
INFO:<Browsing for “Local services” (wait 4-seconds)> Service: _http._tcp.local Service: _pdl-datastream._tcp.local Service: _ipp._tcp.local Service: _printer._tcp.local Service: _udisks-ssh._tcp.local Service: _ssh._tcp.local Service: _sftp-ssh._tcp.local Service: _workstation._tcp.local Service: _http-alt._tcp.local Service: _scanner._tcp.local PASS:<Service Browse>
The example then performs three passes each against the “_http” and “_ipp” local service names, with each pass allowing more records to be passed to the registered callback for processing.
The first pass uses the default control flags, which will pass only the explicit matching ANSWER records for any active PTR answers. The application will output a line for each specific service type that provides a response:
INFO:<Browsing for “_http” (wait 4-seconds)> Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local Service: Xerox Phaser 6280DN (ac:a8:0d)._http._tcp.local PASS:<Service Browse>
The second pass explicitly adds
the MDNS_DISCOVERY_ADDITIONAL
flag to have the
callback supplied with matching ADDITIONAL records, and not just the
matching ANSWER record. For example, this now results in the server
name, port and TXT records being recorded if they are supplied:
INFO:<Browsing for “_http” ADDITIONAL (wait 4-seconds)> INFO:<Callback flags now 03> Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local http://hp8500.local:80/ TXT-0 contains 0 entries Service: Xerox Phaser 6280DN (ac:a8:0d)._http._tcp.local http://XRX0000AAACA80D.local:80/ TXT-0 contains 9 entries [0]=“qtotal=5” [1]=“ty=Xerox Phaser 6280DN” [2]=“product=(Phaser 6280DN)” [3]=“pdl=application/postscript” [4]=“adminurl=http://.local./” [5]=“usb_MFG=Xerox” [6]=“usb_MDL=Xerox Phaser 6280DN” [7]=“Binary=T” [8]=“TBCP=T” PASS:<Service Browse>
The third pass adds the MDNS_DISCOVERY_ALL
flag
requesting that the callback is supplied all ADDITIONAL records in the
response if an ANSWER was previously passed for the response being
processed. This can be used to allow the callback to gather network
address information (from A
or AAAA
records as appropriate) so that the
callback can supply complete service information in a single
result. As can be seen from the following example output, the IPv4 and
IPv6 addresses are now provided in the result passed through the
mailbox from the callback processing to the foreground client code:
INFO:<Browsing for “_http” ALL ADDITIONAL (wait 4-seconds)> INFO:<Callback flags now 07> Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local http://hp8500.local:80/ IPv4 192.168.7.6 IPv6 2001:4D48:AD00:5A17:6AB5:99FF:FED0:220F IPv6 FE80::6AB5:99FF:FED0:220F TXT-0 contains 0 entries Service: Xerox Phaser 6280DN (ac:a8:0d)._http._tcp.local http://XRX0000AAACA80D.local:80/ IPv4 192.168.7.25 TXT-0 contains 9 entries [0]=“qtotal=5” [1]=“ty=Xerox Phaser 6280DN” [2]=“product=(Phaser 6280DN)” [3]=“pdl=application/postscript” [4]=“adminurl=http://.local./” [5]=“usb_MFG=Xerox” [6]=“usb_MDL=Xerox Phaser 6280DN” [7]=“Binary=T” [8]=“TBCP=T” PASS:<Service Browse>
As mentioned, the example will then perform the same queries for the “_ipp” service name outputting suitable results for the discovered service (if any are actually present on the connected network).
Finally the example performs some explicit queries as would be
performed by a simple application that needs to enumerate a
(previously discovered, or hardwired) service. The code uses the first
valid “_http” service response, and directs specific
queries to this service name (unlike the
“wildcard” PTR
queries used to discover
services). It uses the example_query()
to request
the SRV
, and then
the A
/AAAA
address records as
appropriate. The output would be similar to the following.
INFO:<Request specific records> Instance: Officejet Pro 8500 A910 [D0220F] Service: Officejet Pro 8500 A910 [D0220F]._http._tcp.local Host: hp8500.local (Port 80) IPv6 2001:4D48:AD00:5A17:6AB5:99FF:FED0:220F IPv6 FE80::6AB5:99FF:FED0:220F IPv4 192.168.7.6
2024-12-10 | eCosPro Non-Commercial Public License |