Skip to content
FlyingLotus1983 edited this page Oct 19, 2014 · 25 revisions

Context

This page is really just a collection of notes on the internals of FUSE and FireFUSE, that I started for my own purposes, that others may one day find useful.

Links About FUSE in General

Useful commands while running, maintaining, and troubleshooting FireFUSE:

  • ps -ef | grep node show all node.js processes
  • ps -ef | grep raspistill show all raspistill processes
  • sudo fusermount -u /dev/firefuse
  • sudo lsof /dev/firefuse will show you any programs that are currently have files open in FIREFUSE. Use this if you get a "Device or resource busy" error when trying to run the fusermount -u /dev/firefuse above. NOTE: You may have to install this, as it doesn't come stock with Debian. sudo apt-get install lsof should install it.
  • sudo kill -9 pid-of-raspistill
  • sudo nano /var/log/firefuse.log Open log start figuring out what went wrong
  • ls -l /dev/firefuse
  • rm version-xxx where xxx is whatever 'version' files show up in the FireREST directory. This will cause fire/upgrade to rebuild EVERYTHING, which will take a while.
  • fire/web -s Launch FireREST webserver in background for current user
  • fire/web -r Restart FireREST webserver
  • fire/web -k Kill FireREST webserver
  • fire/log -c Continuously copy log entries to console (CTRL-C to quit)
  • fire/log -t Log at TRACE level and show fully detailed information about FireFUSE activity
  • fire/log -d Log at DEBUG level and show some detailed information about FireFUSE activity
  • fire/log -i Log at INFO level (default):
  • fire/config -d Apply default configuration file
  • fire/config -D myconfig.json Set default configuration file
  • fire/config -e examples/400x400.json Edit current configuration file to use 400x400 camera
  • fire/config custom/myconfig.json Apply custom configuration file
  • fire/upgrade will upgrade to you the latest and greatest FireFUSE, FireREST, and FireSight versions.

Errors used by FUSE and other important POSIX file/process functions:

  • ENOSYS: Function not implemented errno.EROFS: Read-only file system
  • EPERM: Operation not permitted
  • EACCES: Permission denied
  • ENOENT: No such file or directory
  • EIO:** I/O error
  • EEXIST: File exists
  • ENOTDIR: Not a directory
  • EISDIR: Is a directory
  • ENOTEMPTY: Directory not empty
  • ESRCH: No process or process group can be found corresponding to that specified by pid.
  • EINVAL: The value of the sig argument is an invalid or unsupported signal number.

Important Directories and Files, Specific to FireFUSE

  • /dev/firefuse/config.json
  • /dev/firefuse/echo
  • /dev/firefuse/firelog
  • /dev/firefuse/firestep
  • /dev/firefuse/holes
  • /dev/firefuse/status
  • /var/firefuse/config.json is the main config file for FireFUSE, FireREST, etc..
  • /var/firefuse/raspistill.PID contains the process ID of Raspistill, which runs in the background
  • /var/log/firefuse.log is the debug log for firefuse (see section below for details).

Building

  • When building, make sure to stop node.js and close any Samba windows on client PCs. These will cause the build to fail, as it needs to unmount the fuse system.

Debugging

FireFUSE will log debug info to: /var/log/firefuse.log

You can change the FireFUSE debug level when building it. If you look at loginfo commands, if you want more info in debugging, change the debug level to a higher level. At level 0, only errors get logged. At level 4, everything gets logged. Lines w/'i' after timestamp: the letter 'i' is the level of the logging

Excerpted from: https://github.com/firepick1/FireSight/blob/master/FireLog.h

#define FIRELOG_ERROR 0

#define FIRELOG_WARN 1

#define FIRELOG_INFO 2

#define FIRELOG_DEBUG 3

#define FIRELOG_TRACE 4

Background and foreground threads (Notes from Karl)

ok. here's what's going on. there are multiple threads running at the same time.

background thread and foreground thread

the foreground handles FUSE file access

this means that if the foreground thread blocks on ANYTHING, the whole FUSE freezes

for GCODE stuff, the foreground puts the GCODE into a queue for the background thread to send on to Marlin.

That way the background thread can block on Marlin while the foreground thread serves FUSE requests

with raspistill being another process, there's even more things going on

you saw the signal, but the return is not immediate (FireFUSE sends the KILL signal to raspistill. Then raspistill does something indeterminate but FireFUSE has moved on after having sent the signal and no longer cares)

what happens is that raspistill writes to a file

amusingly, that file is...in FireFUSE

you'll see that in cv.cpp

raspistill opens the file, which means that FireFUSE cve_open gets called

then cve_write several times

then cve_release

so now there are two things happening:

  1. the triggering request to camera.jpg is STILL waiting
  2. FireFUSE has to simultaneously accept raspistill's data write to the shared queue.

it's very non-sequential and multi-threaded, which is a PIA

once #2 is complete, #1 can unblock and return the queued image to the end user

oh. the one difference between sync and non-sync is the FireFUSE queue

the FireFUSE queue on a non-sync call will always succeed immediately because it just returns the latest thing in the queue.

that is VERY fast and satisfies most clients EXCEPT for folks who need the real latest image.

Sync has to wait for the queue to refresh after raspistill has written the file

queues know if they have fresh or stale data.

so all sync has to do is pthread_yield() until the image is fresh.

then there might be a bug in FireFUSE where it is not yielding until fresh

so sync is basically implemented by clearing the queues of all fresh data and sending KILL to raspistill. That way, the next image in will be taken as the fresh one. By fresh I don't mean latest. By fresh I mean "nobody looked at it yet". It could be fresh and a week old if nobody looked at it. Basically we clean the shelf of old stuff and wait for the new

Queues

NOTE: Queues are currently LIFOs.

There are multiple queues all over, at least the ones you mentioned, but others for all the various things running around the system. for example camera->monitor queue, etc.

it's a bit mind-bendy, yes. for example, monitor doesn't always show the camera, sometimes it shows firesight output. that's why it has it's own queue. and when you are writing to camera.jpg, it ALSO needs a queue because the write takes multiple write calls (you'll see that in the log). without a queue you get pieces of image and that's bad.

the smartptrs are in the queues but are not the queues. each q element is a smartptr

LIFOCache

  • T peek ()
  • T get () This is called when a non-syncing file is read.
  • T get_sync (int msTimeout=0) This is called when a file in /sync/ is read.
  • void post (T value)
  • bool isFresh ()
  • long getWriteCount () writeCount gets incremented every time that
  • long getReadCount ()
  • private: T values[2];
  • private: syncCount Incremented every time get_sync() is called, and decremented every time post() is called.

NOTE: Semaphore gets posted if syncCount gets decremented by post().

Instances:

  • CVE: LIFOCache<SmartPointer<char> > src_saved_png
  • CVE: LIFOCache<SmartPointer<char> > src_save_fire
  • CVE: LIFOCache<SmartPointer<char> > src_process_fire
  • CVE: LIFOCache<SmartPointer<char> > src_firesight_json
  • CVE: LIFOCache<SmartPointer<char> > src_properties_json
  • CameraNode: LIFOCache<SmartPointer<char> > src_camera_jpg
  • CameraNode: LIFOCache<Mat> src_camera_mat_gray
  • CameraNode: LIFOCache<Mat> src_camera_mat_bgr
  • CameraNode: LIFOCache<SmartPointer<char> > src_monitor_jpg
  • CameraNode: LIFOCache<SmartPointer<char> > src_output_jpg

SmartPointer

  • SmartPointer (T *aPtr, size_t count=0, int flags=ALLOCATE, size_t blockSize=1, char blockPad=0)
  • SmartPointer (const SmartPointer &that)
  • SmartPointer & operator= (SmartPointer that)
  • T * data () const
  • int getReferences ()
  • T & operator* ()
  • const T & operator* () const
  • T * operator-> ()
  • const T * operator-> () const
  • operator T * () const
  • size_t size () const
  • void setSize (size_t value)
  • size_t allocated_size () const

Clone this wiki locally