×
Namespaces

Variants
Actions

Archived:Python on Symbian/17. Debugging Techniques

From Nokia Developer Wiki
Jump to: navigation, search

Archived.pngAquivado: Este artigo foi arquivado, pois o conteúdo não é mais considerado relevante para se criar soluções comerciais atuais. Se você achar que este artigo ainda é importante, inclua o template {{ForArchiveReview|escreva a sua justificativa}}.

All PySymbian articles have been archived. PySymbian is no longer maintained by Nokia and is not guaranteed to work on more recent Symbian devices. It is not possible to submit apps to Nokia Store.

Article Metadata
Article
Created: hamishwillee (30 Nov 2010)
Last edited: hamishwillee (08 May 2013)

Original Author: Marcelo Barros

This chapter demonstrates the main debugging techniques for Python on the Symbian platform and gives a brief overview of tools and programming practices that can help you write robust Python code.

Contents

Introduction

Defects can be introduced into software at any point in the software development lifecycle, and may be detected in any of these stages or later during unit testing and system testing or, in the worst case, by the end-user. There is plenty of evidence to show that defects are orders of magnitude easier and cheaper to fix early in the design cycle. There is also a lot of evidence to show that good programming and testing practices can eliminate and detect defects.

The first section of this chapter provides useful links to some of the techniques and tools that can help you write better code to avoid defects. The rest of the chapter demonstrates the techniques you use for debugging Python code on the Symbian platform. Unfortunately, Python on Symbian does not provide an interactive source code debugger; while you can use an IDE for your development if you choose (including the Python IDLE IDE) you can't step through your running code or debug on a target Symbian device. Instead, most of the techniques described use logging to report the state of the application as the code runs. The techniques are flexible and can be used to debug simple scripts or more complex applications.

Writing better code

The following development methodologies and tools can help improve the quality of your code.

  • Release early and often. A frequent release cycle keeps your early-adopters and testers interested, and having more eyes on the code improves the chances of defects being detected. The idea of Release Early, Release Often comes from the book The Cathedral and the Bazaar by Eric S. Raymond.
  • Perform code inspection by another programmer, which is an important tool for detecting defects and ensuring that code is maintainable.
    • Pair programming is an agile methodology that takes the idea of code review further, and combines code generation with review. Free tools, such as Google Code, support code inspection and, even if your project does not have budget for an independent code review or pair programming, you still gain value by inspecting your own code with a critical eye.
  • Test your code thoroughly. In particular, it is good programming practice to divide your code into modules and unit test each individually. test-driven development is a methodology that deserves some attention.
  • Document your code. It is important to document your code as you write it, and maintain the documentation along with any code changes. Good documentation makes it easier for others to work on your code, and to understand it when fixing defects. Python makes it very easy to write code and documentation together.
  • Use version control tools. Source Code Management (SCM)) allows you to keep a record of specific changes and to undo mistakes if necessary.
    • Bzr (http://bazaar.canonical.com/en/) and Mercurial (http://mercurial.selenic.com) are excellent options for small projects.
    • CVS, Subversion and Git are more difficult to learn, but are helpful if you want to host your project on open source repositories like Sourceforge, Savanna, Maemo or Google Code.
  • Use a bug tracker. For a very small project you can track defects using a spreadsheet. However, if you have a larger project, or you need to communicate your defects outside your immediate project team, a bug tracking tool is essential.

Debugging tools and techniques

Python shell

During the development phase it is common to execute all PySymbian applications from the Python shell. The most basic debug technique is to run the code within the Python shell application and use the print command to log application state to the console, as follows:

# instructions here
print "I am here"
# more instructions
print "now I am here"
# ...

You may also want to have an additional DEBUG flag to switch between the debug mode and normal execution mode. For example:

# script starts
import modules
 
# Debug
DEBUG = True
 
# instructions here
def myfunction():
if DEBUG:
print u"In myfunction()" # debug
# more instructions
 
# more instructions

To execute your script from the Python shell, select the menu option Run script (note that you will need to copy it to e:\Python or c:\data\Python first).

This technique is not particularly useful for debugging programs with a user interface because the console (and log messages) is usually hidden by the running application. Provided that the script does not call app.set_exit() to close the script shell, the messages will be available when the script finishes.

Python shell over Bluetooth

The Python shell can be run over a Bluetooth connection. This approach allows you to drive execution of your application from your computer, and ensure that log messages are visible at the point of execution. More information can be found atindex.php/Nokia Open Sourcebut, in summary, you need to:

  1. Configure a COM port on your PC.
  2. Use an application like Hyperterminal or Putty to connect to this port.
  3. In your phone, open the Python shell and select Bluetooth console from menu. Then select the Bluetooth name of your computer.

Python shell over WiFi

The Python shell can also be run over WiFi connections using the Netcat utility (http://netcat.sourceforge.net). If you are running Unix, Linux or Mac OS X, type the following lines in a console:

# Running a TCP server on port 1025
stty raw -echo ; nc -l -p 1025 ; stty sane

There is a Netcat version for Windows but you can have some problems with control characters since stty is not available. In this case, use only:

nc -l -p 1025

Launch the following script on your phone. Do not forget to set the proper IP of your computer (we are using 10.0.0.10). You can improve it with calls to appuifw.query(), allowing users to select IP and port.

import btconsole
from socket import *
 
sock = socket(AF_INET,SOCK_STREAM)
# put the proper IP and port here
sock.connect(("10.0.0.10",1025))
btconsole.run_with_redirected_io(sock,btconsole.interact, None, None, locals())

Audio logging

An alternative method to logging with printed messages, is to use "speak" the messages using the audio module's text to speech converter:

import audio
# instructions here
audio.say(u"I am here")
# more instructions
audio.say(u"now I am here")
# ...

The text to speech functionality is discussed in Chapter 7.

Logging view

An alternative to logging to the shell console is to log to a Text object. Having a dedicated logging view allows us to see the logs for a running UI application, which means that we don't need to run the script in a shell environment in order to debug it.

from appuifw import *
 
logview = Text()
app.body = logview
 
def add_msg(msg):
global logview
# u"\u2029" is the new line code for Text()
logview.add(unicode(msg) + u"\u2029")
# put the cursor at the end
logview.set_pos(logview.len())
 
add_msg(u"Starting...")
# some code here
 
add_msg(u"Now running at this point ...")
# more code ...

The previous fragment logs to a Text object that is the current app.body. In a user interface application we would probably have the logview as a separate view and switch to it when necessary. We could even include this code in the production version of the application, hidden from the user until it is needed to diagnose a problem.

Log files

Some code is harder to debug, either by its very nature (e.g. multi-threaded programs) or because they close unexpectedly with no chance for exception handling. In such cases, log files can provide essential 'post-mortem' debugging information.

A log class is suggested in the next code snippet (just save it to a file called logfile.py):

# logfile.py 
#
import sys
import time
import thread
import os
 
__all__ = [ "FLOG" ]
 
log_path = "C:\\Logs\\"
 
class FileLog(object):
def __init__(self,filename):
if (os.path.exists(log_path)):
# Log enabled
self.log = True
self.filename = log_path+filename
print filename
self.file = open(self.filename,'at')
self.lock = thread.allocate_lock()
else:
appuifw.note(u' Log diabled')
# Log diabled
self.log = False
 
def add(self,msg):
if (self.log):
# collect caller information from stack call using _getframe
caller = sys._getframe(1).f_code.co_name
line = str(sys._getframe(1).f_code.co_firstlineno)
# create log message
timestamp = time.strftime("[%Y%m%d %H:%M:%S] ",time.localtime())
logmsg = timestamp + caller + ":" + line + " - " + msg + " \n"
# write log message, using a semaphore for controlling the file access
self.lock.acquire()
self.file.write(logmsg)
self.file.flush()
self.lock.release()
else:
pass
 
def close(self):
if (self.log):
self.file.close()
else:
pass
 
FLOG = FileLog("filelog.txt")

The logfile module is written in such a manner that existence of the path C:\Logs serves as a flag for enabling or disabling the logging. You may, of course, change this path according to your needs.

To use the logfile module, simply import the object FLOG and log messages with FLOG.add(msg):

import sys
# Append the path where you placed 'logfile.py'
sys.path.append(u"C:\\data\\python")
 
# import FLOG from the logfile module
from logfile import FLOG
 
# You can log to the file by using the 'add' function
FLOG.add("Log initialized")
 
# Instructions and your code go here
FLOG.add("Debug 1")
 
# More instructions and code
FLOG.add("Debug 2")
 
# Closing the log file
FLOG.close()

Caller information is automatically added to the log using the object sys._getframe, and a semaphore (created using thread.allocate_lock) is provided to avoid re-entrancy problems when writing to the file. After writing, the file is flushed and any pending bytes are written, so you can trust that a message is written to the file after the call to FLOG.add().

You may close the file by calling FLOG.close() when you have finished logging to the file.

Runtime error detection

Using the script shell is practical for debugging during development, but is not suitable for detecting runtime errors in standalone applications (which can be created as described in Chapter 16). It is not uncommon when first deploying a program as a standalone application to have errors related to missing modules, or use of the wrong paths in sys.path, and so on. In these situations, we use exception handling to detect and log runtime errors; we can then provide a comprehensive debug message to the user, and even notify ourselves of the problem automatically.

Tracebacks can be used extensively to extract, format and print stack traces of Python scripts. For example, the following code would print the traceback.

import traceback, appuifw, sys
 
def dangerous_function():
appuifw.non_existing_function()
 
try:
# something that would throw an exception
dangerous_function()
except:
# catch exception
print "Exception in user code:"
print '-'*30
traceback.print_exc()
print '-'*30

Different ways to print and format the stack traces can be found in the Python documentation library (http://docs.python.org/library/traceback.html).

Using a traceback module, the following code example shows how we can collect mobile device and exception data after a failure, and then create a new user interface to inform the user of how to report the problem:

try:
# Put you startup code here.
# For instance, import your module and run it.
# import my_module
# my_module.startup()
pass
except Exception, e:
# Oops, something wrong. Report problems to user
# and ask him/her to send them to you.
import appuifw
import traceback
import sys
import e32
 
# Collecting call stack info
e1,e2,e3 = sys.exc_info()
call_stack = unicode(traceback.format_exception(e1,e2,e3))
 
# Creating a friendly user message with exception details
new_line = u"\u2029"
err_msg = u"This programs was unexpectedly closed due to the following error: "
err_msg += unicode(repr(e)) + new_line
err_msg += u"Please, copy and past the text presented here and "
err_msg += u"send it to email@server.com. "
err_msg += u"Thanks in advance and sorry for this inconvenience." + new_line*2
err_msg += u"Call stack:" + new_line + call_stack
 
# Small PySymbian application
lock = e32.Ao_lock()
appuifw.app.body = appuifw.Text(err_msg)
appuifw.app.body.set_pos(0)
appuifw.app.menu = [(u"Exit", lambda: lock.signal())]
appuifw.app.title = u"Error log"
lock.wait()

For instance, suppose your main program is just the following (invalid) code snippet:

#...
try:
import camera
camera.take_photo(camera.RGB32) # error: invalid mode
except Exception, e:
#...

A screenshot of the error message that would be presented is shown in Figure 17.1:

Figure 17.1:Message displayed to user in the event of an error.

Debugging defects on end-user phones

However careful you are in your software development process, it is still possible (even likely) that defects will escape into production code. Furthermore, with so many Symbian devices running in so many different locales, it is possible that issues will occur on devices you can't get access to. Getting your application's runtime debug information from a customer's mobile device is likely to be more useful for debugging than any amount of description they can provide, and can save a lot of time because they've already reproduced the problem for you!

The previous section showed how we might catch an exception and display a message asking the user to email it to us. The following goes one step further, modifying the previous code fragment to demonstrate how you might SMS a debug message to yourself.

try:
# Put you startup code here.
# For instance, import your module and run it.
# import my_module
# my_module.startup()
import camera
camera.take_photo(camera.RGB32) # error: invalid mode
except Exception, e:
# Oops, something wrong. Report problems to user
# and ask him/her to send them to you.
import appuifw
import traceback
import sys
import e32
 
# Collecting call stack info
e1,e2,e3 = sys.exc_info()
call_stack = unicode(traceback.format_exception(e1,e2,e3))
 
# Creating a friendly user message with exception details
new_line = u"\u2029"
#Replace MyProgram with your app's name
err_msg = u"MyProgram was unexpectedly closed due to the following error: "
err_msg += unicode(repr(e)) + new_line
err_msg += u"Please, copy and past the text presented here and "
err_msg += u"send it to email@server.com. "
err_msg += u"Thanks in advance and sorry for this inconvenience." + new_line*2
err_msg += u"Call stack:" + new_line + call_stack
 
def send_sms():
import messaging
msg = appuifw.app.body.get()
messaging.sms_send("+5516xxxxxxxx",msg) # put your phone here
appuifw.note(u"A message was created and sent. Thanks a lot.","info")
 
def save_screenshot():
import graphics
ss = graphics.screenshot()
ss.save(u"e:\\screenshot.png")
appuifw.note(u"Screenshot saved in e:\\ ","info")
 
# Small PySymbian application
lock = e32.Ao_lock()
appuifw.app.body = appuifw.Text(err_msg)
appuifw.app.body.set_pos(0)
appuifw.app.menu = [(u"Send report via SMS",send_sms),
(u"Save screenshot",save_screenshot),
(u"Exit", lambda: lock.signal())]
appuifw.app.title = u"Error log"
lock.wait()

Figure 17.2 shows the screenshot received:

Figure 17.2: Screenshot of a debug SMS sent by an application

SMS is just one alternative to sending a debug message. Another approach might be to send the debug message using MMS, or using an Internet connection. The techniques in Chapter 14 could even be used to post defect information directly into your web-based defect management system.

Working on Windows using an emulator

Normally, you'll test your applications on a real Symbian device, either in the Python shell or as a stand-alone application. However, if you don't have a device, you can instead test your scripts within the Python shell console on an emulator (a simulation of the Symbian platform running on top of the Windows operating system). Instructions on how to set up the emulator for Python development are given in Chapter 1.

When the emulator is running, locate and run your Python script shell. You can debug it, as shown in Figure 17.3.

Figure 17.3: Python interactive console

If you want to debug a script, copy it into the following directory and select Run script from the Python shell menu.

<SDK Installation directory>\epoc32\winscw\c\Data\python

The other debugging techniques explained in the chapter can all be used when debugging on the emulator.

Summary

This chapter covered several techniques for debugging scripts and applications, including printing through the script shell, using a logging view, implementing robust file logging and using a crash log. The techniques described can be used for debugging both simple and complex scripts, and can help you debug and fix problems quickly and efficiently.


Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. Portions copyright Bogdan Galiceanu, Hamish Willee, Marcelo Barros de Almeida, Mike Jipping, Pankaj Nathani and others in wiki document history list. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 8 May 2013, at 08:49.
119 page views in the last 30 days.