Friday, October 12, 2012

Testing Raspberry Pi network speed

Just got another Rapsberry Pi board. This time a 2.0 board (made in the UK!!). Anyway, doing a network (or USB, since the ethernet is via USB) stress test with the following command:

cat /dev/zero | ssh username@remotehost "cat > /dev/null"


That caused 100% CPU loading on the Raspberry Pi. Throughput is about 6MB per second.

Monday, October 1, 2012

Using PMD to scan explicit/implicit casting of BigDecimal to String

At work, a project needed to be upgraded from Java 1.4 to Java 1.6. One of the big issues introduce in Java 1.5 was the behavior change of the toString method of BigDecimal. Under certain situation, it now returns the string in using scientific notation. This change was based on JSR 13. While the change is justifiable, some question if the expert group underestimated the behavioral compatibility impact, especially with programs that interface with databases.

Anyway, one way (the way we used at work) to solve the issue is to use the new method toPlainString. We grep the code to find all the toString method calls and manually change them to toPlainString. But not only this is prone to error, there are many cases that can't be detected. For example, this code doesn't call the toString method explicitly but still affected by the compatibility issue :

String s = "" + bd;


There ought to be a better (and automated) way to scan the source tree. So during a long weekend, I spent a day to play with PMD. Below is a rule that I implemented to do the job. Part of it is based on a sample found in the PMD package.But note that this still CANNOT find all the cases of BigDecimal.toString (more about that later).

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.List;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTType;
import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
import net.sourceforge.pmd.util.CollectionUtil;


/**
 *
 */
public class ScanBigDecimal extends AbstractJavaRule {

    /**
     * These are the BigDecimal methods
     */
    private static final Set<String> BIG_DECIMAL_METHODS = CollectionUtil.asSet(new String[] { ".toString" });

    /**
     * These are the classes that the rule can apply to
     */
    private static final Map<String, Set<String>> MAP_CLASSES = new HashMap<String, Set<String>>();
    static {
        MAP_CLASSES.put("java.math.BigDecimal", BIG_DECIMAL_METHODS);
        MAP_CLASSES.put("BigDecimal", BIG_DECIMAL_METHODS);
    }

    @Override
    public Object visit(ASTLocalVariableDeclaration node, Object data) {

        ASTVariableDeclaratorId var = getDeclaration(node);
        if (var == null) {
            return super.visit(node, data);
        }
        String variableName = var.getImage();
        for (NameOccurrence no: var.getUsages()) {
            // FIXME - getUsages will return everything with the same name as the variable,
            // see JUnit test, case 6. Changing to Node below, revisit when getUsages is fixed
            Node sn = no.getLocation();
            Node primaryExpression = sn.jjtGetParent().jjtGetParent();
            Class<? extends Node> parentClass = primaryExpression.jjtGetParent().getClass();

            // see if we are calling the methods we are interested in
            String methodCall = sn.getImage().substring(variableName.length());
            ASTType nodeType = node.getTypeNode();
            if (nodeType != null && MAP_CLASSES.get(nodeType.getTypeImage()).contains(methodCall)) {
                addViolation(data, sn);
            }
        }
        return super.visit(node, data);
    }

    @Override
    public Object visit(ASTAdditiveExpression node, Object data) {

        boolean hasLiteral = true;
        boolean hasClass = false;

        if (node == null) {
            return super.visit(node, data);
        }

        for (int i = 0; i < node.jjtGetNumChildren(); i++) {
            Node child = node.jjtGetChild(i);

            if (child.hasDescendantOfType(ASTLiteral.class)) {
                hasLiteral = true;
            } else {
                ASTName nameNode = (ASTName)child.getFirstDescendantOfType(ASTName.class);

                if (nameNode != null && findDeclaration(node, nameNode.getImage()) != null) {
                    hasClass = true;
                }
            }

            if (hasLiteral && hasClass) {
                addViolation(data, node);
                break;
            }
        }

        return super.visit(node, data);
    }


    /**
     * This method checks the variable declaration if it is on a class we care
     * about. If it is, it returns the DeclaratorId
     *
     * @param node
     *            The ASTLocalVariableDeclaration which is a problem
     * @return ASTVariableDeclaratorId
     */
    private ASTVariableDeclaratorId getDeclaration(ASTLocalVariableDeclaration node) {
        ASTType type = node.getTypeNode();
        if (MAP_CLASSES.keySet().contains(type.getTypeImage())) {
            return node.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
        }
        return null;
    }

    private ASTVariableDeclaratorId findDeclaration(Node node, String varName) {
        if (node == null || varName == null) {
            return null;
        }

        Node parent = node.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class);
        if (parent != null) {
            List<ASTVariableDeclaratorId> list = parent.findDescendantsOfType(ASTVariableDeclaratorId.class);

            for (ASTVariableDeclaratorId declarator : list) {
                if (varName.equals(declarator.getImage())) {
                    ASTType nodeType = null;
                    if (declarator.jjtGetParent() instanceof ASTFormalParameter) {
                        nodeType = ((ASTFormalParameter)(declarator.jjtGetParent())).getTypeNode();
                    } else if (declarator.jjtGetParent() instanceof ASTLocalVariableDeclaration) {
                        nodeType = ((ASTLocalVariableDeclaration)(declarator.jjtGetParent())).getTypeNode();
                    } else if (declarator.jjtGetParent() instanceof ASTVariableDeclarator) {
                        nodeType = ((ASTLocalVariableDeclaration)(declarator.jjtGetParent().jjtGetParent())).getTypeNode();
                    } else {
                        System.err.println("Unknown type");
                    }

                    if (nodeType != null && MAP_CLASSES.keySet().contains(nodeType.getTypeImage())) {
                        return declarator;
                    }
                }
            }
        }

        return null;
    }
}


Here is the ruleset file:

<?xml version="1.0"?>
        <ruleset name="My custom rules"
            xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd"
            xsi:noNamespaceSchemaLocation="http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
            <rule name="ScanBigDecimal"
                  message="Avoid using BigDecimal.toString or implicit casting to string"
                  class="ScanBigDecimal">
              <description>
              Avoid using BigDecimal.toString or implicit casting to string
              </description>
                <priority>3</priority>

              <example>
        <![CDATA[
            }
        ]]>
              </example>
            </rule>
        </ruleset>


Command to test the rule:

java -cp "C:/Users/clarence/Documents/MyApps/pmd-bin-5.0.0/lib/*";. net.sourceforge.pmd.PMD C:/Users/clarence/Documents/MyTest/pmd/MyTestCase.java text bigdecimal.xml


Below is a sample test case. As said, there are cases that cannot be detected by my PMD rule. For example, concatenating a string with a BigDecimal return value from method call. Or, passing a BigDecimal to a method that takes an Object parameter and internally invoking the toString method.

Anyway, feel free to take the code and make it better~!

import java.math.BigDecimal;

public class MyTestCase {

    public static void main(String args) {
        BigDecimal bd = new BigDecimal("12.345");

        // will trigger error
        System.out.println("This is the number: " + bd);

        // this type can't be detected...
        System.out.println(bd);

        // will trigger error;
        String s = "" + bd;
        String s2 = "" + bd.toString();
        String s3 = bd + "";
        String s4 = bd + MyTestCase.returnStr();

        // can't be detected
        String s5 = "" + returnBigDecimal("12.34");
        System.err.println(returnString(bd));

        // this wont trigger warning
        String s6 = "" + bd.toPlainString();

        String s7 = "" + "a" + "b";

    }

    private static String returnStr() {
        return "hello";
    }

    private static String returnString(BigDecimal bd) {
        return "" + bd;
    }

    private static BigDecimal returnBigDecimal(String num) {
        return new BigDecimal(num);
    }
}

Monday, August 27, 2012

TP-Link TL-MR3020 as AP and router with OpenWrt

Just got a travel router TL-MR3020 and immediately flashed it with OpenWrt. By default, it will be act as an AP. Here is how to add configuration to switch between AP and router modes.

Before we start, make sure the fail safe mode is working: with the device powered off, change the switch to AP mode. Press and hold the WPS button. Turn the power on. Wait for 2 seconds until the WPS light starts to flash slowly. Immediately switch the mode to 3G while still holding the WPS button.

Now the device has IP of 192.168.1.1 and you can connect to it via a LAN cable with your computer configured with IP 192.168.1.x


With fail safe mode working, reboot the device normally. Go to /etc/config and make the following changes.

Define a new wifi network. Make copies of the file network and name them network.ap and network.router. Edit network.router and remove the lan section and add the following:
config interface 'wan'
        option ifname 'eth0'
        option proto 'dhcp'

config interface 'wifi'
        option proto 'static'
        option ipaddr '192.168.2.1'
        option netmask '255.255.255.0'


Change the wireless settings. Make copies of the file wireless and name them wireless.ap and wireless.router. Edit wireless.router and change the wifi-iface section to suit your need. Here I use WPA2
config wifi-iface
        option device   radio0
        option network  wifi
        option mode     ap
        option ssid     put_your_wireless_ssid_here
        #option encryption none
        option encryption psk2
        option key put_your_wireless_password_here


In router mode, the device withh assign IP to others via wifi. So we need to create a new DHCP pool. Make copies of the file dhcp and name them as dhcp.ap and dhcp.router. Add the following section to dhcp.router:
config dhcp wifi
        option interface        wifi
        option start            100
        option limit            150
        option leasetime        12h


Finally, we will modify the firewall rules to allow traffic from wifi to WAN. Make copies of the file firewall and name them as firewall.ap and firewall.router. Edit firewall.router and add the following sections:
config zone
        option name             wifi
        option input            ACCEPT
        option output           ACCEPT
        option forward          REJECT

config forwarding
        option src              wifi
        option dest             wan


Now we are all set. To switch between AP and router modes, just copy the above modified files to the original file and restart wifi, firewall and dhcp services:
ifup wifi
wifi
/etc/init.d/firewall restart
/etc/init.d/dnsmasq restart


However, we can do better than that. We can map the mode switch on TL-MR3020 to auto execute the commands for us.

Create a file name /sbin/sw_ap_mode
#!/bin/sh
/bin/cp -f /etc/config/dhcp.ap /etc/config/dhcp
/bin/cp -f /etc/config/firewall.ap /etc/config/firewall
/bin/cp -f /etc/config/network.ap /etc/config/network
/bin/cp -f /etc/config/wireless.ap /etc/config/wireless
/bin/sync
/sbin/ifup wifi
/sbin/wifi
/etc/init.d/firewall restart
/etc/init.d/dnsmasq restart


Create a file name /sbin/sw_router_mode
#!/bin/sh
/bin/cp -f /etc/config/dhcp.router /etc/config/dhcp
/bin/cp -f /etc/config/firewall.router /etc/config/firewall
/bin/cp -f /etc/config/network.router /etc/config/network
/bin/cp -f /etc/config/wireless.router /etc/config/wireless
/bin/sync
/sbin/ifup wan
/sbin/ifup wifi
/sbin/wifi
/etc/init.d/firewall restart
/etc/init.d/dnsmasq restart

Following the instructions on http://wiki.openwrt.org/doc/howto/hardware.button to edit /etc/hotplug.d/button. Then create /etc/hotplug.d/button/00-button. Execute the following commands:
uci add system button
uci set system.@button[-1].button=BTN_0
uci set system.@button[-1].action=released
uci set system.@button[-1].handler="/sbin/sw_router_mode"
uci commit system

uci add system button
uci set system.@button[-1].button=BTN_0
uci set system.@button[-1].action=pressed
uci set system.@button[-1].handler="/sbin/sw_ap_mode"
uci commit system

Sunday, July 15, 2012

Using USB storage on Raspberry Pi

Assuming the USB storage is on /dev/sda

To mount a USB stick / disk and make it writable for the default login pi

/usr/bin/sudo /bin/mount -t vfat -o uid=pi,gid=pi /dev/sda /mnt/usb/


To create a 512M file on the USB storage and use it as swap:

# create an empty swapfile
/bin/dd if=/dev/zero of=/mnt/usb/swapfile bs=1M count=512
# create a loop device
/usr/bin/sudo /sbin/losetup /dev/loop0 /mnt/usb/swapfile
# setup swap area
/usr/bin/sudo /sbin/mkswap /dev/loop0
# enable the swap
/usr/bin/sudo /sbin/swapon /dev/loop0


To remove the swap:

# disable swap
/usr/bin/sudo /sbin/swapoff /dev/loop0
# delete loop
/usr/bin/sudo /sbin/losetup -d /dev/loop0


Umount the USB storage:

/usr/bin/sudo /bin/umount /mnt/usb/

Sunday, June 3, 2012

USB wifi for Raspberry Pi

Here are steps for setting up the USB wifi adapter TP-LINK TL-WN722N on my Raspberry Pi:


  • Edit /etc/apt/sources.list and add deb http://ftp.us.debian.org/debian squeeze non-free at the end

  • sudo aptitude update

  • sudo aptitude install firmware-atheros

  • Download the firmware
    cd /lib/firmware
    sudo wget http://linuxwireless.org/download/htc_fw/1.3/htc_9271.fw

  • Protect the file /etc/network/interfaces
    sudo chmod 0600 /etc/network/interfaces

  • Edit /etc/network/interfaces
    sudo vi /etc/network/interfaces

  • Add the following lines. Note that I am using WEP. If you are using WPA, use the fields wpa-ssid and wpa-psk instead
    auto wlan0
    allow-hotplug wlan0
    iface wlan0 inet dhcp
            wireless-essid your-ssid
            wireless-key your-hex-key

  • Bring up the interface
    sudo ifup wlan0


About the performance. Using sftp to copy /dev/zero via wifi maxed out the CPU with transfer rate at around 1.6MB/s... maybe need to test the speed without encryption...






Reference: http://elinux.org/RPi_VerifiedPeripherals#Working_USB_Wifi_Adapters

Sunday, May 27, 2012

RocketModem II with Linux kernel 3.x

Got a Comtrol 6-port RocketModem II around 8 years ago. The original plan was to use it in our outfax.com fax machine pool. But it never get its chance as the other modems just wont die :P.


Recently as we moved our machines to a new data center, it is a good time to put it back to work. With so many spare parts lying around in my house, it is not difficult to find a motherboard with PCI slots for it. The hard part is the software. The card was designed 10 years ago with drivers for Linux kernel 2.4. But now for the new machines, we are going to use Ubuntu 12.04 with Linux 3.x kernel.


The first attempt was to use the build-in driver of Comtrol in the Linux kernel. But it just wont work. The modem responds "OK" with every AT command it received but doesnt act on it. Then tried to compile the obsolete drivers but there were just too many things to fix in the source that I eventually gave up.


Eventually, tried to compile and load the latest RocketModem IV drivers. Lo and behold! Everything just works! Problem solved!