Thursday, July 07, 2016

How to enable tftpd or TFTP service on OS X or Mac

I needed to update firmware on a TP-LINK router on a recent trip. One option is to download the firmware bundle and place it on a TFTP server accessible by the TP-link router. At the time, I had only a MacBook Pro running OS X 10.11.5 (El Capitan). 

Knowing OS X is BSD-based, I assume tftpd or tftp service is baked in. It is! Googling yielded a few articles on how to enable tftpd or tftp service on Mac or OS X. None is immediately reusable, by which, I mean to copy+paste :-)

To benefit those who wants to run TFTP service on Mac or OS X, below is a simple MOP (manual of operation) that worked for me. The instructions below assume you have a terminal open already.

  • to enable and run tftpd or tftp service. '-F' to ignore the Disabled key for the service.

sudo launchctl load -F /System/Library/LaunchDaemons/tftp.plist


  • to verify it is running properly
    • to add a test file into the /private/tftpboot/, which is the tftp service root directory.

date > /tmp/test1

sudo cp /tmp/test1 /private/tftpboot/

    • to run tftp client program to retrieve the test file
tftp localhost

get test1

quit

  • to serve any file via TFTP service now running on your Macbook, simply copy it to /private/tftpboot/
sudo cp file2 /private/tftpboot/
  • to clean-up
    • to purge files you no longer wants to serve.

sudo rm /private/tftpboot/test1

    • to unload the service configuration and stop the tftpd or tftp service

sudo launchctl unload /System/Library/LaunchDaemons/tftp.plist


The above assume you need tftpd or tftp service just to run one time or other ad-hoc purposes. If you like to keep tftpd running, you may need to load the tftp service configuration using a different option. Namely, '-w' instead of '-F', to override the Disabled key for the service.
sudo launchctl load -w /System/Library/LaunchDaemons/tftp.plist

Thursday, April 24, 2014

how to profile a python program to pinpoint performance bottlenecks

Recently, I wrote some python programs to help integrating Cisco's UCS (Unified Computing System) with our homegrown infrastructure backends.  The programs run ok, though slower than we expected.

The python programs run from a CentOS-6.5 virtual machine on my MacBook (Mavrerick) against the famous Cisco's UCS Platform Emulator (UCSPE v2.2 1b) also running as a virtual machine on the same MacBook.

To pinpoint performance bottlenecks in these python programs, I started with the standard Python profiler.
# to get a text output of cpu time stats
python -m cProfile myutil.py  > /tmp/myutil.py-cProfile.out
# to ignore calls spent less than 1s in tottime.
grep -v -P "^\s*\d+/*\d*\s+0\."  /tmp/myutil.py-cProfile.out

With the above, we immediately noted that a sigificant percentage of cpu time (tottime) was spent on FindClassIdInMoMetaIgnoreCase and String.lower(). 
         1849158 function calls (1848366 primitive calls) in 109.533 CPU seconds
   Ordered by: standard name
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   863     45.159    0.052   88.026    0.102 UcsBase.py:553(FindClassIdInMoMetaIgnoreCase)        1     2.361    2.361    2.371    2.371 urllib.py:23(<module>)
      764    8.904    0.012    8.904    0.012 {built-in method read}   11825    6.999    0.001    6.999    0.001 {method 'append' of 'list' objects}
1735232  42.783    0.000   42.783    0.000 {method 'lower' of 'str' objects}

Looking at UcsBase.py closely, I was able to locate a silly performance bug and fixed it. As you can see in the new results below, the quick fix reduced the number of calls to String.lower() to half!
    978415 function calls (977623 primitive calls) in 48.234 CPU seconds
   Ordered by: standard name
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      863   12.825    0.015   33.887    0.039 UcsBase.py:553(FindClassIdInMoMetaIgnoreCase)
      764   11.436    0.015   11.436    0.015 {built-in method read}
 873738   21.037    0.000   21.037    0.000 {method 'lower' of 'str' objects}

It still seems too many though. Trying to reduce calls to lower() everywhere in UcsUtils.py and UcsBase.py didn't reduce much. Now we need to identify which/what calls contributed to this many invocation of String.lower().

pycallgraph seems to a magnificent tool to examine a running python program to see which calls which and for how many times. Unfortunately, I couldn't get it to work on centos-6.5 (Python-2.6.6). I ended up resorting to gprof2dot
# to capture profiling results in pstat format
python -m cProfile -o /tmp/python-cProfile-SDK_orig.pstats myutil.py
# use gprof2dot & dot to graph into a svg
python ~/gprof2dot/gprof2dot.py -f pstats /tmp/python-cProfile-SDK_orig.pstats | dot -Tsvg -o  python-cProfile-SDK_orig.svg

Safari & Chrome open up the .svg pictures just fine. Isn't it a beauty! It is very nice to scale up to zoom in on these pictures. After all, .svg stands for Scalable Vector Graphics image :-)
From the pretty pictures, I can see most String.lower() calls were indeed by FindClassIdInMoMetaIgnoreCase().  Oh, well, guess I'll have to tackle calls to FindClassIdInMoMetaIgnoreCase() another day.

If there's interest, I'll follow up with a post on how to set up these python performance profiling utilities. Please let me know what you think, in the comments below.