1 / 27

Brittle Systems will Break – Not Bend: Can Aspect-Oriented Programming Help?

Brittle Systems will Break – Not Bend: Can Aspect-Oriented Programming Help?. Yvonne Coady, Gregor Kiczales, Joon Suan Ong, Andrew Warfield, and Michael Feeley University of British Columbia. a -kernel project. Gregor Kiczales:

gareth
Download Presentation

Brittle Systems will Break – Not Bend: Can Aspect-Oriented Programming Help?

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Brittle Systems will Break – Not Bend: Can Aspect-Oriented Programming Help? Yvonne Coady, Gregor Kiczales, Joon Suan Ong, Andrew Warfield, and Michael Feeley University of British Columbia

  2. a-kernel project Gregor Kiczales: here you are loading the term aspect, you have to just say it, not appear to define it • can AOP improve the modularity of OS code? • better locality • better comprehensibility • more (un)pluggable • more evolvable • working on several aspects of OS behaviour • when running low on pages, wakeup the page daemon • when fetching from disk, try to amortize costs • when consuming disk space, might want to enforce quotas • need for OS community input • what are the right measures? • what parts of the system will benefit most? 09/24/02 EW2002

  3. some concerns crosscut primary modularity they inherently apply to many modules & functions • page daemon wakeupcrosscuts VM and buffer cache • prefetchingcrosscuts VM andfile systems • disk quotacrosscuts file systems • each of these concerns • is designed with a clear, singular purpose • currently has a non-modular (scattered and tangled) implementation /usr/src/sys/ … vm kern ufs gnu vm_page.c vfs_bio.c … vm_fault.c vnode_pager.c … ufs ffs ext2fs ufs_vfs.c ufs_vnops.c ufs_inode.c ffs_vfs.c ffs_inode.c ffs_alloc.c … ext2_vfs.c ext2_vnops.c ext2_inode.c ext2_alloc.c … ufs_readwrite.c … the word crosscutting is like the word hierarchical, it applies to deep structure. remember non-modular implementations of (any kind of) concern tend to show scattering and tangling; modular implementations of any kind of concern show their true structure explicitly and clearly 09/24/02 EW2002

  4. vm_map_t map, cflow(execution(int vm_fault(map, ..))) && and map AspectC • C with a few new linguistic mechanisms • to modularize crosscutting concerns • aspect code interacts with other code at • function calls/executions • field accesses vm_fault vm_pager_getpages • before( vm_object_t object): • execution(int vnode_pager_getpages(object, ..)) • { • /* regular C code, access to • object */ • } vnode_pager_getpages 09/24/02 EW2002

  5. vm_map_t map, cflow(execution(int vm_fault(map, ..))) && and map AspectC • C with a few new linguistic mechanisms • to modularize crosscutting concerns • aspect code interacts with other code at • function calls/executions • field accesses vm_fault vm_pager_getpages • before( vm_object_t object): • execution(int vnode_pager_getpages(object, ..)) • { • /* regular C code, access to • object */ • } • aspect prefetch_normal { vnode_pager_getpages } 09/24/02 EW2002

  6. original wakeup • triggered by specific memory operations • spread across 6 places: 3 files, 4 functions • uses context specific thresholds • different operations use slightly different combinations of local/global state /usr/src/sys/ vm kern vm_fault.c vfs_bio.c vm_page.c alloc_buf() … (1 out of 50 functions) vm_page_alloc() vm_page_unqueue() vm_page_unqueue_nowakeup() … (2 out of 47 functions) vm_fault_additional_pages() … (1 out of 9 functions) 09/24/02 EW2002

  7. original wakeup /usr/src/sys/ zoomed in: 3 files, 4 functions vm kern the ‘hidden structure’… key operations are:vm_page_unqueue, vm_fault_additional_pages, vm_page_alloc, alloc_buf in addition to performing their expected operations, these functions call pagedaemon_wakeup whenappropriate vm_page.c void vm_page_unqueue(…) { // 8 LOC … if ((queue - m->pc) == PQ_CACHE) if (vm_paging_needed()) pagedaemon_wakeup(); } vm_page_t vm_page_alloc(…) { // 35 LOC … pagedaemon_wakeup(); return (NULL); … pagedaemon_wakeup(); return (NULL); … if (vm_paging_needed()) pagedaemon_wakeup(); … return (m); } … vm_fault.c int vm_fault_additional_pages(…) { // 44 LOC … if ((rahead + rbehind) > ((cnt.v_free_count + cnt.v_cache_count) - cnt.v_free_reserved)) pagedaemon_wakeup(); … } … vfs_bio.c int alloc_buf(…) { // 88 LOC … if ((curproc != pageproc) && ((m->queue - m->pc) == PQ_CACHE) && ((cnt.v_free_count + cnt.v_cache_count) < (cnt.v_free_min + cnt.v_cache_min))) pagedaemon_wakeup(); … } 09/24/02 EW2002

  8. localized aspect page_daemon_wakeup { around(vm_page_t m): execution(void vm_page_unqueue(m)) { int queue = m->queue; proceed(m); if ((queue - m->pc) == PQ_CACHE) && vm_paging_needed()) pagedaemon_wakeup(); } … /* more advice */ } 4 calls to vm_page_unqueue() … but also 4 calls to vm_page_unqueue_nowakeup() 09/24/02 EW2002

  9. named pointcuts and advice pointcut unqueuing_available_pages(vm_page_t m): execution(void vm_page_unqueue(m)) around(vm_page_t m): unqueuing_available_pages(m) { /* same advice body */ int queue = m->queue; proceed(m); if ((queue - m->pc) == PQ_CACHE) && vm_paging_needed()) pagedaemon_wakeup(); } eliminated vm_page_unqueue_nowakeup() && cflow(execution(void vm_page_activate(..)) || execution(void vm_page_wire(..)) || execution(void vm_page_unmanage(..)) || execution(void vm_page_deactivate(..))); vm_page_activate vm_page_wire vm_page_unmanage vm_page_deactivate vm_page_unqueue 09/24/02 EW2002

  10. page daemon wakeup aspect • advice declarations • make a relationship between advice code and when it runs aspect page_daemon_wakeup { pointcut unqueuing_available_pages(vm_page_t m): execution(void vm_page_unqueue(m)) && cflow(execution(void vm_page_activate(vm_page_t)) || execution(void vm_page_wire(vm_page_t)) || execution(void vm_page_unmanage(vm_page_t)) || execution(void _vm_page_deactivate(vm_page_t, int))); pointcut allocating_pages(vm_object_t object, vm_pindex_t pindex, int page_req): execution(vm_page_t vm_page_alloc(object, pindex, page_req)); pointcut faulting_pages(int rbehind, int rahead): execution(boolean_t vm_pager_has_page(vm_object_t, vm_pindex_t, int*, int*)) && cflow(execution(int vm_fault_additional_pages(vm_page_t, rbehind, rahead, vm_page_t*, int*))); pointcut allocating_buffers(vm_page_t m, int also_m_busy, const char* msg): execution(int vm_page_sleep_busy(m, also_m_busy, msg)) && cflow(execution(int allocbuf(struct buf*, int))); • around(vm_page_t m): • unqueuing_available_pages(m) • { • int queue = m->queue; • proceed(m); • if (((queue - m->pc) == PQ_CACHE) && (pages_available() < vm_page_threshold())) • pagedaemon_wakeup(); • } • around(vm_object_t object, vm_pindex_t pindex, int page_req): • allocating_pages(object, pindex, page_req) • { • vm_page_t allocd_page = proceed(object, pindex, page_req); • if (allocd_page == NULL) • pagedaemon_wakeup(); • else • if (pages_available() < vm_page_threshold()) • pagedaemon_wakeup(); • return allocd_page; • } • after(int rbehind, int rahead): • faulting_pages(rbehind, rahead) • { • if ((rahead + rbehind) > (pages_available() - cnt.v_free_reserved)) • pagedaemon_wakeup(); • } • around(vm_page_t m, int also_m_busy, const char* msg): • allocating_buffers(m, also_m_busy, msg) • { • int had_to_sleep = proceed(m, also_m_busy, msg); • if (!had_to_sleep && ((m->queue - m->pc) == PQ_CACHE) • && (pages_available() < vfs_page_threshold())) • pagedaemon_wakeup(); • return had_to_sleep; • } • } • named pointcuts • identify points in the execution of the kernel 09/24/02 EW2002

  11. original prefetching access modes have evolved FreeBSD v3.3 -> v4.4 • normal mode - vm_fault()/ffs_getpages() • coordinated allocation/de-allocation • sequential mode • v3.3: divert path -> ffs_read() • v4.4: normal mode++ /usr/src/sys/ vm ufs ufs vm_fault.c vnode_pager.c ufs_readwrite.c vm_fault() vm_fault_additional_pages() … (2 out of 9 functions) vnode_pager_getpages … (1 out of 14 functions) ffs_getpages() ffs_read() … (2 out of 4 functions) 09/24/02 EW2002

  12. zoomed in: 2 files, 2 functions original prefetching /usr/src/sys/ vm ufs ufs the ‘hidden structure’ of sequential mode in v3.3 key operations are: vm_fault, vnode_pager_getpages, ffs_getpages, ffs_read control flow is diverted from page fault path to file system read path in order to prefetch more aggressively vm_fault.c int vm_fault(..) { // 166 LOC ,,, if (fs.object->type != OBJT_DEFAULT && (((fault_flags & VM_FAULT_WIRE_MASK) == 0) || wired)) { int rv; int reqpage; int ahead, behind; if (fs.first_object->behavior == OBJ_RANDOM) { ahead = 0; behind = 0; } else { behind = (vaddr - fs.entry->start) >> PAGE_SHIFT; if (behind > VM_FAULT_READ_BEHIND) behind = VM_FAULT_READ_BEHIND; ahead = ((fs.entry->end - vaddr) >> PAGE_SHIFT) - 1; if (ahead > VM_FAULT_READ_AHEAD) ahead = VM_FAULT_READ_AHEAD; } if ((fs.first_object->type != OBJT_DEVICE) && (fs.first_object->behavior == OBJ_SEQUENTIAL)) { vm_pindex_t firstpindex, tmppindex; if (fs.first_pindex < 2*(VM_FAULT_READ_BEHIND + VM_FAULT_READ_AHEAD + 1)) firstpindex = 0; else firstpindex = fs.first_pindex - 2*(VM_FAULT_READ_BEHIND + VM_FAULT_READ_AHEAD + 1); for(tmppindex = fs.first_pindex - 1; tmppindex >= firstpindex; --tmppindex) { vm_page_t mt; mt = vm_page_lookup( fs.first_object, tmppindex); if (mt == NULL || (mt->valid != VM_PAGE_BITS_ALL)) break; … } ahead += behind; behind = 0; } … ufs_readwrite.c int ffs_getpages() { // 89 LOC ,,, if ((obj->behavior != OBJ_RANDOM) && ((firstindex != 0) && (firstindex <= vp->v_lastr) && ((firstindex + pcount) > vp->v_lastr)) || (obj->behavior == OBJ_SEQUENTIAL)) { struct uio auio; struct iovec aiov; int error; for (i = 0; i < pcount; i++) { m = ap->a_m[i]; vm_page_activate(m); vm_page_io_start(m); vm_page_wakeup(m); } auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = 0; aiov.iov_len = MAXBSIZE; auio.uio_resid = MAXBSIZE; auio.uio_offset = foff; auio.uio_segflg = UIO_NOCOPY; auio.uio_rw = UIO_READ; auio.uio_procp = curproc; error = VOP_READ(vp, &auio, IO_VMIO | ((MAXBSIZE / bsize) << 16), curproc->p_ucred); … 09/24/02 EW2002

  13. named pointcuts identify page fault path identify file system read path advice allocate on page fault path divert to read path page flip if on both paths sequential prefetching aspect v3.3 aspect sequential_prefetching { pointcut vm_fault_path( vm_map_t map ): cflow( execution( int vm_fault( map, .. ))); pointcut ffs_read_path( struct vnode* vp, struct uio* io_inf, int size, struct buff** bpp ):cflow( execution( int ffs_read( vp, io_inf, size, bpp ))); before( vm_map_t map, vm_object_t obj, vm_page_t* plist, int len, int fpage ): execution( int vnode_pager_getpages( obj, plist, len, fpage )) && vm_fault_path( map ) { if ( obj->declared_behaviour == SEQUENTIAL ) { vm_map_lock( map ); plan_and_alloc_sequential( obj, plist, len, fpage ); vm_map_unlock( map ); } } around( vm_object_t obj, vm_page_t* plist, int len, int fpage ): execution( int ffs_getpages( obj, plist, len, fpage )) { if ( obj->behaviour == SEQUENTIAL ) { struct vnode* vp = obj->handle; struct uio* io_inf = io_prep( plist[fpage]->pindex, MAXBSIZE, curproc ); int error = ffs_read( vp, io_inf, MAXBSIZE, curproc->p_ucred ); return cleanup_after_read( error, obj, plist, len, fpage ); } else proceed(obj, plist, len, fpage); } after( struct uio* io_info, int size, struct buf** bpp ): execution( int bread(..) ) && vm_fault_path(..) && ffs_read_path( struct vnode*, io_info, size, bpp ) { flip_buffer_pages_to_allocated_vm_pages( (char *)bpp->b_data, size, io_info ); } } 09/24/02 EW2002

  14. named pointcuts identify page fault path identify file system read path advice allocate on page fault path divert to read path page flip if on both paths sequential prefetching aspect v4.4 aspect sequential_prefetching { pointcut vm_fault_path( vm_map_t map ): cflow( execution( int vm_fault( map, .. ))); pointcut ffs_read_path( struct vnode* vp, struct uio* io_inf, int size, struct buff** bpp ):cflow( execution( int ffs_read( vp, io_inf, size, bpp ))); before( vm_map_t map, vm_object_t obj, vm_page_t* plist, int len, int fpage ): execution( int vnode_pager_getpages( obj, plist, len, fpage )) && vm_fault_path( map ) { if ( obj->declared_behaviour == SEQUENTIAL ) { vm_map_lock( map ); plan_and_alloc_sequential( obj, plist, len, fpage ); vm_map_unlock( map ); } } around( vm_object_t obj, vm_page_t* plist, int len, int fpage ): execution( int ffs_getpages( obj, plist, len, fpage )) { if ( obj->behaviour == SEQUENTIAL ) { struct vnode* vp = obj->handle; struct uio* io_inf = io_prep( plist[fpage]->pindex, MAXBSIZE, curproc ); int error = ffs_read( vp, io_inf, MAXBSIZE, curproc->p_ucred ); return cleanup_after_read( error, obj, plist, len, fpage ); } else proceed(obj, plist, len, fpage); } after( struct uio* io_info, int size, struct buf** bpp ): execution( int bread(..) ) && vm_fault_path(..) && ffs_read_path( struct vnode*, io_info, size, bpp ) { flip_buffer_pages_to_allocated_vm_pages( (char *)bpp->b_data, size, io_info ); } } unplugged 09/24/02 EW2002

  15. original quota /usr/src/sys/ zoomed in: 2 files, 2 functions (10 files, 22 functions total) ufs gnu ext2fs the ‘hidden structure’… not part of core file system functionality (unpluggable) all #if QUOTA in EXT2 (18 out of 46 total) introduce the same functionality as in UFS/FFS corresponding operations ffs ext2_vfsops.c ffs_vfsops.c int ext2_flushfiles(…){// 9 LOC … #if QUOTA int i; #endif … #if QUOTA if (mp->mnt_flag & MNT_QUOTA) { if ((error = vflush(mp, 0, SKIPSYSTEM|flags))!=0) return (error); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP) continue; quotaoff(p, mp, i); } } #endif … } int ffs_flushfiles(…){// 13 LOC … #ifdef QUOTA if (mp->mnt_flag & MNT_QUOTA) { int i; error = vflush(mp, 0, SKIPSYSTEM|flags); if (error) return (error); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP) continue; quotaoff(p, mp, i); } } #endif … } 09/24/02 EW2002

  16. disk quota aspect aspect disk_quota { pointcut flushfiles(register struct mount *mp, int flags, struct proc *p): execution(int ffs_flushfiles(mp, flags, p)) || execution(int ext2_flushfiles(mp, flags, p)); pointcut sync(struct mount *mp): execution(int ffs_sync(mp, int, struct ucred*, struct proc*)) || execution(int ext2_sync(mp, int, struct ucred*, struct proc*)); pointcut vget(struct inode *ip): execution(void ufs_ihashins(ip)) && cflow(execution(int ffs_vget(struct mount*, ino_t, struct vnode**))) || (execution(int ext2_vget(struct mount*, ino_t, struct vnode**))); around(register struct mount *mp, int flags, struct proc *p): flushfiles(mp, flags, p) { register struct ufsmount *ump; ump = VFSTOUFS(mp); if (mp->mnt_flag & MNT_QUOTA) { int i; int error = vflush(mp, NULLVP, SKIPSYSTEM|flags); if (error) return (error); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP) continue; quotaoff(p, mp, i); } } return proceed(mp, flags, p); } after(struct mount *mp): sync(mp) { qsync(mp); } before(struct inode *ip): vget(ip) { int i; for (i = 0; i < MAXQUOTAS; i++) ip->i_dquot[i] = NODQUOT; } } • pointcuts can be extended • include all file systemsby using wildcards • pointcut flushfiles(..): • execution(int !_flushfiles(..)); • no change to advice 09/24/02 EW2002

  17. programming environment • tools for AspectJ • Eclipse, NetBeans, JBuilder, Emacs • allow us to see • what parts of the code are affected by aspects • the crosscutting structure of aspects • support navigation • aspect  code it affects • debugging • none available for AspectC yet… 09/24/02 EW2002

  18. emacs 09/24/02 EW2002

  19. 09/24/02 EW2002

  20. 09/24/02 EW2002

  21. 09/24/02 EW2002

  22. benefits • better separation • aspect / primary system modularity • independent development • unpluggable • explicit interaction • key operations and values identified • redundant code reduced • i.e. quota in EXT2 • locality • global invariants are in one place • i.e. use of thresholds, access modes, resource restrictions • promotes consistent management • i.e. across memory operations, OS versions, file systems • one part of the code is less likely to evolve without the other 09/24/02 EW2002

  23. costs - runtime • static - execution(int vm_page_unqueue()) • inlineable function call to advice body • dynamic - cflow(execution(int f())) && .. • process-local stack taken from/returned to pool of entries • cflow operations access this stack • cflow tested function (f() above) <- push, pop • functions tested within cflow <- test, get • 700MHz Pentium-III processor • per-process: 777 ns (startup), 141 ns (teardown) • per-cflow op: < 100 ns each 09/24/02 EW2002

  24. costs - compile-time • AspectC • a preprocessor • written in Java • aspect code is woven into source files before compilation • run the C preprocessor on the source files (code bloat!) • source files -> abstract syntax tree • abstract syntax tree modified • new source file written (code is readable) • currently weave as needed • feed relevant files with aspect code for AspectC preprocessing • standard C compiler works with woven code • modified makefile for kernel 09/24/02 EW2002

  25. related work • Aspect-Oriented Programming • ECOOP’97 [Kiczales et al] • Hyper/J, subject-oriented programming [Ossher] • composition filters [Aksit] • AspectJ (www.aspectj.org) [Kiczales] • 1stInternational Conference on Aspect-Oriented Software Development (AOSD’02) • workshop on aspects, components, and patterns for infrastructure software • Bossa [Barreto] • weaving • Knit [Eide] • Aspect Weaving as Component Knitting: Separation of Concerns with Knit (ASOC workshop, ICSE’01) • Handi-wrap [Baker] • Runtime Aspect Weaving through Metaprogramming (AOSD’02) 09/24/02 EW2002

  26. other system aspects? • scheduling • decisions spread throughout the system callers of number of mi_switch() calls to callers tsleep ~450 exit ~90 issignal ~90 • networking • new protocols • a cohesive set of related modifications to the standard protocol graph • x-kernel[Hutchinson], Plexus [Fiuczynski] • profiling • naturally involves a collection of points in an executing system • ~300 #ifdef DIAGNOSTIC system-wide FreeBSD 4.4 09/24/02 EW2002

  27. conclusions and future work • improved modularity • provides context • reveals structure of crosscutting • improved changeability • independent development • evolution • simple tool support has shown useful • need to consider sophisticated aspect compositions • scalability • performance • what OS concerns would benefit most from separation? 09/24/02 EW2002

More Related