Friday, June 16, 2023

How to properly do git rebase

https://verdantfox.com/blog/view/how-to-git-rebase-mainmaster-onto-your-feature-branch-even-with-merge-conflicts by Teddy Williams

 

Does your project prefer git rebase instead of git merge? Has your branch fallen out of sync with the main branch and you are unable to automate your rebase due to conflicts? If so, you might have run into rebase hell. This happens when you try to git rebase, solve your conflicts, and push to the main branch, only to find that the main branch is now, once again, out of sync in a never-ending loop. Let's break out of rebase hell with this short guide to rebasing.

The steps

  1. Go to the branch in need of rebasing
  2. Enter git fetch origin (This syncs your main branch with the latest changes)
  3. Enter git rebase origin/main (or git rebase origin/master if your main branch is named master)
  4. Fix merge conflicts that arise however you see fit
  5. After fixing merge conflicts, git add FILE previously merge conflicted files
  6. Enter git rebase --continue (or git rebase --skip if git complains that there were no changes after resolving all conflicts)
  7. Repeat as necessary as merge conflicts arise in the subsequent commits
  8. Once the rebase is complete, enter git push origin HEAD --force-with-lease

    This command pushes your rebase fixed branch to remote. The --force is important as it tells remote, "I know these changes are correct, accept them as is." Without the --force flag, your remote branch will continue to believe it is out of sync and will claim a merge conflict.

And that's it. You have now git rebased the main branch onto your feature branch and broken yourself out of rebase hell.

 

Friday, August 5, 2022

GCC ARM Cortex M4 backtrace print from target

To do a more verbose crash logging from HardFault handler (or assert statement) the Backtrace library can be used: https://github.com/red-rocket-computing/backtrace

Don't forget to add -funwind-tables to compiler flags. For function names to be available -mpoke-function-name needs to be added as well (increases binary size!).

Backtrace only records function name and execution address but it can be easily extended to include the stack pointer information which can help to detect stack overflow related problems.

 

Additional resources to consider:

https://alexkalmuk.medium.com/how-stack-trace-on-arm-works-5634b35ddca1
https://maskray.me/blog/2020-11-08-stack-unwinding
https://github.com/ccoffing/airbag_fd/
https://github.com/bakerstu/openmrn/blob/62683863e8621cef35e94c9dcfe5abcaf996d7a2/src/freertos_drivers/common/cpu_profile.hxx#L162
https://stackoverflow.com/questions/6254058/how-to-get-fullstacktrace-using-unwind-backtrace-on-sigsegv
https://stackoverflow.com/questions/47331426/stack-backtrace-for-arm-core-using-gcc-compiler-when-there-is-a-msp-to-psp-swit
https://stackoverflow.com/questions/70652306/how-does-stack-unwinding-in-cortex-m-devices-works
https://stackoverflow.com/questions/59855643/unwind-backtrace-for-different-context-on-freertos
https://gcc.gnu.org/onlinedocs/gcc-10.3.0/gcc/ARM-Options.html#ARM-Options

Thursday, June 23, 2022

C++: consteval in C++17

 

template <auto V>
static constexpr auto force_consteval = V; 
#define STRINGHASH(str) force_consteval<stringhash(str)> 

C++ get declared type info

Useful for automating some compile-time initialization in combination with consteval or constexpr:

#include "string.h"
#include "stdlib.h"
#include <iostream>

template <class T>
constexpr std::string_view type_name()
{
    using namespace std;
#ifdef __clang__
    string_view p = __PRETTY_FUNCTION__;
    return string_view(p.data() + 34, p.size() - 34 - 1);
#elif defined(__GNUC__)
    string_view p = __PRETTY_FUNCTION__;
#  if __cplusplus < 201402
    return string_view(p.data() + 36, p.size() - 36 - 1);
#  else
    return string_view(p.data() + 49, p.find(';', 49) - 49);
#  endif
#elif defined(_MSC_VER)
    string_view p = __FUNCSIG__;
    return string_view(p.data() + 84, p.size() - 84 - 7);
#endif
}

static char x[] = "Test";

int main() {
    std::cout << type_name<decltype(x)>() << "\r\n";
    std::cout << type_name<std::decay<decltype(x)>::type>();
}


Wednesday, March 16, 2022

Scope mutex locking on FreeRTOS

Class:

/** 
  * @brief The FreeRtosScopedLock class, provides an RAII 
  *        style approach to locking a FreeRTOS mutex, 
  *        similar to C++11 std::scoped_lock<> */class FreeRtosScopedLock

{

public:

    explicit FreeRtosScopedLock(SemaphoreHandle_t mutex) :

            mMutex(mutex)

    {

        xSemaphoreTakeRecursive(mMutex, portMAX_DELAY);

        //for demo, assume success

    }


    ~FreeRtosScopedLock()

    {

        xSemaphoreGiveRecursive(mMutex);

        //for demo, assume success

    }


private:

    SemaphoreHandle_t mMutex;

};

 

Usage example: 

bool AccessMyDeviceNewStyle()

{

    ScopedLock lockItDown(mDevMutex);


    if (!SomeGuardCheck())

    {

        return false;

    }


    if (!AnotherGuardCheck())

    {

        return false;

    }


    //Do Stuff


    return true;

}

Godbolt compile explorer 

 

References:

https://covemountainsoftware.com/2019/11/26/why-i-prefer-c-raii-all-the-things/

https://blogs.sw.siemens.com/embedded-software/2017/03/27/more-on-c-with-an-rtos/ 

https://embeddedartistry.com/blog/2018/02/08/implementing-stdmutex-with-freertos/

Monday, May 23, 2016

Rescue files from dying hard drive

For rescuing files with "least effort" strategy (copy what can be read and don't try endlessly to recover what can't be read) ddrescue and find commands come to rescue. The command below tries to copy all the files from the current location to the destination; if copying fails due to any error it erases the destination file, so everything copied is actually good data (not damaged).

# Makes the directory structure copy
find . -type d -exec mkdir /dst/dir/{} \;

# Tries to copy the files with 1 retry  and exit on error (and delete dst file if exit on error)
find . -type f -exec sh -c "dd_rescue -X1 -e1 {} /dst/dir/{} || rm /dst/dir/{}" \;

Note: this solution only works if your file system is still readable.

Based on the answer from: http://superuser.com/questions/159354/linux-tool-to-copy-files-directories-from-failing-hard-disk

Sunday, August 2, 2015

Linux: find which file is using a specific LBA sector on disk (bad blocks recovery)

There is one universal truth about any hardware: it will fail. Hard drives are no exception and their failure is usually more unpleasant than a failing mouse or graphic card. In case your drive is failing and you don't have backup (bad option) you will try to figure out which file was damaged due to bad sectors so you might just copy the remaining part to another drive and replace the damaged files (if you have a copy). Of course, this works only on hard disks with bad sectors; if the whole disk fails (electronics) then you'll have to use some other option to get the data.

Anyway, to make the story short, you have an accessible drive with some bad sectors. You figured out the LBA address of the failing sectors (with SMART test or kernel complaining about failed reading etc.) and now you would really like to know the file that is using this sector. But due to high level of abstraction (partitions, LVM, filesystem inodes etc.) this is not a trivial task as you have to recalculate addresses, convert the sector number to inode number and so on... As always, there is a shortcut: this script figures out this for you automatically. :) As it is a really useful tool I am posting a copy here on the blog, so it doesn't get lost if the original source goes down, but the original author is still Stuart D Gathman.

The script input parameters are the failing drive (e.g. /dev/sdc) and the LBA address of the failing sector (e.g. 1834273903). When starting the script it will dig in the layers of abstraction and report the unfortunate file (but if you're lucky it might say it is used by free space so no data has been damaged).

The script:


#!/usr/bin/python
# Identify partition, LV, file containing a sector 

# Copyright (C) 2010,2012 Stuart D. Gathman
# Shared under GNU Public License v2 or later
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.

#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.

#   You should have received a copy of the GNU General Public License along
#   with this program; if not, write to the Free Software Foundation, Inc.,
#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import sys
from subprocess import Popen,PIPE

ID_LVM = 0x8e
ID_LINUX = 0x83
ID_EXT = 0x05
ID_RAID = 0xfd

def idtoname(id):
  if id == ID_LVM: return "Linux LVM"
  if id == ID_LINUX: return "Linux Filesystem"
  if id == ID_EXT: return "Extended Partition"
  if id == ID_RAID: return "Software RAID"
  return hex(id)

class Segment(object):
  __slots__ = ('pe1st','pelst','lvpath','le1st','lelst')
  def __init__(self,pe1st,pelst):
    self.pe1st = pe1st;
    self.pelst = pelst;
  def __str__(self):
    return "Seg:%d-%d:%s:%d-%d" % (
      self.pe1st,self.pelst,self.lvpath,self.le1st,self.lelst)

def cmdoutput(cmd):
  p = Popen(cmd, shell=True, stdout=PIPE)
  try:
    for ln in p.stdout:
      yield ln
  finally:
    p.stdout.close()
    p.wait()

def icheck(fs,blk):
  "Return inum from block number, or 0 if free space."
  for ln in cmdoutput("debugfs -R 'icheck %d' '%s' 2>/dev/null"%(blk,fs)):
    b,i = ln.strip().split(None,1)
    if not b[0].isdigit(): continue
    if int(b) == blk:
      if i.startswith('<'):
 return 0
      return int(i)
  raise ValueError('%s: invalid block: %d'%(fs,blk))

def ncheck(fs,inum):
  "Return filename from inode number, or None if not linked."
  for ln in cmdoutput("debugfs -R 'ncheck %d' '%s' 2>/dev/null"%(inum,fs)):
    i,n = ln.strip().split(None,1)
    if not i[0].isdigit(): continue
    if int(i) == inum:
      return n
  return None

def blkid(fs):
  "Return dictionary of block device attributes"
  d = {}
  for ln in cmdoutput("blkid -o export '%s'"%fs):
    k,v = ln.strip().split('=',1)
    d[k] = v
  return d

def getpvmap(pv):
  pe_start = 192 * 2
  pe_size = None
  seg = None
  segs = []
  for ln in cmdoutput("pvdisplay --units k -m %s"%pv):
    a = ln.strip().split()
    if not a: continue
    if a[0] == 'Physical' and a[4].endswith(':'):
      pe1st = int(a[2])
      pelst = int(a[4][:-1])
      seg = Segment(pe1st,pelst)
    elif seg and a[0] == 'Logical':
      if a[1] == 'volume':
 seg.lvpath = a[2]
      elif a[1] == 'extents':
 seg.le1st = int(a[2])
 seg.lelst = int(a[4])
 segs.append(seg)
    elif a[0] == 'PE' and a[1] == 'Size':
      if a[2] == "(KByte)":
 pe_size = int(a[3]) * 2
      elif a[3] == 'KiB':
 pe_size = int(float(a[2])) * 2
  if segs:
    for ln in cmdoutput("pvs --units k -o+pe_start %s"%pv):
      a = ln.split()
      if a[0] == pv:
        lst = a[-1]
 if lst.lower().endswith('k'):
   pe_start = int(float(lst[:-1]))*2
   return pe_start,pe_size,segs
  return None

def findlv(pv,sect):
  res = getpvmap(pv)
  if not res: return None
  pe_start,pe_size,m = res
  if sect < pe_start:
    raise Exception("Bad sector in PV metadata area")
  pe = int((sect - pe_start)/pe_size)
  pebeg = pe * pe_size + pe_start
  peoff = sect - pebeg
  for s in m:
    if s.pe1st <= pe <= s.pelst:
      le = s.le1st + pe - s.pe1st
      return s.lvpath,le * pe_size + peoff

def getmdmap():
  with open('/proc/mdstat','rt') as fp:
    m = []
    for ln in fp:
      if ln.startswith('md'):
 a = ln.split(':')
 raid = a[0].strip()
 devs = []
 a = a[1].split()
 for d in a[2:]:
   devs.append(d.split('[')[0])
 m.append((raid,devs))
    return m

def parse_sfdisk(s):
  for ln in s:
    try:
      part,desc = ln.split(':')
      if part.startswith('/dev/'):
        d = {}
        for p in desc.split(','):
   name,val = p.split('=')
   name = name.strip()
   if name.lower() == 'id':
     d[name] = int(val,16)
   else:
     d[name] = int(val)
 yield part.strip(),d
    except ValueError:
      continue

def findpart(wd,lba):
  s = cmdoutput("sfdisk -d %s"%wd)
  parts = [ (part,d['start'],d['size'],d['Id']) for part,d in parse_sfdisk(s) ]
  for part,start,sz,Id in parts:
    if Id == ID_EXT: continue
    if start <= lba < start + sz:
      return part,lba - start,Id
  return None

if __name__ == '__main__':
  wd = sys.argv[1]
  lba = int(sys.argv[2])
  print wd,lba,"Whole Disk"
  res = findpart(wd,lba)
  if not res:
    print "LBA is outside any partition"
    sys.exit(1)
  part,sect,Id = res
  print part,sect,idtoname(Id)
  if Id == ID_LVM:
    bd,sect = findlv(part,sect)
    # FIXME: problems if LV is snapshot
  elif Id == ID_LINUX:
    bd = part
  else:
    if Id == ID_RAID:
      for md,devs in getmdmap():
 for dev in devs:
   if part == "/dev/"+dev:
     part = "/dev/"+md
     break
        else: continue
 break
    res = findlv(part,sect)
    if res:
      print "PV =",part
      bd,sect = res
    else:
      bd = part
  blksiz = 4096
  blk = int(sect * 512 / blksiz)
  p = blkid(bd)
  try:
    t = p['TYPE']
  except:
    print bd,p
    raise
  print "fs=%s block=%d %s"%(bd,blk,t)
  if t.startswith('ext'):
    inum = icheck(bd,blk)
    if inum:
      fn = ncheck(bd,inum)
      print "file=%s inum=%d"%(fn,inum)
    else:
      print "<free space>"

Friday, November 29, 2013

Add current time to ffmpeg segment muxer filename

The function below should replace the existing one in libavformat/utils.c. It is for libav 0.8.9 version so it might not work on other versions.

Usage: add %t to the filename pattern, it gets replaced with the date and time in format "YYYY-MM-DD_hh-mm-ss".


int av_get_frame_filename(char *buf, int buf_size,
                          const char *path, int number)
{
    const char *p;
    char *q, buf1[30], c;
    int nd, len, percentd_found, percentt_found;
    struct timeval tv;

    q = buf;
    p = path;
    percentd_found = 0;
    percentt_found = 0;
    for(;;) {
        c = *p++;
        if (c == '\0')
            break;
        if (c == '%') {
            do {
                nd = 0;
                while (isdigit(*p)) {
                    nd = nd * 10 + *p++ - '0';
                }
                c = *p++;
            } while (isdigit(c));

            switch(c) {
            case '%':
                goto addchar;
            case 'd':
                if (percentd_found)
                    goto fail;
                percentd_found = 1;
                snprintf(buf1, sizeof(buf1), "%0*d", nd, number);
                len = strlen(buf1);
                if ((q - buf + len) > buf_size - 1)
                    goto fail;
                memcpy(q, buf1, len);
                q += len;
                break;
            case 't':

                if (percentt_found)
                    goto fail;
                percentt_found = 1;

                gettimeofday(&tv, NULL);
                strftime(buf1, sizeof(buf1), "%Y-%m-%d_%H-%M-%S", localtime(&tv.tv_sec));
                len = strlen(buf1);
                if ((q - buf + len) > buf_size - 1)
                    goto fail;
                memcpy(q, buf1, len);
                q += len;
                break;
            default:
                goto fail;
            }
        } else {
        addchar:
            if ((q - buf) < buf_size - 1)
                *q++ = c;
        }
    }
    if (!percentd_found && !percentt_found)
        goto fail;
    *q = '\0';
    return 0;
 fail:
    *q = '\0';
    return -1;
}

Tuesday, November 19, 2013

Raspberry Pi system (Raspbian) size optimization

You use Raspbian system image but only need RPi as a special purpose server so many packages are unnecessary (especially GUI components). A few suggestions how to slim down the Raspbian (or any other Debian based RPi distribution) are explained here:
https://extremeshok.com/2012/07/22/raspberry-pi-raspbian-tuning-optimising-optimizing-for-reduced-memory-usage/
https://wiki.debian.org/ReduceDebian

There are at least two benefits from slimmed down system:
- You can re-use old SD cards lying around the house and
- the package management (apt-*, dpkg) work faster as there are less files to process.

Sunday, February 24, 2013

Adding a track to google maps

So you have your own web page that displays a map from Google Maps and you want it to show a GPS track. Well, it does not work out of the box as Google Maps API does not support loading tracks by itself. But luckily a simple solution in a few lines of jQuery code exists that does exactly that - loads a GPX track onto a map. The code and everything else is accessible at: http://www.jacquet80.eu/blog/post/2011/02/Display-GPX-tracks-using-Google-Maps-API

Monday, December 3, 2012

Mikrotirk L2TP/IPSec configuration (Windows compatible)

/ppp profile
add change-tcp-mss=yes dns-server=192.168.1.254 local-address=172.21.16.254 \
    name=VPN-server only-one=no remote-address=VPN-server use-compression=\
    default use-encryption=default use-ipv6=no use-mpls=default \
    use-vj-compression=default wins-server=192.168.1.3
set 3 change-tcp-mss=yes name=default-encryption only-one=default \
    use-compression=default use-encryption=required use-ipv6=no use-mpls=\
    default use-vj-compression=default

/ppp secret
add caller-id="" disabled=no limit-bytes-in=0 limit-bytes-out=0 name=user password=passwd \
    profile=VPN-server routes="" service=l2tp

/ip pool
add name=VPN-server ranges=172.21.16.100-172.21.16.200

/interface l2tp-server server
set authentication=mschap1,mschap2 default-profile=VPN-server enabled=yes \
    max-mru=1460 max-mtu=1460 mrru=disabled

/ip ipsec proposal
set [ find default=yes ] auth-algorithms=sha1 disabled=no enc-algorithms=3des \
    lifetime=30m name=default pfs-group=modp1024

/ip ipsec peer
add address=0.0.0.0/0 auth-method=pre-shared-key comment="COMPANY VPN" \
    dh-group=modp1024 disabled=no dpd-interval=2m dpd-maximum-failures=5 \
    enc-algorithm=3des exchange-mode=main-l2tp generate-policy=yes \
    hash-algorithm=sha1 lifetime=1d my-id-user-fqdn="" nat-traversal=yes port=\
    500 secret=secret_password send-initial-contact=yes

/ip firewall filter
add action=accept chain=input comment="L2TP VPN" disabled=no dst-address=\
    xx.xx.xx.xx dst-port=500,4500,1701 protocol=udp
add action=accept chain=input comment="L2TP VPN" disabled=no protocol=ipsec-esp
add action=accept chain=output comment="L2TP VPN" disabled=no dst-address=\
    xx.xx.xx.xx dst-port=500,4500,1701 protocol=udp

/system logging
add action=memory disabled=no prefix="" topics=ipsec
add action=memory disabled=no prefix="" topics=radius

Source: http://forum.mikrotik.com/viewtopic.php?f=2&t=65059

Tuesday, November 27, 2012

Serving a custom catch-all web page on a private WiFi

Sometimes you would like to publish a specific private web page for your WiFi users. For example, you want to allow the guests in your restaurant to access the on-line version of your menu while using your open WiFi hotspot (and no other internet web page!). It is an easy task with an OpenWRT or DD-WRT router (your web pages still need to be served from a server, not the router itself). This might be known with the keywords catch-all, wildcard, HTTP, DNS.

First we need to make sure we catch all DNS requests and return the IP of our web server for all different domains. The trick is in an additional DNSMasq configuration option (192.168.1.5 is your web server):
address=/#/192.168.1.5
Of course, your DHCP server should give only your DNS as the DNS server to the client. And this is all we need if the router is not connected to internet as any other hacking (e.g. entering a custom DNS server on the client or trying to use a VPN) will have no effect. But you may still enable the HTTP redirect in DD-WRT firmware just to be sure to catch the IP only web requests also.

When all domains are redirected to our IP address we need to instruct the web server to serve the same page for all requests (ignoring the Host header) which is pretty easy and the most common default configuration. But for visual effect you can server your page on myrestaurant.com (it doesn't matter if the domain exists or not as long as you use it only inside your internal network) and force a http redirect to it if a guest is trying to access any other domain (e.g. facebook.com or google.com). A hint for Apache users (others should google for "http redirect 301" and your web server name):
Redirect 301 / http://www.newdomain.com/

Of course it is highly recommended not to use this wireless network for anything else as it will look bogus from users perspective (web pages not loading etc.). Use a firewall or do not connect the router to internet at all.

Resources for DNSMasq configuration:

http://serverfault.com/questions/351108/using-dnsmasq-to-resolve-all-hosts-to-the-same-address
http://www.dd-wrt.com/wiki/index.php/DNSMasq_-_DNS_for_your_local_network_-_HOWTO
http://coolaj86.info/articles/redirect-domains-and-dns-using-dd-wrt.html

WARNING: this guide is not yet tested but in theory it should work. This warning will be removed when I actually test it (or get a confirmation that it works).

Monday, October 1, 2012

AirCam power usage

Specifications for:
AirCam is 2.4 Watts Maximum.
Actual Measurement:
12.06 Volts x 0.157 Amps = 1.89342 Watts 
Ambient Temperature 19.3 Degrees Celsius and a cable length of 2 metres.

Specifications for:
AirCam Dome is 3.5 Watts Maximum.
Actual Measurement:
12.06 Volts x 0.151 Amps = 1.8821 Watts 
Ambient Temperature 19.5 Degrees Celsius and a cable length of 2 metres.

Source: http://forum.ubnt.com/showthread.php?p=326727